<commit_before>/*global $, google, InfoBox */
var simulation_manager = (function(){
    var ua_is_mobile = navigator.userAgent.indexOf('iPhone') !== -1 || navigator.userAgent.indexOf('Android') !== -1;
    
    var config = (function(){
        var params = {
            center_start: new google.maps.LatLng(47.378, 8.540),
            zoom_start: 13,
            zoom_follow: 17,
            zoom_station: 15,
            zoom_mouseover_min: 7,
            ft_id_mask: '812706',
            ft_id_lines: '1497331',
            ft_id_stations: '1497361',
            json_paths: {
                edges: 'static/geojson/edges-sbb.json',
                stations: 'static/geojson/stations-sbb.json',
                vehicles: 'feed/vehicles/sbb/[hhmm]',
                station_vehicles: 'feed/station_vehicles/sbb/[station_id]/[hhmm]'
            }
        };
        
        var user_params = {};
        var url_parts = window.location.href.split('?');
        if (url_parts.length === 2) {
            var queryparam_groups = url_parts[1].split('&');
            $.each(queryparam_groups, function(index, queryparams_group){
                var queryparam_parts = queryparams_group.split('=');
                user_params[queryparam_parts[0]] = decodeURIComponent(queryparam_parts[1]);
            });
        }
        
        return {
            getParam: function(p) {
                return params[p];
            },
            getUserParam: function(p) {
                return typeof user_params[p] === 'undefined' ? null : user_params[p];
            }
        };
    })();
    
    var map = null;
    
    var simulation_vehicles = {};
    
    var listener_helpers = (function(){
        var listeners = {};
        
        function notify(type) {
            if (listeners[type] === 'undefined') {
                return;
            }
            
            $.each(listeners[type], function(i, fn){
                fn();
            });
        }

        function subscribe(type, fn) {
            if (typeof listeners[type] === 'undefined') {
                listeners[type] = [];
            }
            
            listeners[type].push(fn);
        }
        
        return {
            notify: notify,
            subscribe: subscribe
        };
    })();
    
    var stationsPool = (function(){
        var stations = {};
        
        function get(id) {
            return (typeof stations[id]) === 'undefined' ? '' : stations[id].get('name');
        }
        
        function location_get(id) {
            return (typeof stations[id]) === 'undefined' ? '' : stations[id].get('location');
        }
        
        function add(id, name, x, y) {
            var station = new google.maps.MVCObject();
            station.set('name', name);

            if (parseInt(x, 10) === 0) {
                station.set('location', null);
            } else {
                station.set('location', new google.maps.LatLng(parseFloat(y), parseFloat(x)));
            }
            
            stations[id] = station;
        }
        
        return {
            get: get,
            add: add,
            location_get: location_get
        };
    })();

    // Routes manager.
    // Roles:
    // - keep a reference for the routes between stations
    //      i.e. (Zürich HB-Bern, Zürich HB-Olten, Olten-Bern)
    //      Note: one route can contain one or more edges (the low-level entity in the simulation graph)
    // - interpolate position at given percent along a route
    var linesPool = (function() {
        var network_lines = {};
        var routes = {};
        var route_highlight = new google.maps.Polyline({
            path: [],
            strokeColor: "white",
            strokeOpacity: 0.8,
            strokeWeight: 3,
            map: null,
            icons: [{
                icon: {
                    path: 'M 0,-2 0,2',
                    strokeColor: 'black',
                    strokeOpacity: 1.0,          
                },
                repeat: '50px'
            }],
            timer: null
        });

        
        // TODO - that can be a nice feature request for google.maps.geometry lib
        function positionOnRouteAtPercentGet(ab_edges, perc) {
            var route = routes[ab_edges];
            
            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 routeAdd(ab_edges) {
            if (typeof routes[ab_edges] !== 'undefined') {
                return;
            }
            
            var edges = ab_edges.split(',');
            var routePoints = [];
            $.each(edges, function(k, edgeID) {
                if (edgeID.substr(0, 1) === '-') {
                    var points = network_lines[edgeID.substr(1)].slice().reverse();
                } else {
                    var points = network_lines[edgeID];
                }
                routePoints = routePoints.concat(points);
            });
            
            routes[ab_edges] = {
                'points': routePoints,
                'length': google.maps.geometry.spherical.computeLength(routePoints).toFixed(3)
            };
        }
        
        function lengthGet(ab_edges) {
            return routes[ab_edges].length;
        }
        
        function routeHighlight(vehicle) {
            var points = [];
            $.each(vehicle.edges, function(k, ab_edges){
                if (k === 0) { return; }
                points = points.concat(routes[ab_edges].points);
            });
            
            route_highlight.setPath(points);
            route_highlight.setMap(map);

            var icon_offset = 0;
            route_highlight.set('timer', setInterval(function(){
                if (icon_offset === 99) {
                    icon_offset = 0;
                } else {
                    icon_offset += 10;
                }

                var icons = route_highlight.get('icons');
                icons[0].offset = icon_offset + '%';
                route_highlight.set('icons', icons);
            }, 20));
        }
        
        function routeHighlightRemove() {
            route_highlight.setMap(null);
            clearInterval(route_highlight.get('timer'));
        }
        
        function loadEncodedEdges(edges) {
            $.each(edges, function(index, encoded_edge) {
                network_lines[index] = google.maps.geometry.encoding.decodePath(encoded_edge);
            });
        }
        
        function loadGeoJSONEdges(features) {
            $.each(features, function(index, feature) {
                var edge_coords = [];
                $.each(feature.geometry.coordinates, function(i2, feature_coord){
                    edge_coords.push(new google.maps.LatLng(feature_coord[1], feature_coord[0]));
                });
                
                var edge_id = feature.properties.edge_id;<commit_msg>Better route animation<commit_after>/*global $, google, InfoBox */
var simulation_manager = (function(){
    var ua_is_mobile = navigator.userAgent.indexOf('iPhone') !== -1 || navigator.userAgent.indexOf('Android') !== -1;
    
    var config = (function(){
        var params = {
            center_start: new google.maps.LatLng(47.378, 8.540),
            zoom_start: 13,
            zoom_follow: 17,
            zoom_station: 15,
            zoom_mouseover_min: 7,
            ft_id_mask: '812706',
            ft_id_lines: '1497331',
            ft_id_stations: '1497361',
            json_paths: {
                edges: 'static/geojson/edges-sbb.json',
                stations: 'static/geojson/stations-sbb.json',
                vehicles: 'feed/vehicles/sbb/[hhmm]',
                station_vehicles: 'feed/station_vehicles/sbb/[station_id]/[hhmm]'
            }
        };
        
        var user_params = {};
        var url_parts = window.location.href.split('?');
        if (url_parts.length === 2) {
            var queryparam_groups = url_parts[1].split('&');
            $.each(queryparam_groups, function(index, queryparams_group){
                var queryparam_parts = queryparams_group.split('=');
                user_params[queryparam_parts[0]] = decodeURIComponent(queryparam_parts[1]);
            });
        }
        
        return {
            getParam: function(p) {
                return params[p];
            },
            getUserParam: function(p) {
                return typeof user_params[p] === 'undefined' ? null : user_params[p];
            }
        };
    })();
    
    var map = null;
    
    var simulation_vehicles = {};
    
    var listener_helpers = (function(){
        var listeners = {};
        
        function notify(type) {
            if (listeners[type] === 'undefined') {
                return;
            }
            
            $.each(listeners[type], function(i, fn){
                fn();
            });
        }

        function subscribe(type, fn) {
            if (typeof listeners[type] === 'undefined') {
                listeners[type] = [];
            }
            
            listeners[type].push(fn);
        }
        
        return {
            notify: notify,
            subscribe: subscribe
        };
    })();
    
    var stationsPool = (function(){
        var stations = {};
        
        function get(id) {
            return (typeof stations[id]) === 'undefined' ? '' : stations[id].get('name');
        }
        
        function location_get(id) {
            return (typeof stations[id]) === 'undefined' ? '' : stations[id].get('location');
        }
        
        function add(id, name, x, y) {
            var station = new google.maps.MVCObject();
            station.set('name', name);

            if (parseInt(x, 10) === 0) {
                station.set('location', null);
            } else {
                station.set('location', new google.maps.LatLng(parseFloat(y), parseFloat(x)));
            }
            
            stations[id] = station;
        }
        
        return {
            get: get,
            add: add,
            location_get: location_get
        };
    })();

    // Routes manager.
    // Roles:
    // - keep a reference for the routes between stations
    //      i.e. (Zürich HB-Bern, Zürich HB-Olten, Olten-Bern)
    //      Note: one route can contain one or more edges (the low-level entity in the simulation graph)
    // - interpolate position at given percent along a route
    var linesPool = (function() {
        var network_lines = {};
        var routes = {};
        var route_highlight = new google.maps.Polyline({
            path: [],
            strokeColor: "white",
            strokeOpacity: 0.8,
            strokeWeight: 3,
            map: null,
            icons: [{
                icon: {
                    path: 'M 0,-2 0,2',
                    strokeColor: 'black',
                    strokeOpacity: 1.0,          
                },
                repeat: '40px'
            }],
            timer: null
        });

        
        // TODO - that can be a nice feature request for google.maps.geometry lib
        function positionOnRouteAtPercentGet(ab_edges, perc) {
            var route = routes[ab_edges];
            
            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 routeAdd(ab_edges) {
            if (typeof routes[ab_edges] !== 'undefined') {
                return;
            }
            
            var edges = ab_edges.split(',');
            var routePoints = [];
            $.each(edges, function(k, edgeID) {
                if (edgeID.substr(0, 1) === '-') {
                    var points = network_lines[edgeID.substr(1)].slice().reverse();
                } else {
                    var points = network_lines[edgeID];
                }
                routePoints = routePoints.concat(points);
            });
            
            routes[ab_edges] = {
                'points': routePoints,
                'length': google.maps.geometry.spherical.computeLength(routePoints).toFixed(3)
            };
        }
        
        function lengthGet(ab_edges) {
            return routes[ab_edges].length;
        }
        
        function routeHighlight(vehicle) {
            var points = [];
            $.each(vehicle.edges, function(k, ab_edges){
                if (k === 0) { return; }
                points = points.concat(routes[ab_edges].points);
            });
            
            route_highlight.setPath(points);
            route_highlight.setMap(map);

            var icon_offset = 0;
            route_highlight.set('timer', setInterval(function(){
                if (icon_offset > 39) {
                    icon_offset = 0;
                } else {
                    icon_offset += 2;
                }

                var icons = route_highlight.get('icons');
                icons[0].offset = icon_offset + 'px';
                route_highlight.set('icons', icons);
            }, 20));
        }
        
        function routeHighlightRemove() {
            route_highlight.setMap(null);
            clearInterval(route_highlight.get('timer'));
        }
        
        function loadEncodedEdges(edges) {
            $.each(edges, function(index, encoded_edge) {
                network_lines[index] = google.maps.geometry.encoding.decodePath(encoded_edge);
            });
        }
        
        function loadGeoJSONEdges(features) {
            $.each(features, function(index, feature) {
                var edge_coords = [];
                $.each(feature.geometry.coordinates, function(i2, feature_coord){
                    edge_coords.push(new google.maps.LatLng(feature_coord[1], feature_coord[0]));
                });
                
                var edge_id = feature.properties.edge_id;
{'fraction_non_alphanumeric': 0.1199675214831856, 'fraction_numerical': 0.011299817308342919, 'mean_word_length': 1.3493880146240662, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7083104', 'n_tokens_mistral': 4172, 'n_tokens_neox': 3914, 'n_words': 772}