<commit_before>// constants
var stopCircleColor = 'blue'; // map stop circle color
var stopFillColor = 'blue'; // map stop circle fill color
var setViewLat = 44.40;
var setViewLon = 26.10;
var setViewZoom = 13;
var mapid = 'mapid';

function getRefs(roleName, relation){
    var refList = [];
    var selector = '[role=' + roleName + ']';
    var objList = relation.querySelectorAll(selector);
    for(var p = 0; p < objList.length; p++){
        refList.push(objList[p].getAttribute("ref"));
    }
    return refList;
}

function offsetCoordinates(coords, rand){
    return [coords[0] + rand[0], coords[1] + rand[1]];
}

function getCoordinates(ref, xmlDoc){
    var selector = '[id="' + ref + '"]';
    var node = xmlDoc.querySelectorAll(selector)[0];
    // if ref (usually a platform) is a way or area instead of a node
    if (node.tagName === 'way'){
        var firstNodeRef = node.getElementsByTagName('nd')[0].getAttribute("ref");
        selector = '[id="' + firstNodeRef + '"]';
        node = xmlDoc.querySelectorAll(selector)[0];
    }
    var lat = parseFloat(node.getAttribute("lat"));
    var lon = parseFloat(node.getAttribute("lon"));
    return [lat, lon];
}

function getArrayCoordinates(array, xmlDoc, rand){
    var res = [];
    var temp;
    for(var i=0; i<array.length; i++){
        temp = getCoordinates(array[i], xmlDoc);
        res.push(offsetCoordinates(temp, rand));
    }
    return res;
}

function segmentReferences(waypointReferences, stopReferences){
    var left = 0;
    var right = 0;
    var started = false;
    var counter =  0;
    var segments = [];

    for(var p = 0; p < waypointReferences.length; p++){
        // try to separate segments between stops
        if(waypointReferences[p] === stopReferences[counter]){
            counter ++;
            if(started){
                right = p;
                segments.push(waypointReferences.slice(left, right +1));
                left = p; // starting new segment
            } else {
                left = p;
                started = true;
            }
        }
    }
    return segments;
}

function displayPlatforms(names, coordinates, stopsTimes){
    // display platforms
    var markers = [];
    var circle;
    for(var p = 0; p < names.length; p++){
        var stopTime = stopsTimes[p];
        if (stopTime === undefined){
            stopTime = 16;
        }
        circle = L.circle([coordinates[p][0], coordinates[p][1]], {
            color: stopCircleColor,
            weight: 2,
            fillColor: stopFillColor,
            fillOpacity: 0.1,
            radius: 15*Math.max(1.5, Math.sqrt(stopTime))
        }).bindPopup(names[p]);
    markers.push(circle);
    }
    return markers;
}

Number.prototype.toRad = function() {
   return this * Math.PI / 180;
};

function twoPointLength(lat1, lon1, lat2, lon2){
	// http://stackoverflow.com/questions/14560999/using-the-haversine-formula-in-javascript
	var R = 6371; // km
	//has a problem with the .toRad() method below.
	var x1 = lat2-lat1;
	var dLat = x1.toRad();
	var x2 = lon2-lon1;
	var dLon = x2.toRad();
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	                Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
	                Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	return R * c;
}

function segmentLength(segmentArray, xmlDoc){
	var segmLength = 0;
	var startPoint = [];
	var endPoint = [];
	for(var k = 0; k < segmentArray.length - 1; k++){
		startPoint = getCoordinates(segmentArray[k], xmlDoc);
		endPoint = getCoordinates(segmentArray[k+1], xmlDoc);
		segmLength += twoPointLength(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
	}
	return segmLength;
}

function getPointsNextWay(way_ref, xmlDoc){
	// get the points of the next way
    var next_selector = '[id="' + way_ref + '"]';
    var next_way = xmlDoc.querySelectorAll(next_selector)[0];
    var next_wayPoints = next_way.getElementsByTagName("nd");
    var nextPoints = [];
    for (var w = 0; w < next_wayPoints.length; w ++){
        var ref = next_wayPoints[w].getAttribute("ref");
        nextPoints.push(ref);
    }
    return nextPoints;
}

function joinWays(relation, stopReferences, xmlDoc){
    // get ways and display route
    var prev, found_it;
    var wayReferences = [];
    var waypointReferences = [];
    var ways = relation.querySelectorAll('[role=""]'); // the ways in the route have no role
    // get way refs
    for(p = 0; p < ways.length; p++){
        wayReferences.push(ways[p].getAttribute("ref"));
    }
    // for each way, get points and add to route
    for(var p = 0; p < wayReferences.length; p++){
        var selector = '[id="' + wayReferences[p] + '"]';
        var way = xmlDoc.querySelectorAll(selector)[0];
        var wayPoints = way.getElementsByTagName("nd");
        var currentPoints = [];
        for (var w = 0; w < wayPoints.length; w ++){
            var ref = wayPoints[w].getAttribute("ref");
            currentPoints.push(ref);
        }

        // test for roundabouts
        // assumptions:
        // no two circular roundabouts in a row;
        var round_select = '[v="roundabout"]';
        if(way.querySelectorAll(round_select).length > 0){

            //A. roundabout first segment
            if(p === 0) {
                //find stops in this first roundabout and add them to waypointReferences
                //there should be at least one and usually just that one
                for (s = 0; s < stopReferences.length; s++){
	                var should_break = true;
	                for (n = 0; n < currentPoints.length; n++){
	                    if (stopReferences[s] === currentPoints[n]){
	                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
                            should_break = false;
	                    }
	                }
	                // if  stop not found in  current iteration, then break loop
	                // as stops are ordered
	                if (should_break){
	                    break;
	                }
                }
	            nextPoints = getPointsNextWay(wayReferences[p+1], xmlDoc);
	            // find roundabout point in next segment and add it to waypointReferences
	            for (i = 0; i < currentPoints.length; i++) {
	                if (currentPoints[i] === nextPoints[0] ||
	                    currentPoints[i] === nextPoints[nextPoints.length-1]){
	                    waypointReferences = waypointReferences.concat([currentPoints[i]]);
	                    break;
	                }
	            }
	            continue;
            }

            //B. roundabout last segment
            if(p === wayReferences.length -1){
                //in this case, just add stops located in this last roundabout
                for (s = 0; s < stopReferences.length; s++){
	                for (n = 0; n < currentPoints.length; n++){
	                    if (stopReferences[s] === currentPoints[n]){
	                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
	                    }
	                }
                }
                continue;
            }

            //C. roundabout somewhere in the middle
            var nextPoints = getPointsNextWay(wayReferences[p+1], xmlDoc);
            // find roundabout point in the last segment
            prev = false; // true if found point connecting with previous segment
            found_it = false; // true to continue
            for (i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === waypointReferences[waypointReferences.length-1]) {
                    prev = true;
                    break;
                }
                if (currentPoints[i] === waypointReferences[0]){
                    prev = true;
                    //this can happen only after the first segment, I suppose
                    waypointReferences = waypointReferences.reverse();
                    break;
                }
            }

            // test if there are stops in the current roundabout O(n^2) :-|
            //and add them to waypointReferences
            for (var s = 0; s < stopReferences.length; s++){
                for (var n = 0; n < currentPoints.length; n++){
                    if (stopReferences[s] === currentPoints[n]){
                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
                    }
                }
            }

            // find roundabout point in next segment
            for (var i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === nextPoints[0] ||
                    currentPoints[i] === nextPoints[nextPoints.length-1]){
                    // only add point if previously the roundabout point in last segment was found
                    //otherwise segments might be unconnected
                    if (prev){
                        found_it = true;
                        waypointReferences = waypointReferences.concat([currentPoints[i]]);
                    }
                    break;
                }
            }
            if (found_it){
                continue;
            }
        }

        // first segment (non-roundabout)
        if(p === 0){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }

        // last route point === first point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[0]){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === last point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }
        // first route point === first point  of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[0]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // first route point === last point of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }

        console.log("ERROR: WAYS NOT CONNECTED!");
    }

    return waypointReferences;
}

function processRelation(relation, xmlDoc){
    var names = [];
    var stopsLengths = [];

    // get platform refs and stop position refs
    var middle_platformReferences = getRefs('platform', relation);
    var platform_entry_onlyReferences = getRefs('platform_entry_only', relation);
    var platform_exit_only_References = getRefs('platform_exit_only', relation);
    var platformReferences = platform_entry_onlyReferences.concat(
        middle_platformReferences, platform_exit_only_References);
    var middle_stopReferences = getRefs('stop', relation);
    var stop_entry_onlyReferences = getRefs('stop_entry_only', relation);
    var stop_exit_only_References = getRefs('stop_exit_only', relation);
    var stopReferences = stop_entry_onlyReferences.concat(
        middle_stopReferences, stop_exit_only_References);

    // join ways to create full route
    var waypointReferences = joinWays(relation, stopReferences, xmlDoc);
    // create separate route segments
    var segments = segmentReferences(waypointReferences, stopReferences);
    // calculate segment lengths and total distance
    var segment;
    for(var i = 0; i< segments.length; i++){
        segment = segmentLength(segments[i], xmlDoc);
        stopsLengths.push(segment);
    }
    // get platform names and coordinates
    var platformCoordinates = [];
    for(var p = 0; p < platformReferences.length; p++){
        platformCoordinates.push(getCoordinates(platformReferences[p], xmlDoc));
        var selector = '[id="' + platformReferences[p].toString() + '"]';
        var platform = xmlDoc.querySelectorAll(selector)[0];
        names.push(platform.querySelectorAll('[k="name"]')[0].getAttribute("v"));
    }
    return {
        names: names,
        segments: segments,
        platformCoordinates: platformCoordinates,
        stopsLengths: stopsLengths
    };
}

function addRouteToMap(rel, res, L, xmlDoc, randomize){
    // add route segments to map

    //slightly randomize position of routes to avoid complete overlap
    var rand_lat = 0;
    var rand_lon = 0;
    if(randomize){
        rand_lat = (2*Math.random()-1)/2000;
        rand_lon = (2*Math.random()-1)/2000;
    }

    var rand = [rand_lat, rand_lon];
    var lines = [];

    for(var i=0; i<rel.segments.length; i++){
        lines = lines.concat(L.polyline(getArrayCoordinates(rel.segments[i], xmlDoc, rand),
            {color: res.segmentColors[i], weight: 5}));
    }
    return lines;
}

