<commit_before>$(document).ready(function(){
    var map;

    var imagesPool = (function(){
        var icons = {};
        function iconExists(type) {
            return typeof icons[type] !== 'undefined';
        }

        function iconGet(type) {
            if (typeof icons[type] !== 'undefined') {
                return icons[type];
            }

            var icon = new google.maps.MarkerImage(
                'static/images/vehicle-types/' + type + '.png',
                 new google.maps.Size(20, 20),
                 new google.maps.Point(0, 0),
                 new google.maps.Point(10, 10)
            );
            icons[type] = icon;

            return icon;
        }

        return {
            iconGet: iconGet
        }
    })();

    var linesPool = (function() {
        var routes = {};

        function positionOnRouteAtPercentGet(ids, perc) {
            var route = routes[ids[0] + '_' + ids[1]];

            var dC = 0;
            var dAC = route['length']*perc;

            for (var i=1; i<route['points'].length; i++) {
                var pA = route['points'][i-1];
                var pB = route['points'][i];
                var d12 = google.maps.geometry.spherical.computeDistanceBetween(pA, pB);
                if ((dC + d12) > dAC) {
                    return google.maps.geometry.spherical.interpolate(pA, pB, (dAC - dC)/d12);
                }
                dC += d12;
            }

            return null;
        }

        function routeExists(a, b) {
          if (typeof routes[a + '_' + b] !== 'undefined') {
              return true;
          }
          if (typeof routes[b + '_' + a] !== 'undefined') {
              return true;
          }

          return false;
        }

        function routeAdd(a, b, edges) {
            var routePoints = [];
            $.each(edges, function(k, edgeID) {
                var edge = simcity_topology_edges[Math.abs(edgeID)];

                var points = google.maps.geometry.encoding.decodePath(edge);
                if (edgeID < 0) {
                    points.reverse();
                }
                // TODO - use some MVCArray magic to remove the last element of edges when concatenating ?
                routePoints = routePoints.concat(points);
            });

            routeLength = google.maps.geometry.spherical.computeLength(routePoints).toFixed(3);

            routes[a + '_' + b] = {
                'points': routePoints,
                'length': routeLength
            };
            routes[b + '_' + a] = {
                'points': routePoints.slice().reverse(),
                'length': routeLength
            };
        }

        return {
            positionGet: positionOnRouteAtPercentGet,
            routeExists: routeExists,
            routeAdd: routeAdd
        }
    })();
    var time_helpers = (function(){
        function hms2s(hms) {
            var parts = hms.split(':');
            return parseInt(parts[0], 10)*3600 + parseInt(parts[1], 10)*60 + parseInt(parts[2], 10);
        }
        function s2hms(dayS) {
            function pad2Dec(what) {
                return (what < 10 ? '0' + what : what);
            }

            // From http://stackoverflow.com/questions/1322732/convert-seconds-to-hh-mm-ss-with-javascript
            var hours = Math.floor(dayS / 3600);
            dayS %= 3600;
            var minutes = Math.floor(dayS / 60);
            var seconds = dayS % 60;

            return pad2Dec(hours) + ':' + pad2Dec(minutes) + ':' + pad2Dec(seconds);
        }
        return {
            hms2s: hms2s,
            s2hms: s2hms
        }
    })();
    var timer = (function(){
        var delay = 0;

        function getNow() {
            var now = new Date();

            var hours = now.getHours();
            var minutes = now.getMinutes();
            var seconds = now.getSeconds();

            return hours*3600 + minutes*60 + seconds;
        }

        function getDaySeconds() {
            return getNow() - delay;
        }

        function init(hms) {
            if (typeof(hms) !== 'undefined') {
                delay = getNow() - time_helpers.hms2s(hms);
            }

            var timeContainer = $('#day_time');
            function paintHM() {
                timeContainer.text(time_helpers.s2hms(getDaySeconds()));
            }

            setInterval(function(){
                paintHM();
            }, 1000);
        }

        function getHM() {
          var hms = time_helpers.s2hms(getDaySeconds());
          return hms.substring(0, 5);
        }

        return {
            init: init,
            getTime: getDaySeconds,
            getHM: getHM
        }
    })();

    var map_helpers = (function(){
        function init() {
            var mapStyles = [
              {
                featureType: "poi.business",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "labels",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "labels",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "geometry",
                stylers: [
                  { visibility: "simplified" },
                  { lightness: 70 }
                ]
              },{
                featureType: "transit.line",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "transit.station.bus",
                stylers: [
                  { visibility: "off" }
                ]
              }
            ];

            var start = new google.maps.LatLng(47.378057, 8.5402338);
            var myOptions = {
              zoom: 15,
              center: start,
              mapTypeId: google.maps.MapTypeId.ROADMAP,
              styles: mapStyles
            }
            map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

            var layer = null;
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '1497331'
              },
              map: map
            });
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '1497361'
              },
              map: map
            });
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '812706'
              },
              map: map
            });
        }

        return {
            init: init
        }
    })();

    var vehicle_helpers = (function(){
        var vehicles = [];

        function Vehicle(params) {
            this.id             = params['id'];
            this.stations       = params['sts'];
            this.depS           = params['deps'];
            this.arrS           = params['arrs'];

            $.each(params.edges, function(index, edge) {
                if (index === 0) { return; }

                if (linesPool.routeExists(params['sts'][index-1], params['sts'][index])) {
                    return;
                }

                linesPool.routeAdd(params['sts'][index-1], params['sts'][index], params['edges'][index].split(','));
            });

            this.marker = new google.maps.Marker({
                position: new google.maps.LatLng(0, 0),
                icon: imagesPool.iconGet(params['type']),
                map: map,
                title: params['name'] + ' (' + this.id + ')'
            });
        }
        Vehicle.prototype.render = function() {
            function animate() {
                var hms = timer.getTime();

                var info = {
                    state: null
                };
                for (var i=0; i<that.arrS.length; i++) {
                    if (hms < that.arrS[i]) {
                        if (hms > that.depS[i]) {
                            info = {
                                state: 'motion',
                                stations: [that.stations[i], that.stations[i+1]],
                                percent: (hms - that.depS[i])/(that.arrS[i] - that.depS[i]),
                                message: that.id + '> ' + time_helpers.s2hms(hms) + ' From ' + that.stations[i] + ' --TO-- ' + that.stations[i+1]
                            };
                        } else {
                            info = {
                                state: 'station',
                                station: that.stations[i],
                                timeLeft: that.depS[i] - hms,
                                message: that.id + '> ' + time_helpers.s2hms(hms) + ' In station ' + that.stations[i] + ' until ' + time_helpers.s2hms(that.depS[i])
                            };
                        }
                        break;
                    }
                }

                // TODO - move me above - is waste of code
                switch (info.state) {
                    case 'station':
                        // TODO - if is not yet on the map, add it (first vertex of the route)
                        setTimeout(animate, info.timeLeft*1000);
                        break;
                    case 'motion':
                        var pos = linesPool.positionGet(info.stations, info.percent);
                        if (pos === null) {
                            console.log('Couldnt get the position of ' + that.id + ' between stations: ' + info.stations);
                            break;
                        }

                        if (map.getBounds().contains(pos)) {
                            if (that.marker.getMap() === null) {
                                that.marker.setMap(map);
                            }
                            that.marker.setPosition(pos);
                        } else {
                            that.marker.setMap(null);
                        }

                        setTimeout(animate, 500);
                        break;
                    default:
                        that.marker.setMap(null);
                        break;
                }
            }

            var that = this;
            animate();
        };

        return {
            add: function(data) {
                if (vehicles.indexOf(data['id']) !== -1) {
                    console.log("Vehicle %s is already added", data['id']);
                    return;
                }

                var v = new Vehicle(data);
                v.render();
                vehicles.push(data['id']);
            }
        }
    })();

    // END HELPERS

    var nowHMS = '10:16:55';
    timer.init(nowHMS);
    map_helpers.init();

    $.ajax({
      url: 'feed/vehicles/' + timer.getHM(),
      dataType: 'json',
      success: function(vehicles) {
        $.each(vehicles, function(index, vehicleData) {
            vehicle_helpers.add(vehicleData);
        });
      }
    });
});<commit_msg>Disabled clicks on FT layers<commit_after>$(document).ready(function(){
    var map;

    var imagesPool = (function(){
        var icons = {};
        function iconExists(type) {
            return typeof icons[type] !== 'undefined';
        }

        function iconGet(type) {
            if (typeof icons[type] !== 'undefined') {
                return icons[type];
            }

            var icon = new google.maps.MarkerImage(
                'static/images/vehicle-types/' + type + '.png',
                 new google.maps.Size(20, 20),
                 new google.maps.Point(0, 0),
                 new google.maps.Point(10, 10)
            );
            icons[type] = icon;

            return icon;
        }

        return {
            iconGet: iconGet
        }
    })();

    var linesPool = (function() {
        var routes = {};

        function positionOnRouteAtPercentGet(ids, perc) {
            var route = routes[ids[0] + '_' + ids[1]];

            var dC = 0;
            var dAC = route['length']*perc;

            for (var i=1; i<route['points'].length; i++) {
                var pA = route['points'][i-1];
                var pB = route['points'][i];
                var d12 = google.maps.geometry.spherical.computeDistanceBetween(pA, pB);
                if ((dC + d12) > dAC) {
                    return google.maps.geometry.spherical.interpolate(pA, pB, (dAC - dC)/d12);
                }
                dC += d12;
            }

            return null;
        }

        function routeExists(a, b) {
          if (typeof routes[a + '_' + b] !== 'undefined') {
              return true;
          }
          if (typeof routes[b + '_' + a] !== 'undefined') {
              return true;
          }

          return false;
        }

        function routeAdd(a, b, edges) {
            var routePoints = [];
            $.each(edges, function(k, edgeID) {
                var edge = simcity_topology_edges[Math.abs(edgeID)];

                var points = google.maps.geometry.encoding.decodePath(edge);
                if (edgeID < 0) {
                    points.reverse();
                }
                // TODO - use some MVCArray magic to remove the last element of edges when concatenating ?
                routePoints = routePoints.concat(points);
            });

            routeLength = google.maps.geometry.spherical.computeLength(routePoints).toFixed(3);

            routes[a + '_' + b] = {
                'points': routePoints,
                'length': routeLength
            };
            routes[b + '_' + a] = {
                'points': routePoints.slice().reverse(),
                'length': routeLength
            };
        }

        return {
            positionGet: positionOnRouteAtPercentGet,
            routeExists: routeExists,
            routeAdd: routeAdd
        }
    })();
    var time_helpers = (function(){
        function hms2s(hms) {
            var parts = hms.split(':');
            return parseInt(parts[0], 10)*3600 + parseInt(parts[1], 10)*60 + parseInt(parts[2], 10);
        }
        function s2hms(dayS) {
            function pad2Dec(what) {
                return (what < 10 ? '0' + what : what);
            }

            // From http://stackoverflow.com/questions/1322732/convert-seconds-to-hh-mm-ss-with-javascript
            var hours = Math.floor(dayS / 3600);
            dayS %= 3600;
            var minutes = Math.floor(dayS / 60);
            var seconds = dayS % 60;

            return pad2Dec(hours) + ':' + pad2Dec(minutes) + ':' + pad2Dec(seconds);
        }
        return {
            hms2s: hms2s,
            s2hms: s2hms
        }
    })();
    var timer = (function(){
        var delay = 0;

        function getNow() {
            var now = new Date();

            var hours = now.getHours();
            var minutes = now.getMinutes();
            var seconds = now.getSeconds();

            return hours*3600 + minutes*60 + seconds;
        }

        function getDaySeconds() {
            return getNow() - delay;
        }

        function init(hms) {
            if (typeof(hms) !== 'undefined') {
                delay = getNow() - time_helpers.hms2s(hms);
            }

            var timeContainer = $('#day_time');
            function paintHM() {
                timeContainer.text(time_helpers.s2hms(getDaySeconds()));
            }

            setInterval(function(){
                paintHM();
            }, 1000);
        }

        function getHM() {
          var hms = time_helpers.s2hms(getDaySeconds());
          return hms.substring(0, 5);
        }

        return {
            init: init,
            getTime: getDaySeconds,
            getHM: getHM
        }
    })();

    var map_helpers = (function(){
        function init() {
            var mapStyles = [
              {
                featureType: "poi.business",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "labels",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "labels",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "road",
                elementType: "geometry",
                stylers: [
                  { visibility: "simplified" },
                  { lightness: 70 }
                ]
              },{
                featureType: "transit.line",
                stylers: [
                  { visibility: "off" }
                ]
              },{
                featureType: "transit.station.bus",
                stylers: [
                  { visibility: "off" }
                ]
              }
            ];

            var start = new google.maps.LatLng(47.378057, 8.5402338);
            var myOptions = {
              zoom: 15,
              center: start,
              mapTypeId: google.maps.MapTypeId.ROADMAP,
              styles: mapStyles
            }
            map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

            var layer = null;
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '1497331'
              },
              clickable: false,
              map: map
            });
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '1497361'
              },
              clickable: false,
              map: map
            });
            layer = new google.maps.FusionTablesLayer({
              query: {
                select: 'geometry',
                from: '812706'
              },
              clickable: false,
              map: map
            });
        }

        return {
            init: init
        }
    })();

    var vehicle_helpers = (function(){
        var vehicles = [];

        function Vehicle(params) {
            this.id             = params['id'];
            this.stations       = params['sts'];
            this.depS           = params['deps'];
            this.arrS           = params['arrs'];

            $.each(params.edges, function(index, edge) {
                if (index === 0) { return; }

                if (linesPool.routeExists(params['sts'][index-1], params['sts'][index])) {
                    return;
                }

                linesPool.routeAdd(params['sts'][index-1], params['sts'][index], params['edges'][index].split(','));
            });

            this.marker = new google.maps.Marker({
                position: new google.maps.LatLng(0, 0),
                icon: imagesPool.iconGet(params['type']),
                map: map,
                title: params['name'] + ' (' + this.id + ')'
            });
        }
        Vehicle.prototype.render = function() {
            function animate() {
                var hms = timer.getTime();

                var info = {
                    state: null
                };
                for (var i=0; i<that.arrS.length; i++) {
                    if (hms < that.arrS[i]) {
                        if (hms > that.depS[i]) {
                            info = {
                                state: 'motion',
                                stations: [that.stations[i], that.stations[i+1]],
                                percent: (hms - that.depS[i])/(that.arrS[i] - that.depS[i]),
                                message: that.id + '> ' + time_helpers.s2hms(hms) + ' From ' + that.stations[i] + ' --TO-- ' + that.stations[i+1]
                            };
                        } else {
                            info = {
                                state: 'station',
                                station: that.stations[i],
                                timeLeft: that.depS[i] - hms,
                                message: that.id + '> ' + time_helpers.s2hms(hms) + ' In station ' + that.stations[i] + ' until ' + time_helpers.s2hms(that.depS[i])
                            };
                        }
                        break;
                    }
                }

                // TODO - move me above - is waste of code
                switch (info.state) {
                    case 'station':
                        // TODO - if is not yet on the map, add it (first vertex of the route)
                        setTimeout(animate, info.timeLeft*1000);
                        break;
                    case 'motion':
                        var pos = linesPool.positionGet(info.stations, info.percent);
                        if (pos === null) {
                            console.log('Couldnt get the position of ' + that.id + ' between stations: ' + info.stations);
                            break;
                        }

                        if (map.getBounds().contains(pos)) {
                            if (that.marker.getMap() === null) {
                                that.marker.setMap(map);
                            }
                            that.marker.setPosition(pos);
                        } else {
                            that.marker.setMap(null);
                        }

                        setTimeout(animate, 500);
                        break;
                    default:
                        that.marker.setMap(null);
                        break;
                }
            }

            var that = this;
            animate();
        };

        return {
            add: function(data) {
                if (vehicles.indexOf(data['id']) !== -1) {
                    console.log("Vehicle %s is already added", data['id']);
                    return;
                }

                var v = new Vehicle(data);
                v.render();
                vehicles.push(data['id']);
            }
        }
    })();

    // END HELPERS

    var nowHMS = '10:16:55';
    timer.init(nowHMS);
    map_helpers.init();

    $.ajax({
      url: 'feed/vehicles/' + timer.getHM(),
      dataType: 'json',
      success: function(vehicles) {
        $.each(vehicles, function(index, vehicleData) {
            vehicle_helpers.add(vehicleData);
        });
      }
    });
});<|endoftext|>


<commit_before>var express = require('express');
// var sequelize = require('sequelize');
var config = require('./config/config');
// var database = require('./db/database');

var app = express();
var path = require('path');

var normalizePort = function(val) {
  var port = parseInt(val, 10);
  if (isNaN(port)) {
    return val;
  }
  if (port >= 0) {
    return port;
  }
  return false;
};

// Get port from environment and store in Express.
config.port = normalizePort(process.env.PORT || config.port);

console.log('Path: ', path.join(__dirname + '/../../client'));

// configure our server with all the middleware and routing
require('./config/middleware.js')(app, express);
require('./config/routes.js')(app, express);

// start listening to requests on port 8000
app.listen(config.port, function() { console.log('listening on port: ', config.port); });

// export our app for testing and flexibility, required by index.js
module.exports = app;<commit_msg>(fix) Uncomment db, cleanup<commit_after>var express = require('express');
var config = require('./config/config');
var database = require('./db/database');

var app = express();
var path = require('path');

var normalizePort = function(val) {
  var port = parseInt(val, 10);
  if (isNaN(port)) {
    return val;
  }
  if (port >= 0) {
    return port;
  }
  return false;
};

// Get port from environment and store in Express.
config.port = normalizePort(process.env.PORT || config.port);

// configure our server with all the middleware and routing
require('./config/middleware.js')(app, express);
require('./config/routes.js')(app, express);

// start listening to requests on port 8000
app.listen(config.port, function() { console.log('listening on port: ', config.port); });

// export our app for testing and flexibility, required by index.js
module.exports = app;<|endoftext|>


<commit_before>var map;