function baseMap(L){
    var myMap = L.map(mapid).setView([setViewLat, setViewLon], setViewZoom);
    L.tileLayer('http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '© OpenStreetMap'
    }).addTo(myMap);
    // L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    //     attribution: '© OpenStreetMap contributors'
    // }).addTo(myMap);
    return myMap;
}

function addMultipleRoutes(relList, relColors, randomize){
	var myMap = baseMap(L);
    var xhttp, rel, res, lengths;
    var platforms = [];
    var lines = [];
    var relations = 'http://overpass-api.de/api/interpreter?data=';

	for(var i=0; i<relList.length; i++){
        relations += 'relation(';
        relations += relList[i];
        relations += ');out body;>;out body;';
    }
	xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            var xmlDoc = this.responseXML;
            var downloadedRelations = xmlDoc.getElementsByTagName("relation");
            for(var j=0; j<downloadedRelations.length; j++){
                rel = processRelation(downloadedRelations[j], xmlDoc);
                lengths = rel.names.length;
	            res = {};
	            res.segmentColors = new Array(lengths).fill(relColors[j]);
	            res.stopsTimes = new Array(lengths).fill(30);
	            lines = lines.concat(addRouteToMap(rel, res, L, xmlDoc, myMap, randomize));
                platforms = platforms.concat(displayPlatforms(rel.names, rel.platformCoordinates, res.stopsTimes, myMap));
            }
            var linesLayer = new L.featureGroup(lines).addTo(myMap);
            var platformsLayer = new L.featureGroup(platforms).addTo(myMap);
		}
    };
    xhttp.open("GET", relations, true);
    xhttp.send();
}
<commit_msg>IE compatibility<commit_after>// constants
var stopCircleColor = 'blue'; // map stop circle color
var stopFillColor = 'blue'; // map stop circle fill color
var setViewLat = 44.40;
var setViewLon = 26.10;
var setViewZoom = 13;
var mapid = 'mapid';

function getRefs(roleName, relation){
    var refList = [];
    var selector = '[role=' + roleName + ']';
    var objList = relation.querySelectorAll(selector);
    for(var p = 0; p < objList.length; p++){
        refList.push(objList[p].getAttribute("ref"));
    }
    return refList;
}

function offsetCoordinates(coords, rand){
    return [coords[0] + rand[0], coords[1] + rand[1]];
}

function getCoordinates(ref, xmlDoc){
    var selector = '[id="' + ref + '"]';
    var node = xmlDoc.querySelectorAll(selector)[0];
    // if ref (usually a platform) is a way or area instead of a node
    if (node.tagName === 'way'){
        var firstNodeRef = node.getElementsByTagName('nd')[0].getAttribute("ref");
        selector = '[id="' + firstNodeRef + '"]';
        node = xmlDoc.querySelectorAll(selector)[0];
    }
    var lat = parseFloat(node.getAttribute("lat"));
    var lon = parseFloat(node.getAttribute("lon"));
    return [lat, lon];
}

function getArrayCoordinates(array, xmlDoc, rand){
    var res = [];
    var temp;
    for(var i=0; i<array.length; i++){
        temp = getCoordinates(array[i], xmlDoc);
        res.push(offsetCoordinates(temp, rand));
    }
    return res;
}

function segmentReferences(waypointReferences, stopReferences){
    var left = 0;
    var right = 0;
    var started = false;
    var counter =  0;
    var segments = [];

    for(var p = 0; p < waypointReferences.length; p++){
        // try to separate segments between stops
        if(waypointReferences[p] === stopReferences[counter]){
            counter ++;
            if(started){
                right = p;
                segments.push(waypointReferences.slice(left, right +1));
                left = p; // starting new segment
            } else {
                left = p;
                started = true;
            }
        }
    }
    return segments;
}

function displayPlatforms(names, coordinates, stopsTimes){
    // display platforms
    var markers = [];
    var circle;
    for(var p = 0; p < names.length; p++){
        var stopTime = stopsTimes[p];
        if (stopTime === undefined){
            stopTime = 16;
        }
        circle = L.circle([coordinates[p][0], coordinates[p][1]], {
            color: stopCircleColor,
            weight: 2,
            fillColor: stopFillColor,
            fillOpacity: 0.1,
            radius: 15*Math.max(1.5, Math.sqrt(stopTime))
        }).bindPopup(names[p]);
    markers.push(circle);
    }
    return markers;
}

Number.prototype.toRad = function() {
   return this * Math.PI / 180;
};

function twoPointLength(lat1, lon1, lat2, lon2){
	// http://stackoverflow.com/questions/14560999/using-the-haversine-formula-in-javascript
	var R = 6371; // km
	//has a problem with the .toRad() method below.
	var x1 = lat2-lat1;
	var dLat = x1.toRad();
	var x2 = lon2-lon1;
	var dLon = x2.toRad();
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	                Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
	                Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	return R * c;
}

function segmentLength(segmentArray, xmlDoc){
	var segmLength = 0;
	var startPoint = [];
	var endPoint = [];
	for(var k = 0; k < segmentArray.length - 1; k++){
		startPoint = getCoordinates(segmentArray[k], xmlDoc);
		endPoint = getCoordinates(segmentArray[k+1], xmlDoc);
		segmLength += twoPointLength(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
	}
	return segmLength;
}

function getPointsNextWay(way_ref, xmlDoc){
	// get the points of the next way
    var next_selector = '[id="' + way_ref + '"]';
    var next_way = xmlDoc.querySelectorAll(next_selector)[0];
    var next_wayPoints = next_way.getElementsByTagName("nd");
    var nextPoints = [];
    for (var w = 0; w < next_wayPoints.length; w ++){
        var ref = next_wayPoints[w].getAttribute("ref");
        nextPoints.push(ref);
    }
    return nextPoints;
}

function joinWays(relation, stopReferences, xmlDoc){
    // get ways and display route
    var prev, found_it;
    var wayReferences = [];
    var waypointReferences = [];
    var ways = relation.querySelectorAll('[role=""]'); // the ways in the route have no role
    // get way refs
    for(p = 0; p < ways.length; p++){
        wayReferences.push(ways[p].getAttribute("ref"));
    }
    // for each way, get points and add to route
    for(var p = 0; p < wayReferences.length; p++){
        var selector = '[id="' + wayReferences[p] + '"]';
        var way = xmlDoc.querySelectorAll(selector)[0];
        var wayPoints = way.getElementsByTagName("nd");
        var currentPoints = [];
        for (var w = 0; w < wayPoints.length; w ++){
            var ref = wayPoints[w].getAttribute("ref");
            currentPoints.push(ref);
        }

        // test for roundabouts
        // assumptions:
        // no two circular roundabouts in a row;
        var round_select = '[v="roundabout"]';
        if(way.querySelectorAll(round_select).length > 0){

            //A. roundabout first segment
            if(p === 0) {
                //find stops in this first roundabout and add them to waypointReferences
                //there should be at least one and usually just that one
                for (s = 0; s < stopReferences.length; s++){
	                var should_break = true;
	                for (n = 0; n < currentPoints.length; n++){
	                    if (stopReferences[s] === currentPoints[n]){
	                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
                            should_break = false;
	                    }
	                }
	                // if  stop not found in  current iteration, then break loop
	                // as stops are ordered
	                if (should_break){
	                    break;
	                }
                }
	            nextPoints = getPointsNextWay(wayReferences[p+1], xmlDoc);
	            // find roundabout point in next segment and add it to waypointReferences
	            for (i = 0; i < currentPoints.length; i++) {
	                if (currentPoints[i] === nextPoints[0] ||
	                    currentPoints[i] === nextPoints[nextPoints.length-1]){
	                    waypointReferences = waypointReferences.concat([currentPoints[i]]);
	                    break;
	                }
	            }
	            continue;
            }

            //B. roundabout last segment
            if(p === wayReferences.length -1){
                //in this case, just add stops located in this last roundabout
                for (s = 0; s < stopReferences.length; s++){
	                for (n = 0; n < currentPoints.length; n++){
	                    if (stopReferences[s] === currentPoints[n]){
	                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
	                    }
	                }
                }
                continue;
            }

            //C. roundabout somewhere in the middle
            var nextPoints = getPointsNextWay(wayReferences[p+1], xmlDoc);
            // find roundabout point in the last segment
            prev = false; // true if found point connecting with previous segment
            found_it = false; // true to continue
            for (i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === waypointReferences[waypointReferences.length-1]) {
                    prev = true;
                    break;
                }
                if (currentPoints[i] === waypointReferences[0]){
                    prev = true;
                    //this can happen only after the first segment, I suppose
                    waypointReferences = waypointReferences.reverse();
                    break;
                }
            }

            // test if there are stops in the current roundabout O(n^2) :-|
            //and add them to waypointReferences
            for (var s = 0; s < stopReferences.length; s++){
                for (var n = 0; n < currentPoints.length; n++){
                    if (stopReferences[s] === currentPoints[n]){
                        waypointReferences = waypointReferences.concat([currentPoints[n]]);
                    }
                }
            }

            // find roundabout point in next segment
            for (var i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === nextPoints[0] ||
                    currentPoints[i] === nextPoints[nextPoints.length-1]){
                    // only add point if previously the roundabout point in last segment was found
                    //otherwise segments might be unconnected
                    if (prev){
                        found_it = true;
                        waypointReferences = waypointReferences.concat([currentPoints[i]]);
                    }
                    break;
                }
            }
            if (found_it){
                continue;
            }
        }

        // first segment (non-roundabout)
        if(p === 0){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }

        // last route point === first point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[0]){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === last point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }
        // first route point === first point  of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[0]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // first route point === last point of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }

        console.log("ERROR: WAYS NOT CONNECTED!");
    }

    return waypointReferences;
}

function processRelation(relation, xmlDoc){
    var names = [];
    var stopsLengths = [];

    // get platform refs and stop position refs
    var middle_platformReferences = getRefs('platform', relation);
    var platform_entry_onlyReferences = getRefs('platform_entry_only', relation);
    var platform_exit_only_References = getRefs('platform_exit_only', relation);
    var platformReferences = platform_entry_onlyReferences.concat(
        middle_platformReferences, platform_exit_only_References);
    var middle_stopReferences = getRefs('stop', relation);
    var stop_entry_onlyReferences = getRefs('stop_entry_only', relation);
    var stop_exit_only_References = getRefs('stop_exit_only', relation);
    var stopReferences = stop_entry_onlyReferences.concat(
        middle_stopReferences, stop_exit_only_References);

    // join ways to create full route
    var waypointReferences = joinWays(relation, stopReferences, xmlDoc);
    // create separate route segments
    var segments = segmentReferences(waypointReferences, stopReferences);
    // calculate segment lengths and total distance
    var segment;
    for(var i = 0; i< segments.length; i++){
        segment = segmentLength(segments[i], xmlDoc);
        stopsLengths.push(segment);
    }
    // get platform names and coordinates
    var platformCoordinates = [];
    for(var p = 0; p < platformReferences.length; p++){
        platformCoordinates.push(getCoordinates(platformReferences[p], xmlDoc));
        var selector = '[id="' + platformReferences[p].toString() + '"]';
        var platform = xmlDoc.querySelectorAll(selector)[0];
        names.push(platform.querySelectorAll('[k="name"]')[0].getAttribute("v"));
    }
    return {
        names: names,
        segments: segments,
        platformCoordinates: platformCoordinates,
        stopsLengths: stopsLengths
    };
}

function addRouteToMap(rel, res, L, xmlDoc, randomize){
    // add route segments to map

    //slightly randomize position of routes to avoid complete overlap
    var rand_lat = 0;
    var rand_lon = 0;
    if(randomize){
        rand_lat = (2*Math.random()-1)/2000;
        rand_lon = (2*Math.random()-1)/2000;
    }

    var rand = [rand_lat, rand_lon];
    var lines = [];

    for(var i=0; i<rel.segments.length; i++){
        lines = lines.concat(L.polyline(getArrayCoordinates(rel.segments[i], xmlDoc, rand),
            {color: res.segmentColors[i], weight: 5}));
    }
    return lines;
}

function baseMap(L){
    var myMap = L.map(mapid).setView([setViewLat, setViewLon], setViewZoom);
    L.tileLayer('http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '© OpenStreetMap'
    }).addTo(myMap);
    // L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    //     attribution: '© OpenStreetMap contributors'
    // }).addTo(myMap);
    return myMap;
}

function addMultipleRoutes(relList, relColors, randomize){
	var myMap = baseMap(L);
    var xhttp, rel, res, lengths;
    var platforms = [];
    var lines = [];
    var relations = 'http://overpass-api.de/api/interpreter?data=';

	for(var i=0; i<relList.length; i++){
        relations += 'relation(';
        relations += relList[i];
        relations += ');out body;>;out body;';
    }
	xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            var xmlDoc = this.responseXML;
            var downloadedRelations = xmlDoc.getElementsByTagName("relation");
            for(var j=0; j<downloadedRelations.length; j++){
                rel = processRelation(downloadedRelations[j], xmlDoc);
                lengths = rel.names.length;
	            res = {};
                res.segmentColors = new Array(lengths);
                res.stopsTimes = new Array(lengths);
                for(var i = 0; i<lengths; i++){
                    res.segmentColors[i]=relColors[j];
                    res.stopsTimes=30;
                }
	            lines = lines.concat(addRouteToMap(rel, res, L, xmlDoc, myMap, randomize));
                platforms = platforms.concat(displayPlatforms(rel.names, rel.platformCoordinates, res.stopsTimes, myMap));
            }
            var linesLayer = new L.featureGroup(lines).addTo(myMap);
            var platformsLayer = new L.featureGroup(platforms).addTo(myMap);
		}
    };
    xhttp.open("GET", relations, true);
    xhttp.send();
}
<|endoftext|>


<commit_before>var ConfigParser = require('../../lib/configParser');
var path = require('path');

describe('the config parser', function() {

  it('should have a default config', function() {
    var config = new ConfigParser().getConfig();
    expect(config.specs).toEqual([]);
    expect(config.rootElement).toEqual('body');
  });

  it('should merge in config from an object', function() {
    var toAdd = {
      rootElement: '.mydiv'
    };
    var config = new ConfigParser().addConfig(toAdd).getConfig();
    expect(config.specs).toEqual([]);
    expect(config.rootElement).toEqual('.mydiv');
  });

  it('should merge in config from a file', function() {
    var config = new ConfigParser().
        addFileConfig(__dirname + '/data/config.js').
        getConfig();

    expect(config.rootElement).toEqual('.mycontainer');
    expect(config.onPrepare.indexOf(path.normalize('/spec/unit/data/foo/bar.js'))).not.toEqual(-1);
    expect(config.specs.length).toEqual(1);
    expect(config.specs[0]).toEqual('fakespec[AB].js');
  });

  it('should keep filepaths relative to the cwd when merging', function() {
    var toAdd = {
      onPrepare: 'baz/qux.js'
    };

    var config = new ConfigParser().addConfig(toAdd).getConfig();

    expect(config.onPrepare).toEqual(path.normalize(process.cwd() + '/baz/qux.js'));
  });

  describe('getSpecs()', function() {
    it('should return all the specs from "config.suites" if no other sources are provided', function() {
      var config = {
        specs: [],
        suites: {
          foo: 'foo.spec.js',
          bar: 'bar.spec.js'
        }
      };

      var specs = new ConfigParser.getSpecs(config);

      expect(specs).toEqual(['foo.spec.js', 'bar.spec.js']);
    });
  });

  describe('resolving globs', function() {
    it('should resolve relative to the cwd', function() {
      spyOn(process, 'cwd').and.returnValue(__dirname + '/');
      var toAdd = {
        specs: 'data/*spec[AB].js'
      };
      var config = new ConfigParser().addConfig(toAdd).getConfig();
      var specs = ConfigParser.resolveFilePatterns(config.specs);
      expect(specs.length).toEqual(2);
      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);
      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);
    });

    it('should resolve relative to the config file dir', function() {
      var config = new ConfigParser().
          addFileConfig(__dirname + '/data/config.js').
          getConfig();
      var specs = ConfigParser.resolveFilePatterns(
          config.specs, false, config.configDir);
      expect(specs.length).toEqual(2);
      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);
      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);
    });
  });
});
<commit_msg>test(configParser): removed unneeded "new" operator<commit_after>var ConfigParser = require('../../lib/configParser');
var path = require('path');

describe('the config parser', function() {

  it('should have a default config', function() {
    var config = new ConfigParser().getConfig();
    expect(config.specs).toEqual([]);
    expect(config.rootElement).toEqual('body');
  });

  it('should merge in config from an object', function() {
    var toAdd = {
      rootElement: '.mydiv'
    };
    var config = new ConfigParser().addConfig(toAdd).getConfig();
    expect(config.specs).toEqual([]);
    expect(config.rootElement).toEqual('.mydiv');
  });

  it('should merge in config from a file', function() {
    var config = new ConfigParser().
        addFileConfig(__dirname + '/data/config.js').
        getConfig();

    expect(config.rootElement).toEqual('.mycontainer');
    expect(config.onPrepare.indexOf(path.normalize('/spec/unit/data/foo/bar.js'))).not.toEqual(-1);
    expect(config.specs.length).toEqual(1);
    expect(config.specs[0]).toEqual('fakespec[AB].js');
  });

  it('should keep filepaths relative to the cwd when merging', function() {
    var toAdd = {
      onPrepare: 'baz/qux.js'
    };

    var config = new ConfigParser().addConfig(toAdd).getConfig();

    expect(config.onPrepare).toEqual(path.normalize(process.cwd() + '/baz/qux.js'));
  });

  describe('getSpecs()', function() {
    it('should return all the specs from "config.suites" if no other sources are provided', function() {
      var config = {
        specs: [],
        suites: {
          foo: 'foo.spec.js',
          bar: 'bar.spec.js'
        }
      };

      var specs = ConfigParser.getSpecs(config);

      expect(specs).toEqual(['foo.spec.js', 'bar.spec.js']);
    });
  });

  describe('resolving globs', function() {
    it('should resolve relative to the cwd', function() {
      spyOn(process, 'cwd').and.returnValue(__dirname + '/');
      var toAdd = {
        specs: 'data/*spec[AB].js'
      };
      var config = new ConfigParser().addConfig(toAdd).getConfig();
      var specs = ConfigParser.resolveFilePatterns(config.specs);
      expect(specs.length).toEqual(2);
      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);
      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);
    });

    it('should resolve relative to the config file dir', function() {
      var config = new ConfigParser().
          addFileConfig(__dirname + '/data/config.js').
          getConfig();
      var specs = ConfigParser.resolveFilePatterns(
          config.specs, false, config.configDir);
      expect(specs.length).toEqual(2);
      expect(specs[0].indexOf(path.normalize('unit/data/fakespecA.js'))).not.toEqual(-1);
      expect(specs[1].indexOf(path.normalize('unit/data/fakespecB.js'))).not.toEqual(-1);
    });
  });
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component, Fragment } from 'react'
import Button from 'components/Button'
import { Color } from 'constants/css'
import Loading from 'components/Loading'
import Message from './Message'
import SubjectHeader from './SubjectHeader'
import ConfirmModal from 'components/Modals/ConfirmModal'
import { connect } from 'react-redux'
import { deleteMessage } from 'redux/actions/ChatActions'
import SubjectMsgsModal from '../Modals/SubjectMsgsModal'
import { css } from 'emotion'

const scrollIsAtTheBottom = (content, container) => {
  return content.offsetHeight <= container.offsetHeight + container.scrollTop
}

class MessagesContainer extends Component {
  static propTypes = {
    className: PropTypes.string.isRequired,
    userId: PropTypes.number.isRequired,
    currentChannelId: PropTypes.number.isRequired,
    loadMoreButton: PropTypes.bool,
    messages: PropTypes.array,
    loadMoreMessages: PropTypes.func,
    loading: PropTypes.bool
  }

  state = {
    deleteModal: {
      shown: false,
      messageId: null
    },
    fillerHeight: 20,
    maxScroll: 0,
    newUnseenMessage: false,
    loadMoreButtonLock: false,
    subjectMsgsModal: {
      shown: false,
      subjectId: null,
      content: ''
    }
  }

  componentDidMount() {
    this.setScrollToBottom()
    this.setState(
      {
        fillerHeight:
          ((this.messagesContainer || {}).offsetHeight || 0) >
          ((this.Messages || {}).offsetHeight || 0)
            ? ((this.messagesContainer || {}).offsetHeight || 0) -
              ((this.Messages || {}).offsetHeight || 0)
            : 0
      },
      () => this.setScrollToBottom()
    )
    setTimeout(() => this.setScrollToBottom(), 300)
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    return scrollIsAtTheBottom(this.content, this.messagesContainer)
  }