// Helper variabls to display the indeterminate loader on top of the screen
var oldTileCoordinate;
var TILE_SIZE = 256;
var mapLoaded = false;
var dataLoaded = false;

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function initMap() {
    return new google.maps.Map(document.getElementById('map'), {
        zoom: 12,
        center: {lat: 28.6139, lng: 77.2090},
        mapTypeControl: false,
        streetViewControl: false
    });

}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function userGeolocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function (position) {
            var pos = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            };
            // Using global variable here
            map.setCenter(pos);
            map.setZoom(12);
        }, function () {
            handleLocationError(true);
        });
    } else {
        // Browser doesn't support Geolocation
        handleLocationError(false);
    }
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function handleLocationError(browserHasGeolocation) {
    // Materialize.toast(message, displayLength, className, completeCallback);
    Materialize.toast(browserHasGeolocation ?
        'Is the Location Service enabled?' :
        'Your browser doesn\'t support geolocation.', 2000); // last number is the duration of the toast
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function project(latLng) {
    var siny = Math.sin(latLng.lat() * Math.PI / 180);
    siny = Math.min(Math.max(siny, -0.9999), 0.9999);

    return new google.maps.Point(
        TILE_SIZE * (0.5 + latLng.lng() / 360),
        TILE_SIZE * (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI)));
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function getNewTileCoordinate() {
    var currentBounds = map.getBounds();
    var currentCenterLatLng = currentBounds.getCenter();
    var scale = 1 << map.getZoom();
    var worldCoordinate = project(currentCenterLatLng);
    return new google.maps.Point(
        Math.floor(worldCoordinate.x * scale / TILE_SIZE),
        Math.floor(worldCoordinate.y * scale / TILE_SIZE));
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function initAutocomplete() {

    map = initMap();

    google.maps.event.addListener(map, 'bounds_changed', function () {
        var newTileCoordinate = getNewTileCoordinate();
        if (typeof oldTileCoordinate != 'undefined') {
            if ((Math.abs(newTileCoordinate.x - oldTileCoordinate.x) >= 2) || (Math.abs(newTileCoordinate.y - oldTileCoordinate.y) >= 2)) {
                mapLoaded = false;
                $('.activityIndicator').fadeIn(200);
            }
        }
    });

    google.maps.event.addListener(map, 'tilesloaded', function () {
        oldTileCoordinate = getNewTileCoordinate();
        mapLoaded = true;
        if (mapLoaded && dataLoaded) {
            $('.activityIndicator').fadeOut(200);
        }
    });

    // Load pins on map idle
    google.maps.event.addListener(map, 'idle', function () {
        queryData();
    });

    // HTML5 geolocation
    userGeolocation();

    // Create the search box and link it to the UI element.
    var input = document.getElementById('search-input');
    // var searchDiv = document.getElementById('search-div');
    var searchBox = new google.maps.places.SearchBox(input);
    // map.controls[google.maps.ControlPosition.TOP_LEFT].push(searchDiv);

    // Bias the SearchBox results towards current map's viewport.
    map.addListener('bounds_changed', function () {
        searchBox.setBounds(map.getBounds());
    });

    // var markers = [];
    // Listen for the event fired when the user selects a prediction and retrieve
    // more details for that place.
    searchBox.addListener('places_changed', function () {
        var places = searchBox.getPlaces();

        if (places.length == 0) {
            return;
        }
        //
        // // Clear out the old markers.
        // markers.forEach(function (marker) {
        //     marker.setMap(null);
        // });
        // markers = [];

        // For each place, get the icon, name and location.
        var bounds = new google.maps.LatLngBounds();
        places.forEach(function (place) {
            //     var icon = {
            //         url: place.icon,
            //         size: new google.maps.Size(71, 71),
            //         origin: new google.maps.Point(0, 0),
            //         anchor: new google.maps.Point(17, 34),
            //         scaledSize: new google.maps.Size(25, 25)
            //     };
            //
            //     // Create a marker for each place.
            //     markers.push(new google.maps.Marker({
            //         map: map,
            //         icon: icon,
            //         title: place.name,
            //         position: place.geometry.location
            //     }));

            if (place.geometry.viewport) {
                // Only geocodes have viewport.
                bounds.union(place.geometry.viewport);
            } else {
                bounds.extend(place.geometry.location);
            }
        });
        map.fitBounds(bounds);
    });
}

// Global array to store all the markers currently marked on `map`.
var markers = [];

function setNewMarkers(input) {
    /*
     Marks the pins in `input` on the map. Removes previously marked pins.
     param: input: JSON object of `pins` to be marked on map.
     */
    markers.forEach(function (marker) {
        marker.setMap(null);
    });

    markers = [];

    input.pins.forEach(function (pin) {
        var location = {lat: pin.location.lat, lng: pin.location.lng};
        var title;
        if (pin.crime_list.length != 0)
            title = pin.crime_list[0].type;
        else if (pin.wiki_info_list.length != 0)
            title = pin.wiki_info_list[0].title;
        else
            title = pin.legislator_list[0].title;
        markers.push(new google.maps.Marker({
            map: map,
            title: title,
            position: location
        }));
    });
}<commit_msg>Add marker data based on type of data it represents<commit_after>var map;

// Helper variabls to display the indeterminate loader on top of the screen
var oldTileCoordinate;
var TILE_SIZE = 256;
var mapLoaded = false;
var dataLoaded = false;

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function initMap() {
    return new google.maps.Map(document.getElementById('map'), {
        zoom: 12,
        center: {lat: 28.6139, lng: 77.2090},
        mapTypeControl: false,
        streetViewControl: false
    });

}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function userGeolocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function (position) {
            var pos = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            };
            // Using global variable here
            map.setCenter(pos);
            map.setZoom(12);
        }, function () {
            handleLocationError(true);
        });
    } else {
        // Browser doesn't support Geolocation
        handleLocationError(false);
    }
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function handleLocationError(browserHasGeolocation) {
    // Materialize.toast(message, displayLength, className, completeCallback);
    Materialize.toast(browserHasGeolocation ?
        'Is the Location Service enabled?' :
        'Your browser doesn\'t support geolocation.', 2000); // last number is the duration of the toast
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function project(latLng) {
    var siny = Math.sin(latLng.lat() * Math.PI / 180);
    siny = Math.min(Math.max(siny, -0.9999), 0.9999);

    return new google.maps.Point(
        TILE_SIZE * (0.5 + latLng.lng() / 360),
        TILE_SIZE * (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI)));
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function getNewTileCoordinate() {
    var currentBounds = map.getBounds();
    var currentCenterLatLng = currentBounds.getCenter();
    var scale = 1 << map.getZoom();
    var worldCoordinate = project(currentCenterLatLng);
    return new google.maps.Point(
        Math.floor(worldCoordinate.x * scale / TILE_SIZE),
        Math.floor(worldCoordinate.y * scale / TILE_SIZE));
}

// Some code from https://developers.google.com/maps/documentation/javascript/reference#release-version
function initAutocomplete() {

    map = initMap();

    google.maps.event.addListener(map, 'bounds_changed', function () {
        var newTileCoordinate = getNewTileCoordinate();
        if (typeof oldTileCoordinate != 'undefined') {
            if ((Math.abs(newTileCoordinate.x - oldTileCoordinate.x) >= 2) || (Math.abs(newTileCoordinate.y - oldTileCoordinate.y) >= 2)) {
                mapLoaded = false;
                $('.activityIndicator').fadeIn(200);
            }
        }
    });

    google.maps.event.addListener(map, 'tilesloaded', function () {
        oldTileCoordinate = getNewTileCoordinate();
        mapLoaded = true;
        if (mapLoaded && dataLoaded) {
            $('.activityIndicator').fadeOut(200);
        }
    });

    // Load pins on map idle
    google.maps.event.addListener(map, 'idle', function () {
        queryData();
    });

    // HTML5 geolocation
    userGeolocation();

    // Create the search box and link it to the UI element.
    var input = document.getElementById('search-input');
    // var searchDiv = document.getElementById('search-div');
    var searchBox = new google.maps.places.SearchBox(input);
    // map.controls[google.maps.ControlPosition.TOP_LEFT].push(searchDiv);

    // Bias the SearchBox results towards current map's viewport.
    map.addListener('bounds_changed', function () {
        searchBox.setBounds(map.getBounds());
    });

    // var markers = [];
    // Listen for the event fired when the user selects a prediction and retrieve
    // more details for that place.
    searchBox.addListener('places_changed', function () {
        var places = searchBox.getPlaces();

        if (places.length == 0) {
            return;
        }
        //
        // // Clear out the old markers.
        // markers.forEach(function (marker) {
        //     marker.setMap(null);
        // });
        // markers = [];

        // For each place, get the icon, name and location.
        var bounds = new google.maps.LatLngBounds();
        places.forEach(function (place) {
            //     var icon = {
            //         url: place.icon,
            //         size: new google.maps.Size(71, 71),
            //         origin: new google.maps.Point(0, 0),
            //         anchor: new google.maps.Point(17, 34),
            //         scaledSize: new google.maps.Size(25, 25)
            //     };
            //
            //     // Create a marker for each place.
            //     markers.push(new google.maps.Marker({
            //         map: map,
            //         icon: icon,
            //         title: place.name,
            //         position: place.geometry.location
            //     }));

            if (place.geometry.viewport) {
                // Only geocodes have viewport.
                bounds.union(place.geometry.viewport);
            } else {
                bounds.extend(place.geometry.location);
            }
        });
        map.fitBounds(bounds);
    });
}

// Types of pins
var INVALID_PIN = 0;
var MULTI_PIN = 1;
var CRIME_PIN = 2;
var LEGISLATOR_PIN = 3;
var WIKI_PIN = 4;

// Identify the type of a pin
function typeOfMarker(pin) {

    if (pin.crime_list.length > 0 && pin.legislator_list.length == 0 && pin.wiki_info_list.length == 0) {

        return CRIME_PIN;

    } else if (pin.crime_list.length == 0 && pin.legislator_list.length > 0 && pin.wiki_info_list.length == 0) {

        return LEGISLATOR_PIN;

    } else if (pin.crime_list.length == 0 && pin.legislator_list.length == 0 && pin.wiki_info_list.length > 0) {

        return WIKI_PIN;

    } else if (pin.crime_list.length == 0 && pin.legislator_list.length == 0 && pin.wiki_info_list.length == 0) {

        return INVALID_PIN

    } else {

        return MULTI_PIN;

    }

}

// Global array to store all the markers currently marked on `map`.
var markers = [];

function setNewMarkers(input) {
    /*
     Marks the pins in `input` on the map. Removes previously marked pins.
     param: input: JSON object of `pins` to be marked on map.
     */
    markers.forEach(function (marker) {
        marker.setMap(null);
    });

    markers = [];

    input.pins.forEach(function (pin) {
        var location = {lat: pin.location.lat, lng: pin.location.lng};
        var iconLink; // See https://sites.google.com/site/gmapsdevelopment/
        var title;

        switch (typeOfMarker(pin)) {

            case MULTI_PIN:
                iconLink = 'http://maps.google.com/mapfiles/kml/pal5/icon44.png';
                title = "Multi Pin: Click to see more details";
                break;
            case CRIME_PIN:
                iconLink = 'http://maps.google.com/mapfiles/kml/pal3/icon33.png';
                title = "Crime Data: " + pin.crime_list[0].type;
                break;
            case LEGISLATOR_PIN:
                iconLink = 'http://maps.google.com/mapfiles/kml/pal3/icon53.png';
                title = "Legislator Data: " + pin.legislator_list[0].title;
                break;
            case WIKI_PIN:
                iconLink = 'http://maps.google.com/mapfiles/kml/pal3/icon35.png';
                title = "Wikipedia Data: " + pin.wiki_info_list[0].title;
                break;
            case INVALID_PIN:
                break;

        }

        var contentString = 'Hello, World!';

        var infowindow = new google.maps.InfoWindow({
            content: contentString
        });
        var marker = new google.maps.Marker({
            map: map,
            title: title,
            icon: iconLink,
            position: location
            // animation: google.maps.Animation.DROP
        });
        marker.addListener('click', function () {
            infowindow.open(map, marker);
        });
        markers.push(marker);
    });
}<|endoftext|>


<commit_before>{

	'use strict';

	let

		objectTypes = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error', 'Symbol'],

		typesReference = {},

		toString = typesReference.toString;

	for (let index = 0, size = objectTypes.length; index < size; index++)

		typesReference['[object ' + objectTypes[index] + ']'] = objectTypes[index].toLowerCase();

	class WarbleCore {

		re = {
			// https://www.w3.org/TR/html5/forms.html#e-mail-state-(type=email)
			'email': /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
			'integer': /^\d+$/
		};

		isHooks = {
			emptyObject(object) {

				for (let index in object)

					return false;

				return true;

			},
			email: (value) => this.re.email.test(value),
			integer: (value) => this.re.integer.test(value)
		};

		testHooks = {
			required: (value, isRequired) => isRequired ? value !== undefined : true,
			pattern: (value, pattern) => (typeof pattern === 'object' && typeof pattern.test === 'function' ? pattern : new RegExp(pattern)).test(value),
			min: (value, min = 0) => (Number(value) || 0) >= (Number(min) || 0),
			max: (value, max = Infinity) => (Number(value) || 0) <= (Number(max) || 0),
			minlength: (value, min = 0) => typeof value === 'string' ? value.length >= (Number(min) || 0) : false,
			maxlength: (value, max = Infinity) => typeof value === 'string' ? value.length <= (Number(max) || 0) : false,
			type: (value, type) => core.type(value) === type,
			is(value, types) {

				var response = [];

				if (core.is(types, 'array')) {

					for (let index = 0, size = types.length; index < size; index++)

						if (!core.is(value, types[index]))

							response.push(types[index]);

				} else if (!core.is(value, types))

					response.push(types);

				return response.length > 0 ? response : true;

			}
		};

		type = (value) => value === null ? 'null' : (typeof value === 'object' || typeof value === 'function' ? typesReference[toString.call(value)] || 'object' : typeof value);

		is = (value, type) => this.isHooks.hasOwnProperty(type) ? !!this.isHooks[type](value) : this.type(value) === type;

	}

	let core = new WarbleCore;

	class WarbleFragment {

		constructor(value) {

			this.value = value;

			this.valid = true;

			this.invalid = false;

			this.error = {};

		}

		test(name, param) {

			var response = core.testHooks.hasOwnProperty(name) ? core.testHooks[name](this.value, param) : true;

			if (!response || core.is(response, 'array')) {

				this.valid = false;

				this.invalid = true;

				this.error[name] = true;

				if (core.is(response, 'array'))

					for (let index = 0, size = response.length; index < size; index++)

						this.error[name + ':' + response[index]] = true;

			} else

				delete this.error[name];

			return this;

		}

	}

	class WarbleModel {

		constructor(model) {

			if (typeof model === 'object')

				this.model = model;

			else

				throw new Error('WarbleModel expects an object.');

		}

		validate(data) {

			if (typeof data === 'object') {

				let

					response = {
						'data': {},
						'valid': true,
						'invalid': false
					};

				for (let index in this.model) {

					let value = new WarbleFragment(data[index]);

					if (this.model.hasOwnProperty(index) && typeof this.model[index] === 'object')

						for (let test in this.model[index]) {

							let current = value.test(test, this.model[index][test]);

							if (current.invalid) {

								response.valid = false;

								response.invalid = true;

							}

						}

					response.data[index] = value;

				}

				return response;

			} else

				throw new Error('WarbleModel.validate expects an object.');

		}

	}

	class Warble extends WarbleCore {

		model = (model) => new WarbleModel(model);

		validate(value, options) {

			if (typeof options === 'object') {

				var value = new WarbleFragment(value);

				for (let index in options)

					value.test(index, options[index]);

				return value;

			} else

				throw new Error('Warble.validate expects an object.');

		}

	}

	var warble = new Warble;

};

//

warble.isHooks.foo = function(value) {

	return value > 0;

};

let

	schema = warble.model({
		'name': {
			'required': true,
			'minlength': 3
		},
		'surname': {
			'required': true,
			'minlength': 3
		},
		'age': {
			'required': true,
			'min': 18
		},
		'email': {
			'is': 'email'
		}
	}),

	data = {
		'name': 'Diego',
		'surname': 'Lopes Lima',
		'age': 23,
		'email': 'web.diego.lima@'
	};

console.log(schema.validate(data));

console.log(warble.validate(data.name, schema.model.name));

console.log(warble.type([]));
<commit_msg>Update warble.js<commit_after>{

	'use strict';

	let

		objectTypes = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error', 'Symbol'],

		typesReference = {},

		toString = typesReference.toString;

	for (let index = 0, size = objectTypes.length; index < size; index++)

		typesReference['[object ' + objectTypes[index] + ']'] = objectTypes[index].toLowerCase();

	class WarbleCore {

		re = {
			// https://www.w3.org/TR/html5/forms.html#e-mail-state-(type=email)
			'email': /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
			'integer': /^\d+$/
		};

		isHooks = {
			emptyObject(object) {

				for (let index in object)

					return false;

				return true;

			},
			email: (value) => this.re.email.test(value),
			integer: (value) => this.re.integer.test(value)
		};

		validateHooks = {
			required: (value, isRequired) => isRequired ? value !== undefined : true,
			pattern: (value, pattern) => (typeof pattern === 'object' && typeof pattern.test === 'function' ? pattern : new RegExp(pattern)).test(value),
			min: (value, min = 0) => (Number(value) || 0) >= (Number(min) || 0),
			max: (value, max = Infinity) => (Number(value) || 0) <= (Number(max) || 0),
			minlength: (value, min = 0) => typeof value === 'string' ? value.length >= (Number(min) || 0) : false,
			maxlength: (value, max = Infinity) => typeof value === 'string' ? value.length <= (Number(max) || 0) : false,
			type: (value, type) => core.type(value) === type,
			is(value, types) {

				var response = [];

				if (core.is(types, 'array')) {

					for (let index = 0, size = types.length; index < size; index++)

						if (!core.is(value, types[index]))

							response.push(types[index]);

				} else if (!core.is(value, types))

					response.push(types);

				return response.length > 0 ? response : true;

			}
		};

		type = (value) => value === null ? 'null' : (typeof value === 'object' || typeof value === 'function' ? typesReference[toString.call(value)] || 'object' : typeof value);

		is = (value, type) => this.isHooks.hasOwnProperty(type) ? !!this.isHooks[type](value) : this.type(value) === type;

	}

	let core = new WarbleCore;

	class WarbleFragment {

		constructor(value) {

			this.value = value;

			this.valid = true;

			this.invalid = false;

			this.error = {};

		}

		validate(name, param) {

			var response = core.validateHooks.hasOwnProperty(name) ? core.validateHooks[name](this.value, param) : true;

			if (!response || core.is(response, 'array')) {

				this.valid = false;

				this.invalid = true;

				this.error[name] = true;

				if (core.is(response, 'array'))

					for (let index = 0, size = response.length; index < size; index++)

						this.error[name + ':' + response[index]] = true;

			} else

				delete this.error[name];

			return this;

		}

	}

	class WarbleModel {

		constructor(model) {

			if (typeof model === 'object')

				this.model = model;

			else

				throw new Error('WarbleModel expects an object.');

		}

		validate(data) {

			if (typeof data === 'object') {

				let

					response = {
						'data': {},
						'valid': true,
						'invalid': false
					};

				for (let index in this.model) {

					let value = new WarbleFragment(data[index]);

					if (this.model.hasOwnProperty(index) && typeof this.model[index] === 'object')

						for (let validation in this.model[index]) {

							let current = value.validate(validation, this.model[index][validation]);

							if (current.invalid) {

								response.valid = false;

								response.invalid = true;

							}

						}

					response.data[index] = value;

				}

				return response;

			} else

				throw new Error('WarbleModel.validate expects an object.');

		}

	}

	class Warble extends WarbleCore {

		model = (model) => new WarbleModel(model);

		validate(value, options) {

			if (typeof options === 'object') {

				var value = new WarbleFragment(value);

				for (let index in options)

					value.validate(index, options[index]);

				return value;

			} else

				throw new Error('Warble.validate expects an object.');

		}

	}

	var warble = new Warble;

};

//

warble.isHooks.gmail = function(value) {

	return /\@gmail\.com$/i.test(value);

};

let

	schema = warble.model({
		'name': {
			'required': true,
			'minlength': 3
		},
		'surname': {
			'required': true,
			'minlength': 3
		},
		'age': {
			'required': true,
			'min': 18
		},
		'email': {
			'is': ['email', 'gmail']
		}
	}),

	data = {
		'name': 'Diego',
		'surname': 'Lopes Lima',
		'age': 23,
		'email': 'web.diego.lima@yahoo.com'
	};

console.log(schema.validate(data));

console.log(warble.validate(data.name, schema.model.name));

console.log(warble.type([]));
<|endoftext|>


<commit_before>export { ParamDescConfig, Base };

import * as SequenceRandom_NumberArray from "./SequenceRandom_NumberArray.js";

/**
 * Describe which parameter and how many combination for the parameter.
 *
 *
 * @member {ParamDesc.Base} paramDesc
 *   Which parameter to be used in the permutation.
 *
 * @member {number[]} valueOutMinMax
 *   An integer array restricts the generator range to [ valueOutMin, valueOutMax ]. Itself will be restricted to
 * [ paramDesc.valueDesc.range.min, paramDesc.valueDesc.range.max ] at most. When this.kinds is large, this parameter
 * could lower the kinds to reduce test cases quantity. If null or undefined, only one value (between
 * [ paramDesc.valueDesc.range.min, paramDesc.valueDesc.range.max ] randomly) will be generated. In fact, this is the
 * valueOutMinMax parameter of ValueRange.XXX.valueInputOutputGenerator().
 *
 */
class ParamDescConfig {
  constructor( paramDesc, valueOutMinMax ) {
    this.paramDesc = paramDesc;
    this.valueOutMinMax = valueOutMinMax;
  }
}

/**
 * @member {ParamDesc.Xxx} paramDesc    Which parameter is changed.
 * @member {number}  inValue_original    The parameter's original input value.
 * @member {integer} outValue_original  The parameter's original output value.
 * @member {number}  inValue_new         The parameter's new input value.
 * @member {integer} outValue_new       The parameter's new output value. This is adjusted value of outValue_specified.
 * @member {integer} outValue_specified The parameter's output value which is wanted.
 */
class ParamValueChangeRecord {
  constructor( paramDesc, inValue_original, outValue_original, inValue_new, outValue_new, outValue_specified ) {
    this.paramDesc = paramDesc;
    this.inValue_original = inValue_original;
    this.outValue_original = outValue_original;
    this.inValue_new = inValue_new;
    this.outValue_new = outValue_new;
    this.outValue_specified = outValue_specified;
  }
}

/**
 * This is an object { config, id, in, out } which has one number and two sub-objects.
 *
 * @member {object} config
 *   The configuration for generating parameters combination.
 *
 * @param {ParamDescConfig[]} config.paramDescConfigArray
 *   List all the parameters to be used in permutation combination.
 *
 * @member {number} id
 *   The numeric identifier of this testing parameter combination. No matter onYield_isLegal() is true or false, every combination
 * has its own id. The first id is 0 (not 1).
 *
 * @member {number} yieldCount
 *   How many legal (i.e. ( onYield_isLegal() == true ) ) TestParams are yielded.
 *
 * @member {object} in
 *   The "in" sub-object's data members represent every parameters of some (e.g. PointDepthPoint) Params's constructor. Besides,
 * it also has the following properties:
 *   - paramsNumberArrayObject: All (non-concatenated) parameters (include filters and biases) which will be packed into inputFloat32Array.
 *   - inputFloat32Array: A packed Float32Array from paramsNumberArrayObject with byteOffsetBegin.
 *   - byteOffsetBegin: The offset in inputFloat32Array to the first parameter.
 *
 * @member {object} out
 *   The "out" sub-object's data members represent the "should-be" result of some (e.g. PointDepthPoint) Params.extract().
 * That is, it has the data members of this.in except inputFloat32Array, byteOffsetBegin, weights.
 *
 * @member {ParamValueChangeRecord[]} modifyParamValueHistory
 *   A record will be pushed into this list when calling this.modifyParamValue(). When call this.restoreParamValues(),
 * these value will be restored and this list will be cleared to empty.
 *
 */
class Base {

  /**
   *
   */
  constructor( id = -1 ) {
    this.id = id;
    this.yieldCount = 0;
    this.in = { paramsNumberArrayObject: {} };
    this.out = {};
    this.modifyParamValueHistory = [];

    this.SequenceRandom_NumberArray_Bag = new SequenceRandom_NumberArray.Bag(); // For reducing same weights array re-generating.
  }

  /**
   * Called by permuteParamRecursively() before onYield_before() is called.
   *
   * The the following data should already be ready:
   *   - this.id
   *   - this.in.paramsNumberArrayObject: Every should-be-packed parameter.
   *   - this.in.Xxx: every non-packed parameter.
   *   - this.out.Xxx: every parameter.
   *
   * Sub-class should override this method.
   *
   * @return {boolean}
   *   If return true, the onYield_before() will be called. If return false, it mean this configuration is illegal so that there
   * is no yield will be done (i.e. onYield_before() and onYield_after() will not be called).
   */
  onYield_isLegal() {
    return true;
  }

  /**
   * Called by permuteParamRecursively() when a combination of parameters is complete and before this object to be yielded.
   *
   * The the following data should already be ready:
   *   - this.id
   *   - this.in.paramsNumberArrayObject: Every should-be-packed parameter.
   *   - this.in.Xxx: every non-packed parameter.
   *   - this.out.Xxx: every parameter.
   *
   * This method should fill the following data:
   *   - this.in.inputFloat32Array
   *   - this.in.byteOffsetBegin
   *
   * Sub-class should override this method.
   */
  onYield_before() {
  }

  /**
   * Called by permuteParamRecursively() when a combination of parameters is complete and after this object has been yielded.
   *
   * Usually, this method should clear some temperary data members.
   *
   * Sub-class should override this method.
   */
  onYield_after() {
  }

  /**
   * Modify specified parameter's value and record in this.modifyParamValueHistory so that they could be restore by restoreParamValues().
   *
   * @param {ParamDesc.Xxx} paramDesc
   *   The parameter to be doubled.
   *
   * @param {integer} outValue_specified
   *   The new value to be placed at the this.out[ paramDesc.paramName ].
   */
  modifyParamValue( paramDesc, outValue_specified ) {
    let paramName = paramDesc.paramName;

    let outValue_original = this.out[ paramName ];
    if ( outValue_original == undefined )
      return; // The parameter does not exist. No need to modify it.

    let valueDesc = paramDesc.valueDesc;
    let valueRange = valueDesc.range;

    let outValue_new = valueRange.adjust( outValue_specified ); // Confirm the new value is legal.

    this.out[ paramName ] = outValue_new;

    let inValue_original, inValue_new;

    let singleMinMax = [ outValue_new, outValue_new ]; // Only generate one new value.
    for ( let pair of valueRange.valueInputOutputGenerator( undefined, singleMinMax ) ) {

      inValue_new = pair.valueInput;

      if ( this.in[ paramName ] != undefined ) {
        inValue_original = this.in[ paramName ];
        this.in[ paramName ] = inValue_new;
      }

      if ( this.in.paramsNumberArrayObject[ paramName ] != undefined ) { // Note: If the element exists, it must be an array.

//!!! (2022/05/24 Remarked) Now is number (not number array)
//         inValue_original = this.in.paramsNumberArrayObject[ paramName ][ 0 ];
//         this.in.paramsNumberArrayObject[ paramName ][ 0 ] = inValue_new; // The value is always at the element 0.

        inValue_original = this.in.paramsNumberArrayObject[ paramName ]; // (should be a number (not number array)).
        this.in.paramsNumberArrayObject[ paramName ] = inValue_new;
      }
    }

    let changeRecord = new ParamValueChangeRecord( paramDesc, inValue_original, outValue_original, inValue_new, outValue_new, outValue_specified );
    this.modifyParamValueHistory.push( changeRecord );
  }

  /**
   * Restore parameters' values according to this.modifyParamValueHistory. And empty this.modifyParamValueHistory.
   */
  restoreParamValues() {
    for ( let i = this.modifyParamValueHistory.length - 1; i >= 0; --i ) { // From the last to first.
      let changeRecord = this.modifyParamValueHistory[ i ];
      let paramName = changeRecord.paramDesc.paramName;

      if ( this.out[ paramName ] != undefined )
        this.out[ paramName ] = changeRecord.outValue_original;

      if ( this.in[ paramName ] != undefined ) {
        this.in[ paramName ] = changeRecord.inValue_original;
      }

      if ( this.in.paramsNumberArrayObject[ paramName ] != undefined ) { // Note: If the element exists, it must be an array.

//!!! (2022/05/24 Remarked) Now is number (not number array)
//      this.in.paramsNumberArrayObject[ paramName ][ 0 ] = changeRecord.inValue_original; // The value is always at the element 0.

        this.in.paramsNumberArrayObject[ paramName ] = changeRecord.inValue_original; // (should be a number (not number array)).
      }
    }

    this.modifyParamValueHistory.length = 0; // Clear history.
  }

  /**
   * Ensure io_object[ propertyName ] exists as a number array with specified length. It will be filled with random number
   * as ( sequence_nunmer + random_number_offset ).
   *
   * @param {object} io_object        The object to be checked and modified.
   * @param {string} propertyName     The property io_object[ propertyName ] will be ensured as a number array.
   * @param {number} elementCount     The property io_object[ propertyName ].length will be ensured as elementCount.
   * @param {number} randomOffsetMin  The random number offet lower bound.
   * @param {number} randomOffsetMax  The random number offet upperer bound.
   */
  static ensure_object_property_numberArray_length_filled( io_object, propertyName, elementCount, randomOffsetMin = 0, randomOffsetMax = 0 ) {

    if (   ( io_object[ propertyName ] == undefined )          // The property does not exist.
        || ( !( io_object[ propertyName ] instanceof Array ) ) // The property exists but is not an array.
       ) {
      io_object[ propertyName ] = new Array( elementCount );   // Ensure array with specified length.

    } else {                                                   // The property exists and is an array.
      io_object[ propertyName ].length = elementCount;         // Ensure it with specified length.
    }

    RandTools.fill_numberArray( io_object[ propertyName ], elementCount, randomOffsetMin, randomOffsetMax );
  }

  /**
   * Similar to Base.ensure_object_property_numberArray_length_filled(). But the property will be a shared number array. Its value
   * may be shared with other caller.
   *
   * This may have better performance because of number array re-using (instead of re-generating).
   *
   *
   * @param {object} io_object        The object to be checked and modified.
   * @param {string} propertyName     The property io_object[ propertyName ] will be ensured as a number array.
   * @param {number} elementCount     The property io_object[ propertyName ].length will be ensured as elementCount.
   * @param {number} randomOffsetMin  The random number offet lower bound.
   * @param {number} randomOffsetMax  The random number offet upperer bound.
   */
  ensure_object_property_numberArray_length_existed( io_object, propertyName, elementCount, randomOffsetMin = 0, randomOffsetMax = 0 ) {
    io_object[ propertyName ] = this.SequenceRandom_NumberArray_Bag.get_by_elementCount_randomOffsetMin_randomOffsetMax(
      elementCount, randomOffsetMin, randomOffsetMax );
  }

  /**
   * Responsible for generating testing paramters combinations.
   *
   * @param {ParamDescConfig[]} paramDescConfigArray
   *   List all the parameters to be used in permutation combination.
   *
   * @yield {Base}
   *   Yield this object itself. The returned object (it is this object itself) should not be modified because it will be re-used.
   */
  static * ParamsGenerator( paramDescConfigArray ) {
    this.config = { paramDescConfigArray: paramDescConfigArray };

    this.in.paramsNumberArrayObject = {}; // All parameters which will be packed into weights array.
    yield *Base.permuteParamRecursively.call( this, 0 );
  }

  /**
   * This method will modify this.id and this.in.paramsNumberArrayObject. It also calls itself recursively to permute all parameters.
   *
   * @param {number} currentParamDescIndex
   *   The index into the this.paramDescArray[]. It represents the current parameter to be tried.
   *
   * @yield {Base}
   *   Every time one kind of parameters' combination is generated, the this.result will be yielded.
   */
  static * permuteParamRecursively( currentParamDescConfigIndex ) {

    if ( currentParamDescConfigIndex >= this.config.paramDescConfigArray.length ) {
      // All parameters are used to be composed as one kind of combination.

      ++this.id; // Every combination has unique id no matter whether is legal to be yielded.

      let bLegalToYield = this.onYield_isLegal();
      if ( bLegalToYield ) {

        ++this.yieldCount;  // Complete one kind of combination.

        this.onYield_before();
        yield this;
        this.onYield_after();
        this.restoreParamValues(); // Restore this object because onYield_before() may modify it.
      }

      return; // Stop this recusive. Back-track to another parameters combination.
    }

    // Prepare an re-usable object for placing the value pair of current ParamDesc. (For reducing memory re-allocation.)
    let valuePair;
    {
      if ( !this.config.paramValuePairArray ) {
        this.config.paramValuePairArray = new Array( this.config.paramDescConfigArray.length );
        for ( let i = 0; i < this.config.paramValuePairArray; ++i ) {
          this.config.paramValuePairArray[ currentParamDescConfigIndex ] = {};
        }
      }
      valuePair = this.config.paramValuePairArray[ currentParamDescConfigIndex ];
    }

    let nextParamDescConfigIndex = currentParamDescConfigIndex + 1;

    let paramDescConfig = this.config.paramDescConfigArray[ currentParamDescConfigIndex ];
    let paramDesc = paramDescConfig.paramDesc;
    for ( let pair of paramDesc.valueDesc.range.valueInputOutputGenerator( valuePair, undefined, paramDescConfig.valueOutMinMax ) ) {

      //!!! (2021/07/06 Temp Debug) Check the algorithm might be wrong.
      //if ( paramDesc.valueDesc.range.adjust( pair.valueInput ) != pair.valueOutput )
      //  debugger;

      this.out[ paramDesc.paramName ] = pair.valueOutput;

      // Randomly place the parameter directly or in weights array.

      //!!! (2021/07/19 Temp Used) For testing without randomly.
      //let dice = 0;
      let dice = Math.random();
      if ( dice < 0.5 ) {
        // Try parameter value assigned directly (i.e. by specifying).
        this.in[ paramDesc.paramName ] = pair.valueInput;
        yield *Base.permuteParamRecursively.call( this, nextParamDescConfigIndex );

      } else {
        // Try parameter value assigned from inputFloat32Array (i.e. by evolution).
        this.in[ paramDesc.paramName ] = null;
        this.in.paramsNumberArrayObject[ paramDesc.paramName ] = pair.valueInput; // (number or number array)
        yield *Base.permuteParamRecursively.call( this, nextParamDescConfigIndex );

        this.in.paramsNumberArrayObject[ paramDesc.paramName ] = undefined; // So that it could be re-tried as by-specifying when backtracking.
      }
    }
  }

}
<commit_msg>Update TestParams.js<commit_after>export { ParamDescConfig, Base };

import * as SequenceRandom_NumberArray from "./SequenceRandom_NumberArray.js";

/**
 * Describe which parameter and how many combination for the parameter.
 *
 *
 * @member {ParamDesc.Base} paramDesc
 *   Which parameter to be used in the permutation.
 *
 * @member {number[]} valueOutMinMax
 *   An integer array restricts the generator range to [ valueOutMin, valueOutMax ]. Itself will be restricted to
 * [ paramDesc.valueDesc.range.min, paramDesc.valueDesc.range.max ] at most. When this.kinds is large, this parameter
 * could lower the kinds to reduce test cases quantity. If null or undefined, only one value (between
 * [ paramDesc.valueDesc.range.min, paramDesc.valueDesc.range.max ] randomly) will be generated. In fact, this is the
 * valueOutMinMax parameter of ValueRange.XXX.valueInputOutputGenerator().
 *
 */
class ParamDescConfig {
  constructor( paramDesc, valueOutMinMax ) {
    this.paramDesc = paramDesc;
    this.valueOutMinMax = valueOutMinMax;
  }
}

/**
 * @member {ParamDesc.Xxx} paramDesc    Which parameter is changed.
 * @member {number}  inValue_original    The parameter's original input value.
 * @member {integer} outValue_original  The parameter's original output value.
 * @member {number}  inValue_new         The parameter's new input value.
 * @member {integer} outValue_new       The parameter's new output value. This is adjusted value of outValue_specified.
 * @member {integer} outValue_specified The parameter's output value which is wanted.
 */
class ParamValueChangeRecord {
  constructor( paramDesc, inValue_original, outValue_original, inValue_new, outValue_new, outValue_specified ) {
    this.paramDesc = paramDesc;
    this.inValue_original = inValue_original;
    this.outValue_original = outValue_original;
    this.inValue_new = inValue_new;
    this.outValue_new = outValue_new;
    this.outValue_specified = outValue_specified;
  }
}

/**
 * This is an object { config, id, in, out } which has one number and two sub-objects.
 *
 * @member {object} config
 *   The configuration for generating parameters combination.
 *
 * @param {ParamDescConfig[]} config.paramDescConfigArray
 *   List all the parameters to be used in permutation combination.
 *
 * @member {number} id
 *   The numeric identifier of this testing parameter combination. No matter onYield_isLegal() is true or false, every combination
 * has its own id. The first id is 0 (not 1).
 *
 * @member {number} yieldCount
 *   How many legal (i.e. ( onYield_isLegal() == true ) ) TestParams are yielded.
 *
 * @member {object} in
 *   The "in" sub-object's data members represent every parameters of some (e.g. PointDepthPoint) Params's constructor. Besides,
 * it also has the following properties:
 *   - paramsNumberArrayObject: All (non-concatenated) parameters (include filters and biases) which will be packed into inputFloat32Array.
 *   - inputFloat32Array: A packed Float32Array from paramsNumberArrayObject with byteOffsetBegin.
 *   - byteOffsetBegin: The offset in inputFloat32Array to the first parameter.
 *
 * @member {object} out
 *   The "out" sub-object's data members represent the "should-be" result of some (e.g. PointDepthPoint) Params.extract().
 * That is, it has the data members of this.in except inputFloat32Array, byteOffsetBegin, weights.
 *
 * @member {ParamValueChangeRecord[]} modifyParamValueHistory
 *   A record will be pushed into this list when calling this.modifyParamValue(). When call this.restoreParamValues(),
 * these value will be restored and this list will be cleared to empty.
 *
 */
class Base {

  /**
   *
   */
  constructor( id = -1 ) {
    this.id = id;
    this.yieldCount = 0;
    this.in = { paramsNumberArrayObject: {} };
    this.out = {};
    this.modifyParamValueHistory = [];

    this.SequenceRandom_NumberArray_Bag = new SequenceRandom_NumberArray.Bag(); // For reducing same weights array re-generating.
  }

  /**
   * Called by permuteParamRecursively() before onYield_before() is called.
   *
   * The the following data should already be ready:
   *   - this.id
   *   - this.in.paramsNumberArrayObject: Every should-be-packed parameter.
   *   - this.in.Xxx: every non-packed parameter.
   *   - this.out.Xxx: every parameter.
   *
   * Sub-class should override this method.
   *
   * @return {boolean}
   *   If return true, the onYield_before() will be called. If return false, it mean this configuration is illegal so that there
   * is no yield will be done (i.e. onYield_before() and onYield_after() will not be called).
   */
  onYield_isLegal() {
    return true;
  }

  /**
   * Called by permuteParamRecursively() when a combination of parameters is complete and before this object to be yielded.
   *
   * The the following data should already be ready:
   *   - this.id
   *   - this.in.paramsNumberArrayObject: Every should-be-packed parameter.
   *   - this.in.Xxx: every non-packed parameter.
   *   - this.out.Xxx: every parameter.
   *
   * This method should fill the following data:
   *   - this.in.inputFloat32Array
   *   - this.in.byteOffsetBegin
   *
   * Sub-class should override this method.
   */
  onYield_before() {
  }

  /**
   * Called by permuteParamRecursively() when a combination of parameters is complete and after this object has been yielded.
   *
   * Usually, this method should clear some temperary data members.
   *
   * Sub-class should override this method.
   */
  onYield_after() {
  }

  /**
   * Modify specified parameter's value and record in this.modifyParamValueHistory so that they could be restore by restoreParamValues().
   *
   * @param {ParamDesc.Xxx} paramDesc
   *   The parameter to be doubled.
   *
   * @param {integer} outValue_specified
   *   The new value to be placed at the this.out[ paramDesc.paramName ].
   */
  modifyParamValue( paramDesc, outValue_specified ) {
    let paramName = paramDesc.paramName;

    let outValue_original = this.out[ paramName ];
    if ( outValue_original == undefined )
      return; // The parameter does not exist. No need to modify it.

    let valueDesc = paramDesc.valueDesc;
    let valueRange = valueDesc.range;

    let outValue_new = valueRange.adjust( outValue_specified ); // Confirm the new value is legal.

    this.out[ paramName ] = outValue_new;

    let inValue_original, inValue_new;

    let singleMinMax = [ outValue_new, outValue_new ]; // Only generate one new value.
    for ( let pair of valueRange.valueInputOutputGenerator( undefined, singleMinMax ) ) {

      inValue_new = pair.valueInput;

      if ( this.in[ paramName ] != undefined ) {
        inValue_original = this.in[ paramName ];
        this.in[ paramName ] = inValue_new;
      }

      if ( this.in.paramsNumberArrayObject[ paramName ] != undefined ) { // Note: If the element exists, it must be an array.

//!!! (2022/05/24 Remarked) Now is number (not number array)
//         inValue_original = this.in.paramsNumberArrayObject[ paramName ][ 0 ];
//         this.in.paramsNumberArrayObject[ paramName ][ 0 ] = inValue_new; // The value is always at the element 0.

        inValue_original = this.in.paramsNumberArrayObject[ paramName ]; // (should be a number (not number array)).
        this.in.paramsNumberArrayObject[ paramName ] = inValue_new;
      }
    }

    let changeRecord = new ParamValueChangeRecord( paramDesc, inValue_original, outValue_original, inValue_new, outValue_new, outValue_specified );
    this.modifyParamValueHistory.push( changeRecord );
  }

  /**
   * Restore parameters' values according to this.modifyParamValueHistory. And empty this.modifyParamValueHistory.
   */
  restoreParamValues() {
    for ( let i = this.modifyParamValueHistory.length - 1; i >= 0; --i ) { // From the last to first.
      let changeRecord = this.modifyParamValueHistory[ i ];
      let paramName = changeRecord.paramDesc.paramName;

      if ( this.out[ paramName ] != undefined )
        this.out[ paramName ] = changeRecord.outValue_original;

      if ( this.in[ paramName ] != undefined ) {
        this.in[ paramName ] = changeRecord.inValue_original;
      }

      if ( this.in.paramsNumberArrayObject[ paramName ] != undefined ) { // Note: If the element exists, it must be an array.

//!!! (2022/05/24 Remarked) Now is number (not number array)
//      this.in.paramsNumberArrayObject[ paramName ][ 0 ] = changeRecord.inValue_original; // The value is always at the element 0.

        this.in.paramsNumberArrayObject[ paramName ] = changeRecord.inValue_original; // (should be a number (not number array)).
      }
    }

    this.modifyParamValueHistory.length = 0; // Clear history.
  }

  /**
   * Ensure io_object[ propertyName ] exists as a number array with specified length. It will be filled with random number
   * as ( sequence_nunmer + random_number_offset ).
   *
   * @param {object} io_object        The object to be checked and modified.
   * @param {string} propertyName     The property io_object[ propertyName ] will be ensured as a number array.
   * @param {number} elementCount     The property io_object[ propertyName ].length will be ensured as elementCount.
   * @param {number} randomOffsetMin  The random number offet lower bound.
   * @param {number} randomOffsetMax  The random number offet upperer bound.
   */
  static ensure_object_property_numberArray_length_filled( io_object, propertyName, elementCount, randomOffsetMin = 0, randomOffsetMax = 0 ) {

    if (   ( io_object[ propertyName ] == undefined )          // The property does not exist.
        || ( !( io_object[ propertyName ] instanceof Array ) ) // The property exists but is not an array.
       ) {
      io_object[ propertyName ] = new Array( elementCount );   // Ensure array with specified length.

    } else {                                                   // The property exists and is an array.
      io_object[ propertyName ].length = elementCount;         // Ensure it with specified length.
    }

    RandTools.fill_numberArray( io_object[ propertyName ], elementCount, randomOffsetMin, randomOffsetMax );
  }

  /**
   * Similar to Base.ensure_object_property_numberArray_length_filled(). But the property will be a shared number array. Its value
   * may be shared with other caller.
   *
   * This may have better performance because of number array re-using (instead of re-generating).
   *
   *
   * @param {object} io_object        The object to be checked and modified.
   * @param {string} propertyName     The property io_object[ propertyName ] will be ensured as a number array.
   * @param {number} elementCount     The property io_object[ propertyName ].length will be ensured as elementCount.
   * @param {number} randomOffsetMin  The random number offet lower bound.
   * @param {number} randomOffsetMax  The random number offet upperer bound.
   */
  ensure_object_property_numberArray_length_existed( io_object, propertyName, elementCount, randomOffsetMin = 0, randomOffsetMax = 0 ) {
    io_object[ propertyName ] = this.SequenceRandom_NumberArray_Bag.get_by_elementCount_randomOffsetMin_randomOffsetMax(
      elementCount, randomOffsetMin, randomOffsetMax );
  }

  /**
   * Responsible for generating testing paramters combinations.
   *
   * @param {ParamDescConfig[]} paramDescConfigArray
   *   List all the parameters to be used in permutation combination.
   *
   * @yield {Base}
   *   Yield this object itself. The returned object (it is this object itself) should not be modified because it will be re-used.
   */
  static * ParamsGenerator( paramDescConfigArray ) {
    this.config = { paramDescConfigArray: paramDescConfigArray };

    this.in.paramsNumberArrayObject = {}; // All parameters which will be packed into weights array.
    yield *Base.permuteParamRecursively.call( this, 0 );
  }

  /**
   * This method will modify this.id and this.in.paramsNumberArrayObject. It also calls itself recursively to permute all parameters.
   *
   * @param {number} currentParamDescIndex
   *   The index into the this.paramDescArray[]. It represents the current parameter to be tried.
   *
   * @yield {Base}
   *   Every time one kind of parameters' combination is generated, the this.result will be yielded.
   */
  static * permuteParamRecursively( currentParamDescConfigIndex ) {

    if ( currentParamDescConfigIndex >= this.config.paramDescConfigArray.length ) {
      // All parameters are used to be composed as one kind of combination.

      ++this.id; // Every combination has unique id no matter whether is legal to be yielded.

      let bLegalToYield = this.onYield_isLegal();
      if ( bLegalToYield ) {

        ++this.yieldCount;  // Complete one kind of combination.

        this.onYield_before();
        yield this;
        this.onYield_after();
        this.restoreParamValues(); // Restore this object because onYield_before() may modify it.
      }

      return; // Stop this recusive. Back-track to another parameters combination.
    }

    // Prepare an re-usable object for placing the value pair of current ParamDesc. (For reducing memory re-allocation.)
    let valuePair;
    {
      if ( !this.config.paramValuePairArray ) {
        this.config.paramValuePairArray = new Array( this.config.paramDescConfigArray.length );
        for ( let i = 0; i < this.config.paramValuePairArray.length; ++i ) {
          this.config.paramValuePairArray[ currentParamDescConfigIndex ] = {};
        }
      }
      valuePair = this.config.paramValuePairArray[ currentParamDescConfigIndex ];
    }

    let nextParamDescConfigIndex = currentParamDescConfigIndex + 1;

    let paramDescConfig = this.config.paramDescConfigArray[ currentParamDescConfigIndex ];
    let paramDesc = paramDescConfig.paramDesc;
    for ( let pair of paramDesc.valueDesc.range.valueInputOutputGenerator( valuePair, undefined, paramDescConfig.valueOutMinMax ) ) {

      //!!! (2021/07/06 Temp Debug) Check the algorithm might be wrong.
      //if ( paramDesc.valueDesc.range.adjust( pair.valueInput ) != pair.valueOutput )
      //  debugger;

      this.out[ paramDesc.paramName ] = pair.valueOutput;

      // Randomly place the parameter directly or in weights array.

      //!!! (2021/07/19 Temp Used) For testing without randomly.
      //let dice = 0;
      let dice = Math.random();
      if ( dice < 0.5 ) {
        // Try parameter value assigned directly (i.e. by specifying).
        this.in[ paramDesc.paramName ] = pair.valueInput;
        yield *Base.permuteParamRecursively.call( this, nextParamDescConfigIndex );

      } else {
        // Try parameter value assigned from inputFloat32Array (i.e. by evolution).
        this.in[ paramDesc.paramName ] = null;
        this.in.paramsNumberArrayObject[ paramDesc.paramName ] = pair.valueInput; // (number or number array)
        yield *Base.permuteParamRecursively.call( this, nextParamDescConfigIndex );

        this.in.paramsNumberArrayObject[ paramDesc.paramName ] = undefined; // So that it could be re-tried as by-specifying when backtracking.
      }
    }
  }

}
<|endoftext|>


<commit_before>/*global define*/
define([
        './DeveloperError',
        './JulianDate',
        './ClockStep',
        './ClockRange',
        './TimeStandard'
       ], function(
         DeveloperError,
         JulianDate,
         ClockStep,
         ClockRange,
         TimeStandard) {
    "use strict";

    /**
     * A simple clock for keeping track of simulated time.
     * @alias Clock
     * @constructor
     *
     * @param {Object} [template] The template object containing the properties to be set on the clock.
     * @exception {DeveloperError} startTime must come before stopTime.
     *
     * @see ClockStep
     * @see ClockRange
     * @see JulianDate
     *
     * @example
     * //Create a clock that loops on Christmas day 2012 and runs
     * //in real-time.  currentTime will default to startTime.
     * var clock = new Clock({
     *    startTime : JulianDate.fromIso8601("12-25-2012");
     *    stopTime : JulianDate.fromIso8601("12-26-2012");
     *    clockRange : ClockRange.LOOP;
     * });
     */
    var Clock = function(template) {
        var t = template;
        if (typeof t === 'undefined') {
            t = {};
        }

        var startTime = t.startTime;
        var startTimeUndefined = typeof startTime === 'undefined';

        var stopTime = t.stopTime;
        var stopTimeUndefined = typeof stopTime === 'undefined';

        var currentTime = t.currentTime;
        var currentTimeUndefined = typeof currentTime === 'undefined';

        if (startTimeUndefined && stopTimeUndefined && currentTimeUndefined) {
            currentTime = new JulianDate();
            startTime = currentTime.addDays(-0.5);
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined && stopTimeUndefined) {
            startTime = currentTime.addDays(-0.5);
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined && currentTimeUndefined) {
            startTime = stopTime.addDays(-1.0);
            currentTime = stopTime.addDays(0.5);
        } else if (currentTimeUndefined && stopTimeUndefined) {
            currentTime = startTime.addDays(0.5);
            stopTime = startTime.addDays(1.0);
        } else if (currentTimeUndefined) {
            currentTime = startTime.addSeconds(startTime.secondsDifference(stopTime));
        } else if (stopTimeUndefined) {
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined) {
            startTime = currentTime.addDays(-0.5);
        }

        if (startTime.greaterThan(stopTime)) {
            throw new DeveloperError('startTime must come before stopTime.');
        }

        var multiplier = t.multiplier;
        if (typeof multiplier === 'undefined') {
            multiplier = 1.0;
        }

        var clockStep = t.clockStep;
        if (typeof clockStep === 'undefined') {
            clockStep = ClockStep.SYSTEM_CLOCK_DEPENDENT;
        }

        var clockRange = t.clockRange;
        if (typeof clockRange === 'undefined') {
            clockRange = ClockRange.UNBOUNDED;
        }

        /**
         * The start time of the clock.
         * @type JulianDate
         */
        this.startTime = TimeStandard.convertUtcToTai(startTime);

        /**
         * The stop time of the clock.
         * @type JulianDate
         */
        this.stopTime = TimeStandard.convertUtcToTai(stopTime);

        /**
         * The current time.
         * @type JulianDate
         */
        this.currentTime = currentTime;

        /**
         * Determines how much time advances when tick is called, negative values allow for advancing backwards.
         * If clockStep is set to ClockStep.TICK_DEPENDENT this is the number of seconds to advance.
         * If clockStep is set to ClockStep.SYSTEM_CLOCK_DEPENDENT this value is multiplied by the
         * elapsed system time since the last call to tick.
         * @type Number
         */
        this.multiplier = multiplier;

        /**
         * Determines if calls to tick are frame dependent or system clock dependent.
         * @type ClockStep
         */
        this.clockStep = clockStep;

        /**
         * Determines how tick should behave when startTime or stopTime is reached.
         * @type ClockRange
         */
        this.clockRange = clockRange;

        this._lastCpuTime = new Date().getTime();
    };

    /**
     * Advances the clock from the currentTime based on the current configuration options.
     * @memberof Clock
     *
     * @param {Number} [secondsToTick] optional parameter to force the clock to tick the provided number of seconds,
     * regardless of the value of clockStep and multiplier.
     * @returns {JulianDate} The new value of the currentTime property.
     */
    Clock.prototype.tick = function(secondsToTick) {
        return this._tick(secondsToTick, this.multiplier);
    };

    /**
     * Advances the clock in the opposite direction of the current multiplier.
     * If multiplier is positive this will advance the clock backwards one tick.
     * If multiplier is negative this will advance the clock forward one tick.
     * @memberof Clock
     *
     * @returns {JulianDate} The new value of Clock.currentTime
     */
    Clock.prototype.reverseTick = function() {
        return this._tick(undefined, -this.multiplier);
    };

    Clock.prototype._tick = function(secondsToTick, multiplier) {
        var startTime = this.startTime;
        var stopTime = this.stopTime;
        var currentTime = this.currentTime;
        var currentCpuTime = new Date().getTime();

        if (typeof secondsToTick === 'undefined') {
            if (this.clockStep === ClockStep.TICK_DEPENDENT) {
                currentTime = currentTime.addSeconds(multiplier);
            } else {
                var milliseconds = currentCpuTime - this._lastCpuTime;
                currentTime = currentTime.addSeconds(multiplier * (milliseconds / 1000.0));
            }
        } else {
            currentTime = currentTime.addSeconds(secondsToTick);
        }

        if (this.clockRange === ClockRange.CLAMPED) {
            if (currentTime.lessThan(startTime)) {
                currentTime = startTime;
            } else if (currentTime.greaterThan(stopTime)) {
                currentTime = stopTime;
            }
        } else if (this.clockRange === ClockRange.LOOP) {
            while (currentTime.lessThan(startTime)) {
                currentTime = stopTime.addSeconds(startTime.getSecondsDifference(currentTime));
            }
            while (currentTime.greaterThan(stopTime)) {
                currentTime = startTime.addSeconds(stopTime.getSecondsDifference(currentTime));
            }
        }

        this.currentTime = currentTime;
        this._lastCpuTime = currentCpuTime;
        return currentTime;
    };

    return Clock;
});<commit_msg>Always use TAI time with Clock to improve performance.<commit_after>/*global define*/
define([
        './DeveloperError',
        './JulianDate',
        './ClockStep',
        './ClockRange',
        './TimeStandard'
       ], function(
         DeveloperError,
         JulianDate,
         ClockStep,
         ClockRange,
         TimeStandard) {
    "use strict";

    /**
     * A simple clock for keeping track of simulated time.
     * @alias Clock
     * @constructor
     *
     * @param {Object} [template] The template object containing the properties to be set on the clock.
     * @exception {DeveloperError} startTime must come before stopTime.
     *
     * @see ClockStep
     * @see ClockRange
     * @see JulianDate
     *
     * @example
     * //Create a clock that loops on Christmas day 2012 and runs
     * //in real-time.  currentTime will default to startTime.
     * var clock = new Clock({
     *    startTime : JulianDate.fromIso8601("12-25-2012");
     *    stopTime : JulianDate.fromIso8601("12-26-2012");
     *    clockRange : ClockRange.LOOP;
     * });
     */
    var Clock = function(template) {
        var t = template;
        if (typeof t === 'undefined') {
            t = {};
        }

        var startTime = t.startTime;
        var startTimeUndefined = typeof startTime === 'undefined';

        var stopTime = t.stopTime;
        var stopTimeUndefined = typeof stopTime === 'undefined';

        var currentTime = t.currentTime;
        var currentTimeUndefined = typeof currentTime === 'undefined';

        if (startTimeUndefined && stopTimeUndefined && currentTimeUndefined) {
            currentTime = new JulianDate();
            startTime = currentTime.addDays(-0.5);
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined && stopTimeUndefined) {
            startTime = currentTime.addDays(-0.5);
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined && currentTimeUndefined) {
            startTime = stopTime.addDays(-1.0);
            currentTime = stopTime.addDays(0.5);
        } else if (currentTimeUndefined && stopTimeUndefined) {
            currentTime = startTime.addDays(0.5);
            stopTime = startTime.addDays(1.0);
        } else if (currentTimeUndefined) {
            currentTime = startTime.addSeconds(startTime.secondsDifference(stopTime));
        } else if (stopTimeUndefined) {
            stopTime = currentTime.addDays(0.5);
        } else if (startTimeUndefined) {
            startTime = currentTime.addDays(-0.5);
        }

        if (startTime.greaterThan(stopTime)) {
            throw new DeveloperError('startTime must come before stopTime.');
        }

        var multiplier = t.multiplier;
        if (typeof multiplier === 'undefined') {
            multiplier = 1.0;
        }

        var clockStep = t.clockStep;
        if (typeof clockStep === 'undefined') {
            clockStep = ClockStep.SYSTEM_CLOCK_DEPENDENT;
        }

        var clockRange = t.clockRange;
        if (typeof clockRange === 'undefined') {
            clockRange = ClockRange.UNBOUNDED;
        }

        /**
         * The start time of the clock.
         * @type JulianDate
         */
        this.startTime = TimeStandard.convertUtcToTai(startTime);

        /**
         * The stop time of the clock.
         * @type JulianDate
         */
        this.stopTime = TimeStandard.convertUtcToTai(stopTime);

        /**
         * The current time.
         * @type JulianDate
         */
        this.currentTime = TimeStandard.convertUtcToTai(currentTime);

        /**
         * Determines how much time advances when tick is called, negative values allow for advancing backwards.
         * If clockStep is set to ClockStep.TICK_DEPENDENT this is the number of seconds to advance.
         * If clockStep is set to ClockStep.SYSTEM_CLOCK_DEPENDENT this value is multiplied by the
         * elapsed system time since the last call to tick.
         * @type Number
         */
        this.multiplier = multiplier;

        /**
         * Determines if calls to tick are frame dependent or system clock dependent.
         * @type ClockStep
         */
        this.clockStep = clockStep;

        /**
         * Determines how tick should behave when startTime or stopTime is reached.
         * @type ClockRange
         */
        this.clockRange = clockRange;

        this._lastCpuTime = new Date().getTime();
    };

    /**
     * Advances the clock from the currentTime based on the current configuration options.
     * @memberof Clock
     *
     * @param {Number} [secondsToTick] optional parameter to force the clock to tick the provided number of seconds,
     * regardless of the value of clockStep and multiplier.
     * @returns {JulianDate} The new value of the currentTime property.
     */
    Clock.prototype.tick = function(secondsToTick) {
        return this._tick(secondsToTick, this.multiplier);
    };

    /**
     * Advances the clock in the opposite direction of the current multiplier.
     * If multiplier is positive this will advance the clock backwards one tick.
     * If multiplier is negative this will advance the clock forward one tick.
     * @memberof Clock
     *
     * @returns {JulianDate} The new value of Clock.currentTime
     */
    Clock.prototype.reverseTick = function() {
        return this._tick(undefined, -this.multiplier);
    };

    Clock.prototype._tick = function(secondsToTick, multiplier) {
        var startTime = this.startTime;
        var stopTime = this.stopTime;
        var currentTime = this.currentTime;
        var currentCpuTime = new Date().getTime();

        if (typeof secondsToTick === 'undefined') {
            if (this.clockStep === ClockStep.TICK_DEPENDENT) {
                currentTime = currentTime.addSeconds(multiplier);
            } else {
                var milliseconds = currentCpuTime - this._lastCpuTime;
                currentTime = currentTime.addSeconds(multiplier * (milliseconds / 1000.0));
            }
        } else {
            currentTime = currentTime.addSeconds(secondsToTick);
        }

        if (this.clockRange === ClockRange.CLAMPED) {
            if (currentTime.lessThan(startTime)) {
                currentTime = startTime;
            } else if (currentTime.greaterThan(stopTime)) {
                currentTime = stopTime;
            }
        } else if (this.clockRange === ClockRange.LOOP) {
            while (currentTime.lessThan(startTime)) {
                currentTime = stopTime.addSeconds(startTime.getSecondsDifference(currentTime));
            }
            while (currentTime.greaterThan(stopTime)) {
                currentTime = startTime.addSeconds(stopTime.getSecondsDifference(currentTime));
            }
        }

        this.currentTime = currentTime;
        this._lastCpuTime = currentCpuTime;
        return currentTime;
    };

    return Clock;
});<|endoftext|>


<commit_before>describe("Employees of <XYZ> Company:", function () {
    describe('TAX/TDS Currency', function () {
        var index = 0;
        var myRegion, myCurrency;
        beforeEach(function () {
            myRegion = ['INDIA', 'UK', 'US'];
            myCurrency = new Currency(myRegion[index]);
        });
        afterEach(function () {
            index += 1;
        });
        //Scenario -1
        it('Currency should be used INR', function () {
            expect(myCurrency.currency).toBe('INR');
        });
        //Scenario -2
        it('Currency should be used GBP (Pound, UK£)', function () {
            expect(myCurrency.currency).toBe('UK£');
        });
        //Scenario -3
        it('Currency should be used USD (US$)', function () {
            expect(myCurrency.currency).toBe('US$');
        });
    });
    describe('TAX/TDS', function () {
        describe('Tax deducted for Indian Employees, ', function () {
            var index = 0;
            var grossTaxableIncome = [300000, 700000, 1300000];
            var myTaxableIncome;
            beforeEach(function () {
                myTaxableIncome = new TaxIndiaEmp();
                myTaxableIncome.setIncome(grossTaxableIncome[index]);
            });
            afterEach(function () {
                index += 1;
            });

            //Scenario -4
            it('Should be duducted 10% if Gross Income is between RS 250,000/- AND RS 500,000/-', function () {
                //Let's assume the taxable income is RS 300,000/-
                expect(myTaxableIncome.calculateTDS()).toEqual(5000);
            });
            //Scenario -5
            it('Should be deducted 20% if Gross Income is between RS 500,000/- and RS 1,000,000/-', function () {
                //Let's assume the taxable income is RS 700,000/-
                expect(myTaxableIncome.calculateTDS()).toEqual(40000);
            });
            //Scenario -6
            it('Should be deducted 30% if Gross Income is >RS 10,000,00/-', function () {
                expect(myTaxableIncome.calculateTDS()).toEqual(90000);
            });
        });
        describe('Tax deducted for United Kingdom Employees', function () {

            // xit('Should be deducted 20% if Gross Annual Income is between £10,000/- and £32,000', function () {
            //
            // });
        });
        describe('Tax deducted for United States Employees', function () {
            // xit('Should be deducted 35% of Gross Taxable Income if employee status is SINGLE', function () {
            //
            // });
            // xit('Should be deducted 30% of Gross Taxable Income if employee status is MARRIED', function () {
            //
            // });
        });
    });
});<commit_msg>Revert "comment pending test"<commit_after>describe("Employees of <XYZ> Company:", function () {
    describe('TAX/TDS Currency', function () {
        var index = 0;
        var myRegion, myCurrency;
        beforeEach(function () {
            myRegion = ['INDIA', 'UK', 'US'];
            myCurrency = new Currency(myRegion[index]);
        });
        afterEach(function () {
            index += 1;
        });
        //Scenario -1
        it('Currency should be used INR', function () {
            expect(myCurrency.currency).toBe('INR');
        });
        //Scenario -2
        it('Currency should be used GBP (Pound, UK£)', function () {
            expect(myCurrency.currency).toBe('UK£');
        });
        //Scenario -3
        it('Currency should be used USD (US$)', function () {
            expect(myCurrency.currency).toBe('US$');
        });
    });
    describe('TAX/TDS', function () {
        describe('Tax deducted for Indian Employees, ', function () {
            var index = 0;
            var grossTaxableIncome = [300000, 700000, 1300000];
            var myTaxableIncome;
            beforeEach(function () {
                myTaxableIncome = new TaxIndiaEmp();
                myTaxableIncome.setIncome(grossTaxableIncome[index]);
            });
            afterEach(function () {
                index += 1;
            });

            //Scenario -4
            it('Should be duducted 10% if Gross Income is between RS 250,000/- AND RS 500,000/-', function () {
                //Let's assume the taxable income is RS 300,000/-
                expect(myTaxableIncome.calculateTDS()).toEqual(5000);
            });
            //Scenario -5
            it('Should be deducted 20% if Gross Income is between RS 500,000/- and RS 1,000,000/-', function () {
                //Let's assume the taxable income is RS 700,000/-
                expect(myTaxableIncome.calculateTDS()).toEqual(40000);
            });
            //Scenario -6
            it('Should be deducted 30% if Gross Income is >RS 10,000,00/-', function () {
                expect(myTaxableIncome.calculateTDS()).toEqual(90000);
            });
        });
        describe('Tax deducted for United Kingdom Employees', function () {

            xit('Should be deducted 20% if Gross Annual Income is between £10,000/- and £32,000', function () {

            });
        });
        describe('Tax deducted for United States Employees', function () {
            xit('Should be deducted 35% of Gross Taxable Income if employee status is SINGLE', function () {

            });
            xit('Should be deducted 30% of Gross Taxable Income if employee status is MARRIED', function () {

            });
        });
    });
});<|endoftext|>