  componentDidUpdate(prevProps, prevState, scrollAtBottom) {
    const { messages, userId } = this.props
    const switchedChannel =
      prevProps.currentChannelId !== this.props.currentChannelId
    const newMessageArrived =
      prevProps.messages.length >= 0 &&
      prevProps.messages.length < this.props.messages.length &&
      (prevProps.messages[0]
        ? prevProps.messages[0].id === this.props.messages[0].id
        : false)
    const messageDeleted =
      prevProps.currentChannelId === this.props.currentChannelId &&
      prevProps.messages.length > this.props.messages.length
    if (switchedChannel) {
      this.setState({
        fillerHeight:
          ((this.messagesContainer || {}).offsetHeight || 0) >
          ((this.Messages || {}).offsetHeight || 0)
            ? ((this.messagesContainer || {}).offsetHeight || 0) -
              ((this.Messages || {}).offsetHeight || 0)
            : 0
      })
      this.setScrollToBottom()
      return setTimeout(() => this.setScrollToBottom(), 300)
    }
    if (messageDeleted) {
      this.setState({
        fillerHeight:
          this.messagesContainer.offsetHeight > this.Messages.offsetHeight
            ? this.messagesContainer.offsetHeight - this.Messages.offsetHeight
            : 0
      })
      return
    }
    if (newMessageArrived) {
      const messageSenderId = messages[messages.length - 1].userId
      if (messageSenderId !== userId && !scrollAtBottom) {
        this.setState({ newUnseenMessage: true })
      } else {
        this.setState({
          fillerHeight:
            this.messagesContainer.offsetHeight > this.Messages.offsetHeight
              ? this.messagesContainer.offsetHeight - this.Messages.offsetHeight
              : 0
        })
        this.setScrollToBottom()
      }
    }
  }

  setScrollToBottom() {
    this.messagesContainer.scrollTop = Math.max(
      this.state.maxScroll,
      (this.messagesContainer || {}).offsetHeight || 0,
      this.state.fillerHeight + ((this.messages || {}).offsetHeight || 0)
    )
    this.setState({ maxScroll: this.messagesContainer.scrollTop })
  }

  render() {
    const { className, loadMoreButton, loading, currentChannelId } = this.props
    const {
      deleteModal,
      loadMoreButtonLock,
      newUnseenMessage,
      subjectMsgsModal
    } = this.state
    return (
      <Fragment>
        {subjectMsgsModal.shown && (
          <SubjectMsgsModal
            subjectId={subjectMsgsModal.subjectId}
            subjectTitle={subjectMsgsModal.content}
            onHide={() =>
              this.setState({
                subjectMsgsModal: { shown: false, subjectId: null, content: '' }
              })
            }
          />
        )}

          {loading && <Loading absolute />}

 {
              this.messagesContainer = ref
            }}
            className={css`
              position: absolute;
              left: 0;
              right: 0;
              bottom: 0;
            `}
            style={{
              opacity: loading && '0.3',
              top: currentChannelId === 2 ? '6rem' : 0,
              overflowY: 'scroll'
            }}
            onScroll={() => {
              const content = this.content
              const container = this.messagesContainer
              if (scrollIsAtTheBottom(content, container)) {
                this.setState({ newUnseenMessage: false })
              }
            }}
          >

 {
                this.content = ref
              }}
            >
              {loadMoreButton ? (

                  <Button
                    filled
                    info
                    style={{ marginTop: '1rem' }}
                    disabled={loadMoreButtonLock}
                    onClick={this.onLoadMoreButtonClick}
                  >
                    Load More
                  </Button>

              ) : (

              )}

 {
                  this.Messages = ref
                }}
              >
                {this.renderMessages()}

          {!loading && currentChannelId === 2 && <SubjectHeader />}

            {newUnseenMessage && (
              <Button
                filled
                warning
                onClick={() => {
                  this.setState({ newUnseenMessage: false })
                  const content = this.content
                  const container = this.messagesContainer
                  container.scrollTop = Math.max(
                    container.offsetHeight,
                    content.offsetHeight
                  )
                }}
              >
                New Message
              </Button>
            )}

          {deleteModal.shown && (
            <ConfirmModal
              onHide={() =>
                this.setState({
                  deleteModal: { shown: false, messageId: null }
                })
              }
              title="Remove Message"
              onConfirm={this.onDelete}
            />
          )}

      </Fragment>
    )
  }

  onDelete = async() => {
    const { deleteMessage } = this.props
    const { messageId } = this.state.deleteModal
    try {
      await deleteMessage(messageId)
      this.setState({ deleteModal: { shown: false, messageId: null } })
    } catch (error) {
      console.error(error)
    }
  }

  onLoadMoreButtonClick = async() => {
    const messageId = this.props.messages[0].id
    const channelId = this.props.messages[0].channelId
    const { userId, loadMoreMessages } = this.props
    const { loadMoreButtonLock } = this.state
    if (!loadMoreButtonLock) {
      this.setState({ loadMoreButtonLock: true })
      await loadMoreMessages(userId, messageId, channelId)
      this.setState({ loadMoreButtonLock: false })
    }
  }

  renderMessages = () => {
    const { messages } = this.props
    return messages.map((message, index) => {
      let { isNotification } = message
      let messageStyle = isNotification ? { color: Color.gray() } : null
      return (
        <Message
          key={index}
          onDelete={messageId =>
            this.setState({
              deleteModal: {
                shown: true,
                messageId
              }
            })
          }
          index={index}
          style={messageStyle}
          message={message}
          isLastMsg={index === messages.length - 1}
          setScrollToBottom={this.setScrollToBottom.bind(this)}
          showSubjectMsgsModal={({ subjectId, content }) =>
            this.setState({
              subjectMsgsModal: { shown: true, subjectId, content }
            })
          }
        />
      )
    })
  }
}

export default connect(
  null,
  { deleteMessage }
)(MessagesContainer)
<commit_msg>Update index.js<commit_after>import PropTypes from 'prop-types'
import React, { Component, Fragment } from 'react'
import Button from 'components/Button'
import { Color } from 'constants/css'
import Loading from 'components/Loading'
import Message from './Message'
import SubjectHeader from './SubjectHeader'
import ConfirmModal from 'components/Modals/ConfirmModal'
import { connect } from 'react-redux'
import { deleteMessage } from 'redux/actions/ChatActions'
import SubjectMsgsModal from '../Modals/SubjectMsgsModal'
import { css } from 'emotion'

const scrollIsAtTheBottom = (content, container) => {
  return content.offsetHeight <= container.offsetHeight + container.scrollTop
}

class MessagesContainer extends Component {
  static propTypes = {
    className: PropTypes.string.isRequired,
    userId: PropTypes.number.isRequired,
    currentChannelId: PropTypes.number.isRequired,
    loadMoreButton: PropTypes.bool,
    messages: PropTypes.array,
    loadMoreMessages: PropTypes.func,
    loading: PropTypes.bool
  }

  state = {
    deleteModal: {
      shown: false,
      messageId: null
    },
    fillerHeight: 20,
    maxScroll: 0,
    newUnseenMessage: false,
    loadMoreButtonLock: false,
    subjectMsgsModal: {
      shown: false,
      subjectId: null,
      content: ''
    }
  }

  componentDidMount() {
    this.setScrollToBottom()
    this.setState(
      {
        fillerHeight:
          this.messagesContainer.offsetHeight > this.messages.offsetHeight
            ? this.messagesContainer.offsetHeight - this.messages.offsetHeight
            : 0
      },
      () => this.setScrollToBottom()
    )
    setTimeout(() => this.setScrollToBottom(), 300)
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    return scrollIsAtTheBottom(this.content, this.messagesContainer)
  }

  componentDidUpdate(prevProps, prevState, scrollAtBottom) {
    const { messages, userId } = this.props
    const switchedChannel =
      prevProps.currentChannelId !== this.props.currentChannelId
    const newMessageArrived =
      prevProps.messages.length >= 0 &&
      prevProps.messages.length < this.props.messages.length &&
      (prevProps.messages[0]
        ? prevProps.messages[0].id === this.props.messages[0].id
        : false)
    const messageDeleted =
      prevProps.currentChannelId === this.props.currentChannelId &&
      prevProps.messages.length > this.props.messages.length
    if (switchedChannel) {
      this.setState({
        fillerHeight:
          this.messagesContainer.offsetHeight > this.messages.offsetHeight
            ? this.messagesContainer.offsetHeight - this.messages.offsetHeight
            : 0
      })
      this.setScrollToBottom()
      return setTimeout(() => this.setScrollToBottom(), 300)
    }
    if (messageDeleted) {
      this.setState({
        fillerHeight:
          this.messagesContainer.offsetHeight > this.messages.offsetHeight
            ? this.messagesContainer.offsetHeight - this.messages.offsetHeight
            : 0
      })
      return
    }
    if (newMessageArrived) {
      const messageSenderId = messages[messages.length - 1].userId
      if (messageSenderId !== userId && !scrollAtBottom) {
        this.setState({ newUnseenMessage: true })
      } else {
        this.setState({
          fillerHeight:
            this.messagesContainer.offsetHeight > this.messages.offsetHeight
              ? this.messagesContainer.offsetHeight - this.messages.offsetHeight
              : 0
        })
        this.setScrollToBottom()
      }
    }
  }

  setScrollToBottom() {
    this.messagesContainer.scrollTop = Math.max(
      this.state.maxScroll,
      this.messagesContainer.offsetHeight,
      this.state.fillerHeight + this.messages.offsetHeight
    )
    this.setState({ maxScroll: this.messagesContainer.scrollTop })
  }

  render() {
    const { className, loadMoreButton, loading, currentChannelId } = this.props
    const {
      deleteModal,
      loadMoreButtonLock,
      newUnseenMessage,
      subjectMsgsModal
    } = this.state
    return (
      <Fragment>
        {subjectMsgsModal.shown && (
          <SubjectMsgsModal
            subjectId={subjectMsgsModal.subjectId}
            subjectTitle={subjectMsgsModal.content}
            onHide={() =>
              this.setState({
                subjectMsgsModal: { shown: false, subjectId: null, content: '' }
              })
            }
          />
        )}

          {loading && <Loading absolute />}

 {
              this.messagesContainer = ref
            }}
            className={css`
              position: absolute;
              left: 0;
              right: 0;
              bottom: 0;
            `}
            style={{
              opacity: loading && '0.3',
              top: currentChannelId === 2 ? '6rem' : 0,
              overflowY: 'scroll'
            }}
            onScroll={() => {
              const content = this.content
              const container = this.messagesContainer
              if (scrollIsAtTheBottom(content, container)) {
                this.setState({ newUnseenMessage: false })
              }
            }}
          >

 {
                this.content = ref
              }}
            >
              {loadMoreButton ? (

                  <Button
                    filled
                    info
                    style={{ marginTop: '1rem' }}
                    disabled={loadMoreButtonLock}
                    onClick={this.onLoadMoreButtonClick}
                  >
                    Load More
                  </Button>

              ) : (

              )}

 {
                  this.messages = ref
                }}
              >
                {this.renderMessages()}

          {!loading && currentChannelId === 2 && <SubjectHeader />}

            {newUnseenMessage && (
              <Button
                filled
                warning
                onClick={() => {
                  this.setState({ newUnseenMessage: false })
                  const content = this.content
                  const container = this.messagesContainer
                  container.scrollTop = Math.max(
                    container.offsetHeight,
                    content.offsetHeight
                  )
                }}
              >
                New Message
              </Button>
            )}

          {deleteModal.shown && (
            <ConfirmModal
              onHide={() =>
                this.setState({
                  deleteModal: { shown: false, messageId: null }
                })
              }
              title="Remove Message"
              onConfirm={this.onDelete}
            />
          )}

      </Fragment>
    )
  }

  onDelete = async() => {
    const { deleteMessage } = this.props
    const { messageId } = this.state.deleteModal
    try {
      await deleteMessage(messageId)
      this.setState({ deleteModal: { shown: false, messageId: null } })
    } catch (error) {
      console.error(error)
    }
  }

  onLoadMoreButtonClick = async() => {
    const messageId = this.props.messages[0].id
    const channelId = this.props.messages[0].channelId
    const { userId, loadMoreMessages } = this.props
    const { loadMoreButtonLock } = this.state
    if (!loadMoreButtonLock) {
      this.setState({ loadMoreButtonLock: true })
      await loadMoreMessages(userId, messageId, channelId)
      this.setState({ loadMoreButtonLock: false })
    }
  }

  renderMessages = () => {
    const { messages } = this.props
    return messages.map((message, index) => {
      let { isNotification } = message
      let messageStyle = isNotification ? { color: Color.gray() } : null
      return (
        <Message
          key={index}
          onDelete={messageId =>
            this.setState({
              deleteModal: {
                shown: true,
                messageId
              }
            })
          }
          index={index}
          style={messageStyle}
          message={message}
          isLastMsg={index === messages.length - 1}
          setScrollToBottom={this.setScrollToBottom.bind(this)}
          showSubjectMsgsModal={({ subjectId, content }) =>
            this.setState({
              subjectMsgsModal: { shown: true, subjectId, content }
            })
          }
        />
      )
    })
  }
}

export default connect(
  null,
  { deleteMessage }
)(MessagesContainer)
<|endoftext|>


<commit_before>var logger = require('../config/logger').mainLogger

module.exports.calculateScore = function (run) {
  var score = 0

  if (run.showedUp !== undefined && run.showedUp) {
    score += 3
  }

  var dropTileIndexes = []

  for (var i in run.tiles) {
    var tile = run.tiles[i]

    if (tile.scoredItems.dropTiles !== undefined) {
      if (tile.scoredItems.dropTiles.length > 0) {
        dropTileIndexes.concat(tile.index)
      }
    }
  }

  dropTileIndexes.sort(function (a, b) {
    return a - b
  })

  for (var i in run.tiles) {
    var tile = run.tiles[i]

    if (tile.scoredItems.obstacles !== undefined) {
      for (var j in tile.scoredItems.obstacles) {
        if (tile.scoredItems.obstacles[j]) {
          score += 10
        }
      }
    }
    if (tile.scoredItems.speedbumps !== undefined) {
      for (var j in tile.scoredItems.speedbumps) {
        if (tile.scoredItems.speedbumps[j]) {
          score += 5
        }
      }
    }
    if (tile.scoredItems.intersections !== undefined) {
      for (var j in tile.scoredItems.intersections) {
        if (tile.scoredItems.intersections[j]) {
          score += 15
        }
      }
    }
    if (tile.scoredItems.gaps !== undefined) {
      for (var j in tile.scoredItems.gaps) {
        if (tile.scoredItems.gaps[j]) {
          score += 10
        }
      }
    }
    if (tile.scoredItems.dropTiles !== undefined) {
      for (var j in tile.scoredItems.dropTiles) {
        if (tile.scoredItems.dropTiles[j]) {
          var count
          var index = dropTileIndexes.indexOf(tile.index[j])
          if (index == 0) {
            count = tile.index[j]
          } else {
            count = tile.index[j] - dropTileIndexes[index - 1]
          }

          score += max(count * (3 - run.LoPs[index]), 0)
        }
      }
    }
  }

  score += run.rescuedVictims * 40

  return score
}<commit_msg>Fixed math.max<commit_after>var logger = require('../config/logger').mainLogger

module.exports.calculateScore = function (run) {
  var score = 0

  if (run.showedUp !== undefined && run.showedUp) {
    score += 3
  }

  var dropTileIndexes = []

  for (var i in run.tiles) {
    var tile = run.tiles[i]

    if (tile.scoredItems.dropTiles !== undefined) {
      if (tile.scoredItems.dropTiles.length > 0) {
        dropTileIndexes.concat(tile.index)
      }
    }
  }

  dropTileIndexes.sort(function (a, b) {
    return a - b
  })

  for (var i in run.tiles) {
    var tile = run.tiles[i]

    if (tile.scoredItems.obstacles !== undefined) {
      for (var j in tile.scoredItems.obstacles) {
        if (tile.scoredItems.obstacles[j]) {
          score += 10
        }
      }
    }
    if (tile.scoredItems.speedbumps !== undefined) {
      for (var j in tile.scoredItems.speedbumps) {
        if (tile.scoredItems.speedbumps[j]) {
          score += 5
        }
      }
    }
    if (tile.scoredItems.intersections !== undefined) {
      for (var j in tile.scoredItems.intersections) {
        if (tile.scoredItems.intersections[j]) {
          score += 15
        }
      }
    }
    if (tile.scoredItems.gaps !== undefined) {
      for (var j in tile.scoredItems.gaps) {
        if (tile.scoredItems.gaps[j]) {
          score += 10
        }
      }
    }
    if (tile.scoredItems.dropTiles !== undefined) {
      for (var j in tile.scoredItems.dropTiles) {
        if (tile.scoredItems.dropTiles[j]) {
          var count
          var index = dropTileIndexes.indexOf(tile.index[j])
          if (index == 0) {
            count = tile.index[j]
          } else {
            count = tile.index[j] - dropTileIndexes[index - 1]
          }

          score += Math.max(count * (3 - run.LoPs[index]), 0)
        }
      }
    }
  }

  score += run.rescuedVictims * 40

  return score
}<|endoftext|>


<commit_before>const Event = Jymfony.Component.EventDispatcher.Event;
const InvalidArgumentException = Jymfony.Component.EventDispatcher.Exception.InvalidArgumentException;

/**
 * @memberOf Jymfony.Component.EventDispatcher
 */
class EventDispatcher {
    constructor() {
        this.listeners = {};
        this.sorted = {};
    }

    /**
     * Dispatches an event.
     * Returns a promise that resolves asynchronously running all
     * the listeners.
     *
     * @param {string} eventName
     * @param {Jymfony.Component.EventDispatcher.Event} event
     *
     * @returns {Promise.<Jymfony.Component.EventDispatcher.Event>}
     */
    dispatch(eventName, event = new Event) {
        let p = Promise.resolve(event);
        for(let listener of this.getListeners(eventName)) {
            p.then(event => {
                if (event.isPropagationStopped()) {
                    return event;
                }

                return __jymfony.Async.run(listener, event, eventName, this);
            });
        }

        return p;
    }

    /**
     * Gets the listeners associated to an event.
     *
     * @param {string} eventName
     *
     * @returns {Array}
     */
    * getListeners(eventName = undefined) {
        if (eventName) {
            if (! this.listeners[eventName]) {
                return [];
            }

            if (! this.sorted[eventName]) {
                this._sortListeners(eventName);
            }

            for (let listener of this.sorted[eventName]) {
                yield listener.listener;
            }
        } else {
            for (let eventName of Object.keys(this.listeners)) {
                yield * this.getListeners(eventName);
            }
        }
    }

    /**
     * Attach a listener to an event.
     *
     * @param {string} eventName
     * @param {Function|Array} listener
     * @param {int} priority
     */
    addListener(eventName, listener, priority = 0) {
        if (isCallableArray(listener)) {
            listener = getCallableFromArray(listener);
        }

        if (! isFunction(listener)) {
            throw new InvalidArgumentException("Listener must be a function");
        }

        let listeners = this.listeners[eventName];
        if (undefined === listeners) {
            listeners = this.listeners[eventName] = [];
        }

        listeners.push({ listener: listener, priority: priority });
        delete this.sorted[eventName];
    }

    /**
     * Whether an event has listeners attached.
     *
     * @param {string} eventName
     *
     * @returns {boolean} true if at least one listener is registered, false otherwise
     */
    hasListeners(eventName) {
        return (!! this.listeners[eventName]) && 0 < this.listeners[eventName].length;
    }

    /**
     * Detach a listener.
     *
     * @param {string} eventName
     * @param {Function|Array} listener
     */
    removeListener(eventName, listener) {
        if (! this.listeners[eventName]) {
            return;
        }

        if (isCallableArray(listener)) {
            listener = getCallableFromArray(listener);
        }

        for (let registered of this.listeners[eventName]) {
            if (! EventDispatcher._funcEquals(registered.listener, listener)) {
                continue;
            }

            let index = this.listeners[eventName].indexOf(registered);
            this.listeners[eventName].splice(index, 1);
        }
    }

    /**
     * Adds and event subscriber
     *
     * @param {*} subscriber
     */
    addSubscriber(subscriber) {
        let events = __jymfony.getFunction(subscriber, 'getSubscribedEvents')();
        for (let eventName of Object.keys(events)) {
            let params = events[eventName];
            if (isString(params)) {
                this.addListener(eventName, [ subscriber, params ]);
            } else if (isString(params[0])) {
                this.addListener(eventName, [ subscriber, params[0] ], params[1] || 0);
            } else {
                for (let listener of params) {
                    this.addListener(eventName, [ subscriber, listener[0] ], listener[1] || 0);
                }
            }
        }
    }

    /**
     * Detaches all the listeners from a subscriber
     *
     * @param {*} subscriber
     */
    removeSubscriber(subscriber) {
        let events = __jymfony.getFunction(subscriber, 'getSubscribedEvents')();
        for (let eventName of Object.keys(events)) {
            let params = events[eventName];
            if (isArray(params) && isArray(params[0])) {
                for(let listener of params) {
                    this.removeListener(eventName, [ subscriber, listener[0] ]);
                }
            } else {
                this.removeListener(eventName, [ subscriber, isString(params) ? params : params[0] ]);
            }
        }
    }

    /**
     * Sorts the array of listeners based on priority.
     *
     * @param {string} eventName
     *
     * @private
     */
    _sortListeners(eventName) {
        if (undefined === this.listeners[eventName]) {
            return;
        }

        // Clone the array
        let listeners = [ ...this.listeners[eventName] ];
        this.sorted[eventName] = listeners.sort((a, b) => {
            return b.priority - a.priority;
        });
    }

    static _funcEquals(func1, func2) {
        if (func1.innerObject) {
            func1 = func1.innerObject;
        }

        if (func2.innerObject) {
            func2 = func2.innerObject;
        }

        if (func1.constructor.name !== func2.constructor.name) {
            return false;
        }

        if (func1 instanceof BoundFunction) {
            return func1.equals(func2);
        }

        return func1 === func2;
    }
}

module.exports = EventDispatcher;
<commit_msg>[EventDispatcher] renamed private properties in EventDispatcher<commit_after>const Event = Jymfony.Component.EventDispatcher.Event;
const InvalidArgumentException = Jymfony.Component.EventDispatcher.Exception.InvalidArgumentException;

/**
 * @memberOf Jymfony.Component.EventDispatcher
 */
class EventDispatcher {
    constructor() {
        /**
         * Listeners map.
         *
         * @type {Object.<string, [Object]>}
         * @private
         */
        this._listeners = {};

        /**
         * Listeners map sorted by priority.
         *
         * @type {Object.<string, [Object]>}
         * @private
         */
        this._sorted = {};
    }

    /**
     * Dispatches an event.
     * Returns a promise that resolves asynchronously running all
     * the listeners.
     *
     * @param {string} eventName
     * @param {Jymfony.Component.EventDispatcher.Event} event
     *
     * @returns {Promise.<Jymfony.Component.EventDispatcher.Event>}
     */
    dispatch(eventName, event = new Event) {
        let p = Promise.resolve(event);
        for(let listener of this.getListeners(eventName)) {
            p.then(event => {
                if (event.isPropagationStopped()) {
                    return event;
                }

                return __jymfony.Async.run(listener, event, eventName, this);
            });
        }

        return p;
    }

    /**
     * Gets the listeners associated to an event.
     *
     * @param {string} eventName
     *
     * @returns {Array}
     */
    * getListeners(eventName = undefined) {
        if (eventName) {
            if (! this._listeners[eventName]) {
                return [];
            }

            if (! this._sorted[eventName]) {
                this._sortListeners(eventName);
            }

            for (let listener of this._sorted[eventName]) {
                yield listener.listener;
            }
        } else {
            for (let eventName of Object.keys(this._listeners)) {
                yield * this.getListeners(eventName);
            }
        }
    }

    /**
     * Attach a listener to an event.
     *
     * @param {string} eventName
     * @param {Function|Array} listener
     * @param {int} priority
     */
    addListener(eventName, listener, priority = 0) {
        if (isCallableArray(listener)) {
            listener = getCallableFromArray(listener);
        }

        if (! isFunction(listener)) {
            throw new InvalidArgumentException("Listener must be a function");
        }

        let listeners = this._listeners[eventName];
        if (undefined === listeners) {
            listeners = this._listeners[eventName] = [];
        }

        listeners.push({ listener: listener, priority: priority });
        delete this._sorted[eventName];
    }

    /**
     * Whether an event has listeners attached.
     *
     * @param {string} eventName
     *
     * @returns {boolean} true if at least one listener is registered, false otherwise
     */
    hasListeners(eventName) {
        return (!! this._listeners[eventName]) && 0 < this._listeners[eventName].length;
    }

    /**
     * Detach a listener.
     *
     * @param {string} eventName
     * @param {Function|Array} listener
     */
    removeListener(eventName, listener) {
        if (! this._listeners[eventName]) {
            return;
        }

        if (isCallableArray(listener)) {
            listener = getCallableFromArray(listener);
        }

        for (let registered of this._listeners[eventName]) {
            if (! EventDispatcher._funcEquals(registered.listener, listener)) {
                continue;
            }

            let index = this._listeners[eventName].indexOf(registered);
            this._listeners[eventName].splice(index, 1);
        }
    }

    /**
     * Adds and event subscriber
     *
     * @param {*} subscriber
     */
    addSubscriber(subscriber) {
        let events = __jymfony.getFunction(subscriber, 'getSubscribedEvents')();
        for (let eventName of Object.keys(events)) {
            let params = events[eventName];
            if (isString(params)) {
                this.addListener(eventName, [ subscriber, params ]);
            } else if (isString(params[0])) {
                this.addListener(eventName, [ subscriber, params[0] ], params[1] || 0);
            } else {
                for (let listener of params) {
                    this.addListener(eventName, [ subscriber, listener[0] ], listener[1] || 0);
                }
            }
        }
    }

    /**
     * Detaches all the listeners from a subscriber
     *
     * @param {*} subscriber
     */
    removeSubscriber(subscriber) {
        let events = __jymfony.getFunction(subscriber, 'getSubscribedEvents')();
        for (let eventName of Object.keys(events)) {
            let params = events[eventName];
            if (isArray(params) && isArray(params[0])) {
                for(let listener of params) {
                    this.removeListener(eventName, [ subscriber, listener[0] ]);
                }
            } else {
                this.removeListener(eventName, [ subscriber, isString(params) ? params : params[0] ]);
            }
        }
    }

    /**
     * Sorts the array of listeners based on priority.
     *
     * @param {string} eventName
     *
     * @private
     */
    _sortListeners(eventName) {
        if (undefined === this._listeners[eventName]) {
            return;
        }

        // Clone the array
        let listeners = [ ...this._listeners[eventName] ];
        this._sorted[eventName] = listeners.sort((a, b) => {
            return b.priority - a.priority;
        });
    }

    static _funcEquals(func1, func2) {
        if (func1.innerObject) {
            func1 = func1.innerObject;
        }

        if (func2.innerObject) {
            func2 = func2.innerObject;
        }

        if (func1.constructor.name !== func2.constructor.name) {
            return false;
        }

        if (func1 instanceof BoundFunction) {
            return func1.equals(func2);
        }

        return func1 === func2;
    }
}

module.exports = EventDispatcher;
<|endoftext|>


<commit_before>import { run } from '@ember/runloop';
import { getOwner } from '@ember/application';
import { test, moduleForComponent } from 'ember-qunit';
import { findAll } from 'ember-native-dom-helpers';
import wait from 'ember-test-helpers/wait';
import hbs from 'htmlbars-inline-precompile';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';

moduleForComponent(
  'job-page/parts/evaluations',
  'Integration | Component | job-page/parts/evaluations',
  {
    integration: true,
    beforeEach() {
      window.localStorage.clear();
      this.store = getOwner(this).lookup('service:store');
      this.server = startMirage();
      this.server.create('namespace');
    },
    afterEach() {
      this.server.shutdown();
      window.localStorage.clear();
    },
  }
);

test('lists all evaluations for the job', function(assert) {
  let job;

  this.server.create('job', { noFailedPlacements: true, createAllocations: false });
  this.store.findAll('job');

  return wait().then(() => {
    run(() => {
      job = this.store.peekAll('job').get('firstObject');
    });

    this.setProperties({ job });

    this.render(hbs`
      {{job-page/parts/evaluations job=job}}
    `);

    return wait().then(() => {
      const evaluationRows = findAll('[data-test-evaluation]');
      assert.equal(
        evaluationRows.length,
        job.get('evaluations.length'),
        'All evaluations are listed'
      );

      job
        .get('evaluations')
        .sortBy('modifyIndex')
        .reverse()
        .forEach((evaluation, index) => {
          assert.equal(
            evaluationRows[index].querySelector('[data-test-id]').textContent.trim(),
            evaluation.get('shortId'),
            `Evaluation ${index} is ${evaluation.get('shortId')}`
          );
        });
    });
  });
});
<commit_msg>Remove old evaluations component tests<commit_after><|endoftext|>


<commit_before>//
// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Test includes
var fs = require('fs');
var path = require('path');
var sinon = require('sinon');
var util = require('util');
var _ = require('underscore');

var keyFiles = require('../../lib/util/keyFiles');
var profile = require('../../lib/util/profile');
var utils = require('../../lib/util/utils');

var executeCommand = require('./cli-executor').execute;
var nockHelper = require('./nock-helper');

exports = module.exports = CLITest;

function CLITest(testPrefix, env, forceMocked) {
  if (!Array.isArray(env)) {
    forceMocked = env;
    env = [];
  }

  this.testPrefix = testPrefix;
  this.currentTest = 0;
  this.recordingsFile = __dirname + '/../recordings/' + this.testPrefix + '.nock.js';

  if (forceMocked) {
    this.isMocked = true;
  } else {
    this.isMocked = testPrefix && !process.env.NOCK_OFF;
  }

  this.isRecording = process.env.AZURE_NOCK_RECORD;
  this.skipSubscription = true;

  // change this in derived classes to switch mode
  this.commandMode = 'asm';

  // Normalize environment
  this.requiredEnvironment = env.map(function (env) {
    if (typeof(env) === 'string') {
      return { name: env, secure: false };
    } else {
      return env;
    }
  });

  this.validateEnvironment();
}

_.extend(CLITest.prototype, {
  validateEnvironment: function () {
    if (this.isMocked) {
      return;
    }

    var missing = [];
    this.requiredEnvironment.forEach(function (e) {
      if (!process.env[e.name] && !e.defaultValue) {
        missing.push(e.name);
      }
    });

    if (missing.length > 0) {
      throw new Error('This test requires the following environment variables which are not set: ' +
        missing.join(', '));
    }
  },

  setEnvironmentDefaults: function () {
    this.requiredEnvironment.forEach(function (env) {
      if (env.defaultValue && !process.env[env.name]) {
        process.env[env.name] = env.defaultValue;
      }
    });
  },

  setupSuite: function (callback) {
    if (this.isMocked) {
      process.env.AZURE_ENABLE_STRICT_SSL = false;

      // Force mode regardless of current stored setting
      var commandMode = this.commandMode;
      CLITest.wrap(sinon, utils, 'readConfig', function (originalReadConfig) {
        return function () {
          var config = originalReadConfig();
          config.mode = commandMode;
          return config;
        };
      });
    }

    if (!this.isMocked || this.isRecording) {
      this.setEnvironmentDefaults();
    }

    if (this.isRecording) {
        this.writeRecordingHeader();
    }

    // Remove any existing cache files before starting the test
    this.removeCacheFiles();

    callback();
  },

  teardownSuite: function (callback) {
    function restore(fn) {
      if (fn.restore) {
        fn.restore();
      }
    }

    this.currentTest = 0;

    if (this.isMocked) {
      if (this.isRecording) {
        fs.appendFileSync(this.recordingsFile, '];');
      }

      restore(utils.readConfig);

      delete process.env.AZURE_ENABLE_STRICT_SSL;
    }

    callback();
  },

  removeCacheFiles: function () {
    var sitesCachePath = path.join(utils.azureDir(), util.format('sites.%s.json', process.env.AZURE_SUBSCRIPTION_ID));
    if (utils.pathExistsSync(sitesCachePath)) {
      fs.unlinkSync(sitesCachePath);
    }

    var spacesCachePath = path.join(utils.azureDir(), util.format('spaces.%s.json', process.env.AZURE_SUBSCRIPTION_ID));
    if (utils.pathExistsSync(spacesCachePath)) {
      fs.unlinkSync(spacesCachePath);
    }

    var environmentsPath = path.join(utils.azureDir(), 'environment.json');
    if (utils.pathExistsSync(environmentsPath)) {
      fs.unlinkSync(environmentsPath);
    }
  },

  execute: function (cmd) {
    if (!_.isString(cmd) && !_.isArray(cmd)) {
      throw new Error('First argument needs to be a string or array with the command to execute');
    }

    var args = Array.prototype.slice.call(arguments);

    if (args.length < 2 || !_.isFunction(args[args.length - 1])) {
      throw new Error('Callback needs to be passed as last argument');
    }

    var callback = args[args.length - 1];

    if (_.isString(cmd)) {
      cmd = cmd.split(' ');

      var rep = 1;
      for (var i = 0; i < cmd.length; i++) {
        if (cmd[i] === '%s') {
          cmd[i] = args[rep++];
        }
      }
    }

    if (cmd[0] !== 'node') {
      cmd.unshift('cli.js');
      cmd.unshift('node');
    }

    if (!this.skipSubscription && this.isMocked && !this.isRecording && cmd[2] != 'vm' && cmd[3] != 'location') {
      cmd.push('-s');
      cmd.push(process.env.AZURE_SUBSCRIPTION_ID);
    }

    executeCommand(cmd, callback);
  },

  setupTest: function (callback) {
    nockHelper.nockHttp();

    if (this.isMocked && this.isRecording) {
      // nock recoding
      nockHelper.nock.recorder.rec(true);
    } else if (this.isMocked) {
      // nock playback
      var nocked = require(this.recordingsFile);

      if (this.currentTest < nocked.scopes.length) {
        nocked.scopes[this.currentTest++].forEach(function (createScopeFunc) {
          createScopeFunc(nockHelper.nock);
        });
      } else {
        throw new Error('It appears the ' + this.recordingsFile + ' file has more tests than there are mocked tests. ' +
          'You may need to re-generate it.');
      }

      if (nocked.getMockedProfile) {
        profile.current = nocked.getMockedProfile();
      }

      if (nocked.setEnvironment) {
        nocked.setEnvironment();
      }
    }

    callback();
  },

  teardownTest: function (callback) {
    if (this.isMocked && this.isRecording) {
      // play nock recording
      var scope = this.scopeWritten ? ',\n[' : '[';
      this.scopeWritten = true;
      var lineWritten;
      nockHelper.nock.recorder.play().forEach(function (line) {
        if (line.indexOf('nock') >= 0) {
          // apply fixups of nock generated mocks

          // do not filter on body as they usual have time related stamps
          line = line.replace(/(\.post\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.get\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.put\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.delete\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.merge\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.patch\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');

          scope += (lineWritten ? ',\n' : '') + 'function (nock) { \n' +
            'var result = ' + line + ' return result; }';
          lineWritten = true;
        }
      });
      scope += ']';
      fs.appendFileSync(this.recordingsFile, scope);
      nockHelper.nock.recorder.clear();
    }

    nockHelper.unNockHttp();

    callback();
  },

  writeRecordingHeader: function () {
    var template = fs.readFileSync(path.join(__dirname, 'preamble.template'), { encoding: 'utf8' });

    fs.writeFileSync(this.recordingsFile, _.template(template, {
      sub: profile.current.currentSubscription,
      requiredEnvironment: this.requiredEnvironment
    }));
  },

  /**
  * Generates an unique identifier using a prefix, based on a currentList and repeatable or not depending on the isMocked flag.
  *
  * @param {string} prefix          The prefix to use in the identifier.
  * @param {array}  currentList     The current list of identifiers.
  * @param {bool}   isMocked        Boolean flag indicating if the test is mocked or not.
  * @return {string} A new unique identifier.
  */
  generateId: function (prefix, currentList) {
    if (!currentList) {
      currentList = [];
    }

    while (true) {
      var newNumber;
      if (this.isMocked) {
        // Predictable
        newNumber = prefix + (currentList.length + 1);
        currentList.push(newNumber);

        return newNumber;
      } else {
        // Random
        newNumber = prefix + Math.floor(Math.random() * 10000);
        if (currentList.indexOf(newNumber) === -1) {
          currentList.push(newNumber);

          return newNumber;
        }
      }
    }
  }
});

/**
* A helper function to handle wrapping an existing method in sinon.
*
* @param {ojbect} sinonObj    either sinon or a sinon sandbox instance
* @param {object} object      The object containing the method to wrap
* @param {string} property    property name of method to wrap
* @param {function (function)} setup function that receives the original function,
*                              returns new function that runs when method is called.
* @return {object}             The created stub.
*/
CLITest.wrap = function wrap(sinonObj, object, property, setup) {
  var original = object[property];
  return sinonObj.stub(object, property, setup(original));
};
<commit_msg>Fixed environment validation while recording<commit_after>//
// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Test includes
var fs = require('fs');
var path = require('path');
var sinon = require('sinon');
var util = require('util');
var _ = require('underscore');

var keyFiles = require('../../lib/util/keyFiles');
var profile = require('../../lib/util/profile');
var utils = require('../../lib/util/utils');

var executeCommand = require('./cli-executor').execute;
var nockHelper = require('./nock-helper');

exports = module.exports = CLITest;

function CLITest(testPrefix, env, forceMocked) {
  if (!Array.isArray(env)) {
    forceMocked = env;
    env = [];
  }

  this.testPrefix = testPrefix;
  this.currentTest = 0;
  this.recordingsFile = __dirname + '/../recordings/' + this.testPrefix + '.nock.js';

  if (forceMocked) {
    this.isMocked = true;
  } else {
    this.isMocked = testPrefix && !process.env.NOCK_OFF;
  }

  this.isRecording = process.env.AZURE_NOCK_RECORD;
  this.skipSubscription = true;

  // change this in derived classes to switch mode
  this.commandMode = 'asm';

  // Normalize environment
  this.requiredEnvironment = env.map(function (env) {
    if (typeof(env) === 'string') {
      return { name: env, secure: false };
    } else {
      return env;
    }
  });

  this.validateEnvironment();
}

_.extend(CLITest.prototype, {
  validateEnvironment: function () {
    if (this.isMocked && !this.isRecording) {
      return;
    }

    var missing = [];
    this.requiredEnvironment.forEach(function (e) {
      if (!process.env[e.name] && !e.defaultValue) {
        missing.push(e.name);
      }
    });

    if (missing.length > 0) {
      throw new Error('This test requires the following environment variables which are not set: ' +
        missing.join(', '));
    }
  },

  setEnvironmentDefaults: function () {
    this.requiredEnvironment.forEach(function (env) {
      if (env.defaultValue && !process.env[env.name]) {
        process.env[env.name] = env.defaultValue;
      }
    });
  },

  setupSuite: function (callback) {
    if (this.isMocked) {
      process.env.AZURE_ENABLE_STRICT_SSL = false;

      // Force mode regardless of current stored setting
      var commandMode = this.commandMode;
      CLITest.wrap(sinon, utils, 'readConfig', function (originalReadConfig) {
        return function () {
          var config = originalReadConfig();
          config.mode = commandMode;
          return config;
        };
      });
    }

    if (!this.isMocked || this.isRecording) {
      this.setEnvironmentDefaults();
    }

    if (this.isRecording) {
        this.writeRecordingHeader();
    }

    // Remove any existing cache files before starting the test
    this.removeCacheFiles();

    callback();
  },

  teardownSuite: function (callback) {
    function restore(fn) {
      if (fn.restore) {
        fn.restore();
      }
    }

    this.currentTest = 0;

    if (this.isMocked) {
      if (this.isRecording) {
        fs.appendFileSync(this.recordingsFile, '];');
      }

      restore(utils.readConfig);

      delete process.env.AZURE_ENABLE_STRICT_SSL;
    }

    callback();
  },

  removeCacheFiles: function () {
    var sitesCachePath = path.join(utils.azureDir(), util.format('sites.%s.json', process.env.AZURE_SUBSCRIPTION_ID));
    if (utils.pathExistsSync(sitesCachePath)) {
      fs.unlinkSync(sitesCachePath);
    }

    var spacesCachePath = path.join(utils.azureDir(), util.format('spaces.%s.json', process.env.AZURE_SUBSCRIPTION_ID));
    if (utils.pathExistsSync(spacesCachePath)) {
      fs.unlinkSync(spacesCachePath);
    }

    var environmentsPath = path.join(utils.azureDir(), 'environment.json');
    if (utils.pathExistsSync(environmentsPath)) {
      fs.unlinkSync(environmentsPath);
    }
  },

  execute: function (cmd) {
    if (!_.isString(cmd) && !_.isArray(cmd)) {
      throw new Error('First argument needs to be a string or array with the command to execute');
    }

    var args = Array.prototype.slice.call(arguments);

    if (args.length < 2 || !_.isFunction(args[args.length - 1])) {
      throw new Error('Callback needs to be passed as last argument');
    }

    var callback = args[args.length - 1];

    if (_.isString(cmd)) {
      cmd = cmd.split(' ');

      var rep = 1;
      for (var i = 0; i < cmd.length; i++) {
        if (cmd[i] === '%s') {
          cmd[i] = args[rep++];
        }
      }
    }

    if (cmd[0] !== 'node') {
      cmd.unshift('cli.js');
      cmd.unshift('node');
    }

    if (!this.skipSubscription && this.isMocked && !this.isRecording && cmd[2] != 'vm' && cmd[3] != 'location') {
      cmd.push('-s');
      cmd.push(process.env.AZURE_SUBSCRIPTION_ID);
    }

    executeCommand(cmd, callback);
  },

  setupTest: function (callback) {
    nockHelper.nockHttp();

    if (this.isMocked && this.isRecording) {
      // nock recoding
      nockHelper.nock.recorder.rec(true);
    } else if (this.isMocked) {
      // nock playback
      var nocked = require(this.recordingsFile);

      if (this.currentTest < nocked.scopes.length) {
        nocked.scopes[this.currentTest++].forEach(function (createScopeFunc) {
          createScopeFunc(nockHelper.nock);
        });
      } else {
        throw new Error('It appears the ' + this.recordingsFile + ' file has more tests than there are mocked tests. ' +
          'You may need to re-generate it.');
      }

      if (nocked.getMockedProfile) {
        profile.current = nocked.getMockedProfile();
      }

      if (nocked.setEnvironment) {
        nocked.setEnvironment();
      }
    }

    callback();
  },

  teardownTest: function (callback) {
    if (this.isMocked && this.isRecording) {
      // play nock recording
      var scope = this.scopeWritten ? ',\n[' : '[';
      this.scopeWritten = true;
      var lineWritten;
      nockHelper.nock.recorder.play().forEach(function (line) {
        if (line.indexOf('nock') >= 0) {
          // apply fixups of nock generated mocks

          // do not filter on body as they usual have time related stamps
          line = line.replace(/(\.post\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.get\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.put\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.delete\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.merge\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');
          line = line.replace(/(\.patch\('.*?')\s*,\s*"[^]+[^\\]"\)/, '.filteringRequestBody(function (path) { return \'*\';})\n$1, \'*\')');

          scope += (lineWritten ? ',\n' : '') + 'function (nock) { \n' +
            'var result = ' + line + ' return result; }';
          lineWritten = true;
        }
      });
      scope += ']';
      fs.appendFileSync(this.recordingsFile, scope);
      nockHelper.nock.recorder.clear();
    }

    nockHelper.unNockHttp();

    callback();
  },

  writeRecordingHeader: function () {
    var template = fs.readFileSync(path.join(__dirname, 'preamble.template'), { encoding: 'utf8' });

    fs.writeFileSync(this.recordingsFile, _.template(template, {
      sub: profile.current.currentSubscription,
      requiredEnvironment: this.requiredEnvironment
    }));
  },

  /**
  * Generates an unique identifier using a prefix, based on a currentList and repeatable or not depending on the isMocked flag.
  *
  * @param {string} prefix          The prefix to use in the identifier.
  * @param {array}  currentList     The current list of identifiers.
  * @param {bool}   isMocked        Boolean flag indicating if the test is mocked or not.
  * @return {string} A new unique identifier.
  */
  generateId: function (prefix, currentList) {
    if (!currentList) {
      currentList = [];
    }

    while (true) {
      var newNumber;
      if (this.isMocked) {
        // Predictable
        newNumber = prefix + (currentList.length + 1);
        currentList.push(newNumber);

        return newNumber;
      } else {
        // Random
        newNumber = prefix + Math.floor(Math.random() * 10000);
        if (currentList.indexOf(newNumber) === -1) {
          currentList.push(newNumber);

          return newNumber;
        }
      }
    }
  }
});

/**
* A helper function to handle wrapping an existing method in sinon.
*
* @param {ojbect} sinonObj    either sinon or a sinon sandbox instance
* @param {object} object      The object containing the method to wrap
* @param {string} property    property name of method to wrap
* @param {function (function)} setup function that receives the original function,
*                              returns new function that runs when method is called.
* @return {object}             The created stub.
*/
CLITest.wrap = function wrap(sinonObj, object, property, setup) {
  var original = object[property];
  return sinonObj.stub(object, property, setup(original));
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const Base = require('../../Base.js').Base;
const BaseMap = require('../../base/BaseMap.js').BaseMap;
const breakpointsToMediaQueries = require('../../utils/processors.js').breakpointsToMediaQueries;

/**
 * Holds all global configurations.
 *
 * @memberOf model.configuration
 */
class GlobalConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        // Settings
        this._values = new BaseMap();
        this._values.load(this.defaults);
        this._values.load(options);
        if (!this._values.has('mediaQueries'))
        {
            this._values.set('mediaQueries', breakpointsToMediaQueries(this._values.get('breakpoints')));
        }
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/GlobalConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/GlobalConfiguration';
    }

    /**
     * @inheritDoc
     */
    get defaults()
    {
        const result =
        {
            groups:
            {
                default: 'common'
            },
            breakpoints:
            {
            },
            tests:
            [
            ]
        };
        return result;
    }

    /**
     * @inheritDoc
     */
    get(name)
    {
        const result = this._values.getByPath(name);
        if (typeof result === 'undefined')
        {
            throw new Error('Could not find settings for ' + name);
        }
        return result;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.GlobalConfiguration = GlobalConfiguration;
<commit_msg>feat(model): adds key check to GlobalConfiguration<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const Base = require('../../Base.js').Base;
const BaseMap = require('../../base/BaseMap.js').BaseMap;
const breakpointsToMediaQueries = require('../../utils/processors.js').breakpointsToMediaQueries;

/**
 * Holds all global configurations.
 *
 * @memberOf model.configuration
 */
class GlobalConfiguration extends Base
{
    /**
     * @param {object} options
     */
    constructor(options)
    {
        super();

        // Settings
        this._values = new BaseMap();
        this._values.load(this.defaults);
        this._values.load(options);
        if (!this._values.has('mediaQueries'))
        {
            this._values.set('mediaQueries', breakpointsToMediaQueries(this._values.get('breakpoints')));
        }
    }

    /**
     * @inheritDoc
     */
    static get injections()
    {
        return { 'parameters': ['model.configuration/GlobalConfiguration.options'] };
    }

    /**
     * @inheritDoc
     */
    static get className()
    {
        return 'model.configuration/GlobalConfiguration';
    }

    /**
     * @inheritDoc
     */
    get defaults()
    {
        const result =
        {
            groups:
            {
                default: 'common'
            },
            breakpoints:
            {
            },
            tests:
            [
            ]
        };
        return result;
    }

    /**
     * @inheritDoc
     */
    has(name)
    {
        return this._values.has(name);
    }

    /**
     * @inheritDoc
     */
    get(name)
    {
        const result = this._values.getByPath(name);
        if (typeof result === 'undefined')
        {
            throw new Error('Could not find settings for ' + name);
        }
        return result;
    }
}

/**
 * Exports
 * @ignore
 */
module.exports.GlobalConfiguration = GlobalConfiguration;
<|endoftext|>


<commit_before>// @flow
import MobxReactForm from 'mobx-react-form';
import { waitForExist } from './waitForExist';

export default class ReactToolboxMobxForm extends MobxReactForm {
  bindings() {
    return {
      ReactToolbox: {
        id: 'id',
        name: 'name',
        type: 'type',
        value: 'value',
        label: 'label',
        placeholder: 'hint',
        disabled: 'disabled',
        error: 'error',
        onChange: 'onChange',
        onFocus: 'onFocus',
        onBlur: 'onBlur',
      },
    };
  }
}

type HandleFormErrorsOptions = {
  focusElement?: ?boolean,
  asyncSelector?: ?boolean,
};

export const handleFormErrors = async (
  querySelector: string,
  options: HandleFormErrorsOptions = {}
) => {
  const { focusElement, asyncSelector } = options;

  const firstErrorLabel = asyncSelector
    ? await waitForExist(querySelector)
    : document.querySelector(querySelector);

  if (firstErrorLabel) {
    firstErrorLabel.scrollIntoView({ behavior: 'smooth' });
  }

  if (
    focusElement &&
    firstErrorLabel &&
    firstErrorLabel.parentNode instanceof HTMLElement
  ) {
    const input = firstErrorLabel.parentNode.querySelector('input');
    if (input) input.focus();
  }
};
<commit_msg>[DDW-619] timeout for focusing input<commit_after>// @flow
import MobxReactForm from 'mobx-react-form';
import { waitForExist } from './waitForExist';

export default class ReactToolboxMobxForm extends MobxReactForm {
  bindings() {
    return {
      ReactToolbox: {
        id: 'id',
        name: 'name',
        type: 'type',
        value: 'value',
        label: 'label',
        placeholder: 'hint',
        disabled: 'disabled',
        error: 'error',
        onChange: 'onChange',
        onFocus: 'onFocus',
        onBlur: 'onBlur',
      },
    };
  }
}

type HandleFormErrorsOptions = {
  focusElement?: ?boolean,
  asyncSelector?: ?boolean,
};

export const handleFormErrors = async (
  querySelector: string,
  options: HandleFormErrorsOptions = {}
) => {
  const { focusElement, asyncSelector } = options;

  const firstErrorLabel = asyncSelector
    ? await waitForExist(querySelector)
    : document.querySelector(querySelector);

  if (firstErrorLabel) {
    firstErrorLabel.scrollIntoView({ behavior: 'smooth' });
  }

  if (
    focusElement &&
    firstErrorLabel &&
    firstErrorLabel.parentNode instanceof HTMLElement
  ) {
    const input = firstErrorLabel.parentNode.querySelector('input');
    if (input) setTimeout(() => input.focus(), 500);
  }
};
<|endoftext|>