/*
Copyright 2012-2026 VeloViewer. All Rights Reserved
###V176###
*/
var activitiesTimer = null,
  lineLayer = null,
  mapDataLayer = null,
  elevChart, elevChartData, elevChartFill;
var splitAuto, gettingPublic = true,
  brushExtent = [0, 0],
  brushing = false,
  chartData = [],
  mainChart;
var startIndex;
var endIndex;
var actStrsPublic = [],
  actStrs = [],
  segShown = false,
  map3, filterTimer = null,
  filterTime = 200,
  xmlDataObj, activitiesData, mapData, segment, mapExtent, fullMapExtent, highlightFeature, highlightFeature2, markerFeature, filtIndExtent, otherBrushExtent = [0, 0],
  llObj, timeObj, elevObj, spdObj, hrtObj, cadObj, tempObj, torqueObj, lenObj, grdObj, pwrObj, aveSpdObj, avePwrObj, nrmPwrObj, altGainObj, distObj, speedValues, timeValues, distValues, otherDistValues, eleValues, mainXObj, mainXVal, wattsAvl = false;
var allCharts = [],
  sumCharts = [],
  rateLimitedMessage = false,
  rateLimited = false,
  filtersFinishedFunRun = false,
  dataProcessed = false,
  initCalled = false,
  cpref = 'sd',
  p3dLoaded = false,
  curTab = 'map',
  currentChartColor = '',
  filename = '',
  elevationPreSmoothed = true,
  sadTT = 0,
  currentLocationDx2, currentLocationInd = 0,
  id, isOS = false,
  setMapMarkerDelay = 100,
  elevBackgroundSize = 2000;

var kz = 0;
if (typeof(raceStages) !== 'undefined' && raceStages.length > 0 && raceStages[0].kmZero > 0 && getParameterByName('n') == '') {
  kz = +raceStages[0].kmZero;
} else {
  try {
    var distI = route.streams.map(function(d) {
      return d.type
    }).indexOf('distance');
    var d0 = route.streams[distI].data[0];
    if (d0 != 0) {
      route.streams[distI].data = route.streams[distI].data.map(function(d) {
        return d - d0
      });
      route.distance -= d0;
    }
  } catch (e) {}
}

var xType = 'distance';
var
  /*lineStyle = {
    strokeOpacity: 1,
    strokeWidth: 3,
    pointRadius: 0.5,
    pointerEvents: "visiblePainted",
    strokeColor: '#DD4814'
  },*/
  lineStyle2 = {
    strokeOpacity: 1,
    strokeWidth: 3,
    pointRadius: 0.5,
    pointerEvents: "visiblePainted",
    strokeColor: '#0000FF'
  };
//elevMult = (elevMult == 2 ? 5 : elevMult),
fStyle = {
  fill: true,
  fillOpacity: 0,
  fillColor: '#000',
  stroke: true,
  strokeColor: '#000',
  strokeOpacity: 0.8,
  strokeWidth: 3,
  pointRadius: 4
};


var jsonObj;

function findParam(obj, param) {
  if (typeof(param.length) === 'undefined') {
    param = [param];
  }
  var r = [];
  for (var att in obj) {
    if (param.indexOf(att) > -1) {
      if (typeof(obj[att].length) === 'number') {
        r = d3.merge([r, obj[att]]);
      } else {
        r.push(obj[att]);
      }
    } else {
      if (typeof(obj[att]) === 'object') {
        r = d3.merge([r, findParam(obj[att], param)]);
      }
    }
  }
  return r;
}

function sortDistances(a, b) {
  return a.d - b.d
}

function setupLatLngElv() {
  route.latLngArr2Copy = clone(route.latLngArr2);
  route.elevationArr2Copy = clone(route.elevationArr2);
  route.distanceArr2Copy = clone(route.distanceArr2);

  if (spinLightYVal < -90) {
    spinYVal = -25;
    spinLightYVal = -30;
  }

  var latExtent = d3.extent(route.latLngArr2.map(function(d) {
    return d.lat
  }));
  var lngExtent = d3.extent(route.latLngArr2.map(function(d) {
    return d.lng
  }));

  route.minLat = latExtent[0];
  route.maxLat = latExtent[1];
  route.minLng = lngExtent[0];
  route.maxLng = lngExtent[1];

  if (typeof(route.gradArr) === 'undefined') {
    route.gradArr = route.distanceArr2.map(function(d,i) { 
      let j = i == 0 ? 1 : i;
      return (route.elevationArr2[j] - route.elevationArr2[j-1]) / (route.distanceArr2[j] - route.distanceArr2[j-1]);
    });
  }
  segment = clone(route);
}

function setMapMarker(dx, dx2) {
  dx2 = typeof(dx2) === 'undefined' ? d3.event.clientX - elevChart.markerG.left : dx2;
  dx = typeof(dx) === 'undefined' ? elevChart.xScale().invert(dx2) : dx;
  var ind = Math.min(route.distanceArr2.length - 1, Math.max(0, d3.bisectLeft(route.distanceArr2, dx)));
  currentLocationInd = ind;
  currentLocationDx2 = dx2;
  /*var ind2 = d3.bisectLeft(elevChartData[0].values.map(function(d) {
      return d.x
  }), dx);*/
  if (typeof(raceStages) !== 'undefined' && raceStages.length > 0) {
    d3.select('#currentLocation').text(Math.ceil(+raceStages[0].kmZero + route.distanceArr2[ind]) + ' ' + ind);
    if (displayWeatherInfo) displayWeatherInfo(ind, dx2);
  }

  marker.attr('transform', 'translate(' + dx2 + ',0)');
  if (route.distanceArr2[ind + 1] != route.distanceArr2[ind]) {
    if (ind == route.distanceArr2.length - 1) {
      ind--;
    }
    marker.mt.text(f1dp(100 * (route.elevationArr2[ind + 1] - route.elevationArr2[ind]) / (route.distanceArr2[ind + 1] - route.distanceArr2[ind])) + '% ' + fDist(dx) + ' ' + fSmlDist(route.elevationArr2[ind]));
  } else {
    marker.mt.text(f1dp(0) + '% ' + fDist(dx) + ' ' + fSmlDist(route.elevationArr2[ind]));
  }
  marker.mtr.text(fDist(route.distance - dx));

  // make sure marker is on screen
  var mtRectWidth = marker.mt.node().getBoundingClientRect().width + 10;
  var mtrRectWidth = marker.mtr.node().getBoundingClientRect().width + 10;
  var keepOnScreenAdjuster = 0;
  if (dx2 < mtRectWidth - 35) {
    keepOnScreenAdjuster = mtRectWidth - dx2 - 35;
  }
  marker.mtRect.attr('width', mtRectWidth).attr("transform", "translate(" + (-1 * (mtRectWidth + 1) + keepOnScreenAdjuster) + ",68)");
  marker.mtrRect.attr('width', mtrRectWidth).attr("transform", "translate(" + (keepOnScreenAdjuster) + ",68)");
  marker.mt.attr("transform", "translate(" + (keepOnScreenAdjuster - 5) + ",80)");
  marker.mtr.attr("transform", "translate(" + (keepOnScreenAdjuster + 5) + ",80)");

  if (curTab == 'sv') {
    svMarker.setPosition(new google.maps.LatLng(segment.latLngArr2Copy[ind].lat, segment.latLngArr2Copy[ind].lng));
    if (d3.event != null && d3.event.ctrlKey) {
      if (!svPano.visible) {
        svPano.setVisible(true);
      }
      ind = ind == route.latLngArr2.length - 1 ? ind - 1 : ind;
      isPanningSV = true;
      setSVPano(route.latLngArr2[ind], route.latLngArr2[ind + 1], true);
      d3.event = null;
      //isPanningSV = false;
    }
  }
}

function setMapFilter() {
  if (typeof(map3) !== 'undefined' && typeof(map3.controls.length) !== 'undefined') {
    /*if (highlightFeature != null) {
        lineLayer3.removeFeatures([highlightFeature]);
    }*/

    if (brushExtent[0] != brushExtent[1] && brushExtent[1] > 0) {
      var linePoints = route.linePoints
        .slice(filtIndExtent[0], filtIndExtent[1] + 1);

      var latExtent = d3.extent(linePoints.map(function(d) {
        return d.y
      }));
      var lngExtent = d3.extent(linePoints.map(function(d) {
        return d.x
      }));

      mapExtent = new OpenLayers.Bounds(lngExtent[0], latExtent[0], lngExtent[1], latExtent[1]);

      map3.zoomToExtent(mapExtent);

      var lineString = new OpenLayers.Geometry.LineString(linePoints);
      highlightFeature = new OpenLayers.Feature.Vector(lineString, null, mapLineBGSel);

      lineLayer3.addFeatures([highlightFeature]);
    } else {
      highlightFeature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(route.linePoints.slice(0)), null, mapLineBGSel);

      map3.zoomToExtent(fullMapExtent);
    }

    var f = lineLayer3.features;
    lineLayer3.removeAllFeatures();
    lineLayer3.addFeatures([f[0], highlightFeature, f[2]]);
    if (markerFeature != null) {
      lineLayer3.addFeatures([markerFeature]);
    }
    setMapDetail('O', lineLayer3);
  }
}

function setSelectedInfo() {
  if (typeof(filtIndExtent) !== 'undefined') {
    d3.select('#currentLocation2').text(filtIndExtent.join() + ',1');
    d3.select('#currentLocation3').text('lat: ' + segment.latLngArr2Copy[filtIndExtent[0]].lat.toFixed(5) + ' lng: ' + segment.latLngArr2Copy[filtIndExtent[0]].lng.toFixed(5));

    var dist = segment.distanceArr2Copy[filtIndExtent[1]] - segment.distanceArr2Copy[filtIndExtent[0]];
    var grad = (segment.elevationArr2Copy[filtIndExtent[1]] - segment.elevationArr2Copy[filtIndExtent[0]]) / (segment.distanceArr2Copy[filtIndExtent[1]] - segment.distanceArr2Copy[filtIndExtent[0]]);
    if (filtIndExtent[0] == 0 && segment.distanceArr2Copy.length - 1 == filtIndExtent[1]) {
      d3.select('#selectedInfoPanel').html(fDist(dist, 3) + ' ' + fSmlDist(d3.sum(route.elevationGainArr)) + ' ' + f1dp(grad * 100) + ' %');
    } else {
      var distFromStart = segment.distanceArr2Copy[filtIndExtent[0]];
      var distToEnd = segment.distanceArr2Copy[segment.distanceArr2Copy.length - 1] - segment.distanceArr2Copy[filtIndExtent[1]];
      d3.select('#selectedInfoPanel').html('<span title="Distance from km zero to start of selection">' + fDist(distFromStart, 3) + '</span><span style="margin:0px 5px;padding:2px 0px;background-color:#DDD"><button class="btn btn-primary btn-mini" id="shiftFiltExtDown" style="padding-left: 2px;padding-right: 2px;border-width: 0px; margin: 0px 0px 2.5px 1px;">&lt;</button><button class="btn btn-primary btn-mini" id="shiftFiltExtDownInv" style="padding-left: 2px;padding-right: 2px;border-width: 0px; margin: 0px 0px 2.5px 1px;">&gt;</button> <span style="width:140px;font-weight:normal;display:inline-block;text-align:center">' + fDist(dist, 2) + ' ' + fSmlDist(d3.sum(route.elevationGainArr.slice(filtIndExtent[0], filtIndExtent[1]))) + ' ' + f1dp(grad * 100) + ' %</span> <button id="shiftFiltExtUpInv" class="btn btn-primary btn-mini" style="padding-left: 2px;padding-right: 2px;border-width: 0px; margin: 0px 0px 2.5px 1px;">&lt;</button><button id="shiftFiltExtUp" class="btn btn-primary btn-mini" style="padding-left: 2px;padding-right: 2px;border-width: 0px; margin: 0px 0px 2.5px 1px;">&gt;</button></span><span title="Distance from end of selection to end of the route">' + fDist(distToEnd, 3) + '</span>');

      d3.select('#shiftFiltExtDown').attr('title', 'Lengthen selection by one data-point from the start.\nHold alt key to move approx 250m.\nHold shift key to move approx 500m.')
        .on('click', function() {
          var delta = -1;
          if (d3.event.shiftKey || d3.event.altKey) {
            while (route.distanceArr2Copy[filtIndExtent[0]] - route.distanceArr2Copy[filtIndExtent[0] + delta] < (d3.event.shiftKey ? 500 : 250) && filtIndExtent[0] + delta > 0) {
              delta--;
            }
          }
          brushExtent = [route.distanceArr2Copy[Math.max(0, filtIndExtent[0] + delta)], brushExtent[1]];
          elevBrush.extent(brushExtent);
          d3.select('#elevChart svg .brush').call(elevBrush);
          filterLocalData(d3.event.shiftKey || d3.event.altKey);
        });
      d3.select('#shiftFiltExtDownInv').attr('title', 'Shorten selection by one data-point from the start.\nHold alt key to move approx 250m.\nHold shift key to move approx 500m.')
        .on('click', function() {
          var delta = 1;
          if (d3.event.shiftKey || d3.event.altKey) {
            while (route.distanceArr2Copy[filtIndExtent[0]] - route.distanceArr2Copy[filtIndExtent[0] + delta] > (d3.event.shiftKey ? -500 : -250) && filtIndExtent[0] + delta < filtIndExtent[1] - 1) {
              delta++;
            }
          }
          if (filtIndExtent[0] + delta >= filtIndExtent[1]) return;
          brushExtent = [route.distanceArr2Copy[Math.max(0, filtIndExtent[0] + delta)], brushExtent[1]];
          elevBrush.extent(brushExtent);
          d3.select('#elevChart svg .brush').call(elevBrush);
          filterLocalData(d3.event.shiftKey || d3.event.altKey);
        });
      d3.select('#shiftFiltExtUp').attr('title', 'Lengthen selection by one data-point from the end.\nHold alt key to move approx 250m.\nHold shift key to move approx 500m.')
        .on('click', function() {
          var delta = 1;
          if (d3.event.shiftKey || d3.event.altKey) {
            while (route.distanceArr2Copy[filtIndExtent[1]] - route.distanceArr2Copy[filtIndExtent[1] + delta] > (d3.event.shiftKey ? -500 : -250) && filtIndExtent[1] + delta < route.distanceArr2Copy.length - 1) {
              delta++;
            }
          }
          brushExtent = [brushExtent[0], route.distanceArr2Copy[Math.min(route.distanceArr2Copy.length - 1, filtIndExtent[1] + delta)]];
          elevBrush.extent(brushExtent);
          d3.select('#elevChart svg .brush').call(elevBrush);
          filterLocalData(d3.event.shiftKey || d3.event.altKey);
        });
      d3.select('#shiftFiltExtUpInv').attr('title', 'Shorten selection by one data-point from the end.\nHold alt key to move approx 250m.\nHold shift key to move approx 500m.')
        .on('click', function() {
          var delta = -1;
          if (d3.event.shiftKey || d3.event.altKey) {
            while (route.distanceArr2Copy[filtIndExtent[1]] - route.distanceArr2Copy[filtIndExtent[1] + delta] < (d3.event.shiftKey ? 500 : 250) && filtIndExtent[1] + delta > filtIndExtent[0] + 1) {
              delta--;
            }
          }
          if (filtIndExtent[1] + delta <= filtIndExtent[0]) return;
          brushExtent = [brushExtent[0], route.distanceArr2Copy[Math.min(route.distanceArr2Copy.length - 1, filtIndExtent[1] + delta)]];
          elevBrush.extent(brushExtent);
          d3.select('#elevChart svg .brush').call(elevBrush);
          filterLocalData(d3.event.shiftKey || d3.event.altKey);
        });
    }
    if (typeof (selectionChanged) !== 'undefined') selectionChanged();
    if (typeof (showLapOptions) !== 'undefined') showLapOptions();
  }
}

function drawElevationChart() {
  let elevArr =  route.elevationArr2;
  nv.addGraph(function() {
    route.elevationExt = d3.extent(elevArr);
    elevChart = nv.models.lineChart()
      .margin({
        left: route.elevationExt[1] > 1000 ? 50 : (route.elevationExt[1] > 100 ? 40 : 35),
        bottom: 25,
        top: typeof(elevChartMarginTop) === 'undefined' ? 5 : elevChartMarginTop
      }) //Adjust chart margins to give the x-axis some breathing room.
      //.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
      .showLegend(false) //Show the legend, allowing users to turn on/off line series.
    //.showYAxis(true) //Show the y-axis
    //.showXAxis(true) //Show the x-axis
    ;

    elevChart.duration(0);

    elevChart.xAxis //Chart x-axis settings
      .tickFormat(function(d) {
        return fDist(d, (route.distance < 20 / lrgLenMult ? 1 : 0))
      });

    elevChart.yAxis //Chart y-axis settings
      .tickFormat(function(d) {
        return fSmlDist(d, 0)
      });

    elevChartData = [{
      values: route.distanceArr2.map(function(d, i) {
        return {
          x: d,
          y: elevArr[i],
          i: i,
          z: 0
        }
      }),
      key: 'Elevation',
      area: true,
      color: 'url("#elevFillGrad")'
    }];

    if (typeof(setOtherElevations) !== 'undefined') {
      setOtherElevations();
    }
    elevChartData.forEach(function(d) {
      d.values = simplify(d.values, 0.5);
    });

    if (window.innerWidth < 768) {
      d3.select('#elevChart').style('margin-left', '-15px').style('margin-right', '-15px');
    } else {
      d3.select('#elevChart').style('margin-left', '-10px').style('margin-right', '-10px');
    }

    d3.select('#elevChart svg')
      .datum(elevChartData) //Populate the <svg> element with chart data...
      .call(elevChart); //Finally, render the chart!

    elevBrush = d3.svg.brush()
      .x(elevChart.xAxis.scale())
      .on('brush', onBrush)
      .on('brushend', onBrush)
      .extent([0, 0]);

    var brushC = d3.select('#elevChart svg g');
    (brushC.empty() ? brushC.append('g') : brushC.select('g'))
    .append('g')
      .attr("id", "markerG")
      .attr("class", "brush")
      .on('mousemove', throttled(setMapMarkerDelay, function() {
        setMapMarker()
      }))
      .on('mouseover', d3.select('#elevChart .nv-point-paths').on('mouseover'))
      .on('mouseout', d3.select('#elevChart .nv-point-paths').on('mouseout'))
      .call(elevBrush)
      .selectAll('rect')
      .attr('height', 64);

    d3.select('#areaHighlight').remove();
    d3.select('#elevChart .nv-linesWrap .nv-groups')
      .insert("rect", ":first-child")
      .style('display', 'none')
      .attr({
        'id': 'areaHighlight',
        'width': '50',
        'height': '5',
        'fill': '#777'
      });

    d3.selectAll('#marker').remove();
    marker = d3.select('#elevChart .nv-linesWrap .nv-groups').append('g')
      .attr('class', 'marker');
    marker.append('line')
      .attr('stroke', '#222')
      .attr('x1', 0)
      .attr('x2', 0)
      .attr('y1', 0)
      .attr('y2', 64);

    marker.mtRect = marker.append('rect')
      .classed('markerTopVal', true)
      .attr('fill', 'white')
      .attr('stroke', 'black')
      //.attr('fill-opacity', 0.8)
      //.attr('stroke-opacity', 0.8)
      .attr('height', 15)
      .attr('width', 100)
      .attr('rx', 5)
      .attr('ry', 5)
      .attr("transform", "translate(-101,68)");
    marker.mt = marker.append('text')
      .classed('markerTopVal', true)
      .attr("transform", "translate(-5,80)")
      .style("text-anchor", "end")
      .text(f1dp(0) + '% ' + fDist(0) + ' ' + fSmlDist(elevArr[0]));

    marker.mtrRect = marker.append('rect')
      .classed('markerTopVal', true)
      .attr('fill', 'white')
      .attr('stroke', 'black')
      //.attr('fill-opacity', 0.8)
      //.attr('stroke-opacity', 0.8)
      .attr('height', 15)
      .attr('width', 60)
      .attr('rx', 5)
      .attr('ry', 5)
      .attr("transform", "translate(1,68)");
    marker.mtr = marker.append('text')
      .classed('markerTopVal', true)
      .attr("transform", "translate(5,80)")
      .text(fDist(route.distance));

    marker.mtRect.attr('width', marker.mt.node().getBoundingClientRect().width + 8).attr("transform", "translate(-" + (marker.mt.node().getBoundingClientRect().width + 9) + ",68)");
    marker.mtrRect.attr('width', marker.mtr.node().getBoundingClientRect().width + 8);

    d3.selectAll('#elevChart .nv-point-paths path, #elevChart .nv-point-clips, #elevChart .nv-point-paths').remove();
    d3.select("svg g.nv-series-0").style("fill-opacity", 1).style('stroke', '#333');

    setElevFillGradSize();

    elevChart.markerG = d3.select('#markerG').node();
    elevChart.markerG.left = elevChart.markerG.getBoundingClientRect().left;

    //Update the chart when window resizes.
    nv.utils.windowResize(function() {
      elevChart.update();
      d3.select("svg g.nv-series-0").style("fill-opacity", 1).style('stroke', '#333');
      elevBrush.extent(brushExtent);
      d3.select('#elevChart .brush').call(elevBrush);
      setBrushDash();
      setElevFillGradSize();
      if (typeof(sizeChangedX) !== 'undefined') {
        sizeChangedX();
      }
      if (typeof(drawElevChartWaymarkers) !== 'undefined') {
        drawElevChartWaymarkers();
      }
      if (typeof(drawElevChartWeather) !== 'undefined') {
        drawElevChartWeather();
      }

      elevChart.markerG.left = elevChart.markerG.getBoundingClientRect().left;
    });

    var d = document.getElementById('areaHighlight');
    d.parentNode.appendChild(d);

    return elevChart;
  });
  d3.select('#elevChart').on('dblclick', function() {
    d3.select('#markerG').call(elevBrush.extent([0, 0]));
    brushExtent = elevBrush.extent();
    filterLocalData();
  });
}

function setElevFillGradSize() {
  d3.select('#elevFillGrad image').style('transform', 'scale(' + (d3.select('.nv-series-0 path').node().getBoundingClientRect().width / elevBackgroundSize) + ')');

  /*elevChartFill.selectAll('rect')
    .attr('x', function(d, i) {
      return i == 0 ? 0 : elevChart.xScale()(gMapFullGradArr[i - 1].x);
      //return i == 0 ? 0 : (100 * (gMapFullGradArr[i - 1].x + kz) / route.distance) + '%'
    })
    .attr('width', function(d, i) {
      if (i == 0) {
        return elevChart.xScale()(d.x);
        //return (100 * (d.x + kz) / route.distance) + '%'
      } else {
        return elevChart.xScale()(d.x - gMapFullGradArr[i - 1].x - kz);
        //return ((100 * (d.x + kz) / route.distance) - (100 * (gMapFullGradArr[i - 1].x + kz) / route.distance)) + '%'
      }
    })*/
}

function onBrush() {
  brushExtent = d3.event.target.extent();
  filterLocalData();
}

function filterLocalData(autoZoom) {
  autoZoom = typeof(autoZoom) === 'undefined' ? true : autoZoom;
  setBrushDash();
  d3.selectAll('#getgpx, #getgpxr, #getkml, #gettcx, #getcsv').remove();
  if (filterTimer == null) {
    filterTimer = setTimeout(function() {
      var startTime = new Date().getTime();

      if (brushExtent[0] == brushExtent[1]) {
        filtIndExtent = [0, segment.latLngArr2Copy.length - 1];
      } else {
        filtIndExtent = [Math.max(0, d3.bisect(segment.distanceArr2Copy, brushExtent[0]) - 1), Math.min(segment.distanceArr2Copy.length - 1, d3.bisectLeft(segment.distanceArr2Copy, brushExtent[1]))];
        if (segment.distanceArr2Copy.length > filtIndExtent[0] + 1 && segment.distanceArr2Copy[filtIndExtent[0] + 1] < segment.distanceArr2Copy[filtIndExtent[0]]) {
          filtIndExtent[0]++;
        }
      }
      if (curTab == 'p3d') {
        segment.latLngArr2 = segment.latLngArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        segment.elevationArr2 = segment.elevationArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        segment.distanceArr2 = segment.distanceArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        document.getElementById('elevRange').value = elevMult;
        if (isTS && typeof(p3dDrawTSProfile) !== 'undefined') {
          throttled(250, p3dDrawTSProfile());
        } else {
          throttled(100, set3dSegment());
        }
      }
      if (curTab == 'map') {
        setMapFilter()
      }
      if (curTab == 'map3d') {
        segment.latLngArr2 = segment.latLngArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        segment.elevationArr2 = segment.elevationArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        segment.distanceArr2 = segment.distanceArr2Copy.slice(Math.max(0, filtIndExtent[0]), filtIndExtent[1] + 1);
        draw3dmap(!map3ddrawn);
      }
      if (curTab == 'breakdown') {
        drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent)
      }
      if (curTab == 'sv') {
        setMapDetail('G', svMap);
        setGMapDistMarkers();
        setSvExtent(autoZoom);
      }
      if (curTab == 'earth') {
        drawCesiumPath(true);
      }
      setSelectedInfo();
      if (typeof(setFilterDataX) !== 'undefined') {
        setFilterDataX();
      }
      filterTime = new Date().getTime() - startTime;
      filterTimer = null;
    }, Math.min(filterTime * 1.1, 250));
  }
}

function brushmove() {
  d3.selectAll('.splitVal')
    .style('background', null);

  brushExtent = d3.event.target.extent();

  allCharts.filter(function(d, i) {
    return i > 0
  }).forEach(function(d) {
    d.b.extent = [0, 0];
    d.mb.call(d.b);
  })

  filterLocalData();
  setBrushText();
}

function brushend() {
  brushing = false;
  if (d3.event.target.extent()[0] == d3.event.target.extent()[1]) {
    brushExtent = [0, 0];
    filterLocalData();
    //setBrushText();
  }
}

$('a[data-toggle="tab"][href="#mapTab"]').on('shown', function(e) {
  curTab = 'map';
  d3.selectAll('.floatingVVLogo').style('display', 'none');
  setMapChartHeight();
  setMapFilter();
});

$('a[data-toggle="tab"][href="#p3dTab"]').on('shown', function(e) {
  curTab = 'p3d';
  d3.selectAll('.floatingVVLogo').style('display', 'none');
  filterLocalData();
});

$('a[data-toggle="tab"][href="#segmentsTab"]').on('shown', function(e) {
  curTab = 'segments';
  d3.selectAll('.floatingVVLogo').style('display', 'none');
  setScrollingTableSize();
  /*d3.selectAll('.floatingVVLogo').style('display','none');
  if (segmentsLoaded && !isBeta) {
    prepareSegments(segments);
  }
  if (isBeta) {
    filtersFinishedFun();
  }
  d3.select('#typeFlipperDiv').style('display', 'none');*/
  if (!segShown) {
    segShown = true;
    setTimeout(function() {
      $('#collapseFilter').collapse('hide');
      var colBtn = $('#filtersExpander')[0];
      setCollapseState(colBtn);
      d3.select(colBtn).classed('collapsed', true);
      window.setTimeout(sizeChanged, 1000);
    }, 2000);
  }
});

function setMapChartHeight() {
  if ($('#mapChartContainer3').length > 0) {
    $('#mapChartContainer3').height(Math.max(350, window.innerHeight - 274));
    $('#mapDomObj3').height($('#mapChartContainer3').height() - $('#mapOptions3').height() - ($('#div_photo_ex').css('display') == 'none' ? 0 : $('#div_photo_ex').height()));

    /*if ($(window).width() < 768) {} else {
        $('#mainChartContainer2').height($('#mapChartContainer3').height() - 41);
    }*/

    if (typeof(map3) !== 'undefined') {
      map3.updateSize();
    }
  }
}

function sizeChanged() {
  /*if (brushExtent[1] > 0) {
      allCharts[0].b.extent(brushExtent);
      allCharts[0].mb.call(allCharts[0].b);
  }*/

  if (curTab == 'segments') {
    setScrollingTableSize();
  }
  if (curTab == 'map') {
    setMapChartHeight();
    if (typeof(map3) !== 'undefined') {
      map3.zoomToExtent(mapExtent);
    }
  }
  if (curTab == 'map3d') {
    d3.select('#map3d')
      .style('height', (window.innerHeight - document.getElementById('map3d').getBoundingClientRect().top - window.scrollY) + 'px');
  }
  if (curTab == 'weather') {
    d3.select('#weatherTab')
      .style('height', (window.innerHeight - document.getElementById('weatherTab').getBoundingClientRect().top - window.scrollY - 50) + 'px');
  }
  if (curTab == 'earth') {
    d3.select('.cesium-widget canvas').style('height', (window.innerHeight - document.getElementById('earthTab').getBoundingClientRect().top - window.scrollY - 50) + 'px');
  }
  if (curTab == 'p3d') {
    sizep3d();
    if (isTS && typeof(p3dDrawTSProfile) !== 'undefined') {
      p3dDrawTSProfile()
    } else {
      set3dSegment();
    }
  }
  if (curTab == 'breakdown') {
    drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent)
  }
  // temp change
  if (!(window.innerWidth == screen.width && window.innerHeight == screen.height)) {
    $('#svTab').height(Math.max(350, window.innerHeight - document.getElementById('svTab').getBoundingClientRect().top - window.scrollY - 50));
  } else {
    /*if (screen.width < 768 && ) {
        $('#svTab').height(window.innerHeight + 10);
    } else {*/
    $('#svTab').height(Math.max(350, window.innerHeight - document.getElementById('svTab').getBoundingClientRect().top - window.scrollY - 50));
    //}
  }
  if (curTab == 'sv') {
    google.maps.event.trigger(svMap, "resize");
  }
  if (d3.select('#waymarkerContainer').node() != null) {
    d3.select('#waymarkerContainer ol').style('height', Math.max(303, window.innerHeight - 50 - document.getElementById('svTab').getBoundingClientRect().top - window.scrollY - document.getElementById('waymarkerTopSectionDiv').getBoundingClientRect().height - (rhro && !isRaceOrg ? document.getElementById('rhroText').getBoundingClientRect().height : 0)) + 'px');
  }

  if (window.innerWidth < 768) {
    d3.select('#elevChart').style('margin-left', '-15px').style('margin-right', '-15px');
    if (waymarkerAdminVisible) {
      d3.select('#toggleWaymarkers').classed('active', false);
      waymarkerAdminVisible = false;
      setPanoMapWaymarkerSize();
    }
  } else {
    d3.select('#elevChart').style('margin-left', '-10px').style('margin-right', '-10px');
  }
}

var isMobileDevice = false;
var doubleBacks = [];

function localInit() {
  id = route.id;
  route.latLngArr2 = route.streams.filter(function(d) {
    return d.type == 'latlng'
  })[0].data.map(function(d) {
    return {
      lat: d[0],
      lng: d[1]
    }
  });
  route.distanceArr2 = route.streams.filter(function(d) {
    return d.type == 'distance'
  })[0].data;
  route.elevationArr2 = route.streams.filter(function(d) {
    return d.type == 'altitude'
  })[0].data;

  // if "clean" passed on query string then strip out duplicate entries and out'n'backs
  if (getParameterByName('clean') != '' || (loggedInAthleteId == 306128 || loggedInAthleteId == 88661939)) {
    route.latLngArr2Orig = route.latLngArr2.map(function (d, i) { 
      var o = JSON.parse(JSON.stringify(d));
      o.i = i;
      return o;
    });
    var da = [];
    for (var i = 0; i < route.distanceArr2.length - 1; i++) {
      while (route.distanceArr2.length - 1 > i && route.latLngArr2[i].lat == route.latLngArr2[i + 1].lat && route.latLngArr2[i].lng == route.latLngArr2[i + 1].lng) {
        route.latLngArr2.splice(i + 1, 1);
        route.distanceArr2.splice(i + 1, 1);
        route.elevationArr2.splice(i + 1, 1);
        da.push(i + 1);
      }
    }
    console.info('Removed duplicates:');
    console.info(da.map(function(d) {
      return [d, route.distanceArr2[d]]
    }));
    var oa = [];
    for (var i = 2; i < route.distanceArr2.length; i++) {
      if (route.latLngArr2[i].lat == route.latLngArr2[i - 2].lat && route.latLngArr2[i].lng == route.latLngArr2[i - 2].lng) {
        oa.push(i);
      }
    }
    doubleBacks = oa.map(function(d) {
      return [d, route.distanceArr2[d]]
    });
    console.info('Double backs:');
    console.info(doubleBacks);
    if (oa.length > 0 && (getParameterByName('clean') != '' || (loggedInAthleteId == 306128 || loggedInAthleteId == 88661939))) {
      if (getParameterByName('clean') != '' || confirm('Remove ' + oa.length + ' double backs? ' + JSON.stringify(doubleBacks))) {
        while (oa.length > 0) {
          while (oa.length > 0) {
            var i = oa.pop();
            route.latLngArr2.splice(i - 1, 2);
            route.distanceArr2.splice(i - 1, 2);
            route.elevationArr2.splice(i - 1, 2);
            console.info('Removed double back ' + i);
          }
          for (var i = 2; i < route.distanceArr2.length; i++) {
            if (route.latLngArr2[i].lat == route.latLngArr2[i - 2].lat && route.latLngArr2[i].lng == route.latLngArr2[i - 2].lng) {
              oa.push(i);
            }
          }
        }
      } else {
        d3.select('body').append('div').style({
            'float': 'left',
            'margin': '0px 10px'
          }).selectAll('button').data(doubleBacks).enter().append('button').classed('btn btn-mini', true)
          .html(function(d) {
            return fDist(d[1])
          })
          .on('click', function(d) {
            filterDataToDist(d[1] - 100 - kz, d[1] - kz + 100);
          });
      }
    }
    var td = 0;
    route.distanceArr2 = route.latLngArr2.map(function(d, i) {
      if (i == 0) {
        return 0
      }
      var dist = Math.round(distLatLon(route.latLngArr2[i - 1].lat, route.latLngArr2[i - 1].lng, d.lat, d.lng) * 10) / 10;
      td += dist;
      return td
    });
    route.distance = route.distanceArr2.last();
  }

  if (route.id == 0 && typeof(interpolateRoute) !== 'undefined') {
    useNewSegmentMatching = true;
    interpolateRoute();
  }
  
  if (fDateTime(new Date()) > '2024-11-01') {
    var td = 0;
    route.distanceArr2 = route.latLngArr2.map(function(d, i) {
      if (i == 0) {
        return 0
      }
      var dist = Math.round(distLatLon(route.latLngArr2[i - 1].lat, route.latLngArr2[i - 1].lng, d.lat, d.lng) * 10) / 10;
      td += dist;
      return td
    });
    route.distance = route.distanceArr2.last();
  }

  route.streams = [];

  if (typeof(getMainWeather) !== 'undefined' && typeof(raceStages) !== 'undefined') getMainWeather();

  // simplify if on small dvice
  try {
    isMobileDevice = navigator.platform.match(/iPad/i) != null;
    isMobileDevice = isMobileDevice || window.matchMedia("only screen and (max-width: 760px)").matches;
  } catch (e) {}

  if (typeof(raceStages) !== 'undefined' && raceStages.length > 0 && raceStages[0].distance == null) {
    isMobileDevice = false;
  }

  var elevExtent = d3.extent(route.elevationArr2);
  var elevRange = elevExtent[1] - elevExtent[0];
  elevMult = elevMult == 2 ? Math.max(2, Math.min(5, 1000 / elevRange)) : elevMult;

  var di = route.distanceArr2.map(function(d, i) {
      return i == 0 || route.distanceArr2[i - 1] != d ? 0 : i
    })
    .filter(function(d) {
      return d > 0
    });
  route.distanceArr2.removeIndexes(di);
  route.elevationArr2.removeIndexes(di);
  route.latLngArr2.removeIndexes(di);

  /*var latScale = d3.scale.linear()
      .domain(route.distanceArr2)
      .range(route.latLngArr2.map(function(d) { return d.lat }));
  var lngScale = d3.scale.linear()
      .domain(route.distanceArr2)
      .range(route.latLngArr2.map(function(d) { return d.lng }));
  var elvScale = d3.scale.linear()
      .domain(route.distanceArr2)
      .range(route.elevationArr2.map(function(d) { return d }));
  for (var i = 1; i < route.distanceArr2.length; i++) {
      var d = route.distanceArr2[i - 1];
      var dd = route.distanceArr2[i] - d;
      var dd10 = dd / 20;
      if (dd10 > 2) {
          var t = i + Math.floor(dd10) - 1;
          while (i < t) {
              d += 20;
              route.distanceArr2.splice(i, 0, d);
              route.latLngArr2.splice(i, 0, { lat: latScale(d), lng: lngScale(d) });
              route.elevationArr2.splice(i, 0, elvScale(d));
              i++;
          }
      }
  }*/

  /*var smoothed = filterData(route.distanceArr2.map(function(d, i) {
      return {
          x: d,
          y: route.elevationArr2[i]
      }
  }));
  route.elevationArr2 = smoothed.map(function(d) {
      return d.ySmooth
  });*/

  route.segments.forEach(function(d) {
    d.if = d.starred ? 1 : 0;
    delete d.starred;
  });
  route.segments = route.segments.filter(function(d, i) {
    return route.segments.slice(0, i).map(function(e) {
      return +e.id
    }).indexOf(+d.id) == -1
  });
  segments = clone(route.segments);
  if (typeof(initRoute) !== 'undefined') {
    initRoute();
  }

  // simplify after laps added
  if (false && isMobileDevice) {
    var s = simplifyMaxCount(route.latLngArr2.map(function(d, i) {
      return {
        x: d.lng,
        y: d.lat,
        z: route.elevationArr2[i],
        d: route.distanceArr2[i],
        i: i
      }
    }), 2000);
    route.latLngArr2 = s.map(function(d) {
      return {
        lat: d.y,
        lng: d.x
      };
    });
    route.distanceArr2 = s.map(function(d) {
      return d.d;
    });
    route.elevationArr2 = s.map(function(d) {
      return d.z;
    });
    s = null;
    filtIndExtent = [0, route.latLngArr2.length - 1];
  }

  route.bearingArr = route.latLngArr2.map(function(d, i) {
    var ll0 = i == 0 ? route.latLngArr2[0] : route.latLngArr2[i - 1];
    var ll1 = i == 0 ? route.latLngArr2[1] : route.latLngArr2[i];
    return bearing(ll0.lat, ll0.lng, ll1.lat, ll1.lng);
  });

  var smoothed = filterData(route.distanceArr2.map(function(d, i) {
    return {
      x: d,
      y: route.elevationArr2[i]
    }
  }), 5);
  route.elevationArr2 = smoothed.map(function(d) {
    return d.ySmooth
  });

  route.distance = route.distanceArr2.last();
  route.elevationGainArr = [];
  for (var i = 0; i < route.elevationArr2.length; i++) {
    route.elevationGainArr.push(i == 0 ? 0 : Math.max(0, route.elevationArr2[i] - route.elevationArr2[i - 1]));
  }
  route.elevation_gain = d3.sum(route.elevationGainArr);

  if (!isMobileDevice && typeof(raceStage) !== 'undefined' && typeof(saveRouteStats) !== 'undefined') {
    saveRouteStats();
  }

  d3.select('#elevChart svg')
    .style('height', (89 + (typeof(elevChartMarginTop) === 'undefined' ? 5 : elevChartMarginTop)) + 'px');

  getConfigs();

  // calc gradients

  /*allCharts.push({
    p: d3.select("#elevChart"),
    yd: elevObj,
    h: 75,
    m: [5, 10, 14, 50]
  });*/
  setMapChartHeight();
  //initMap('3');

  d3.select('#myTabs')
    .append('li').classed('hidden-phone', true).html('<a href="#breakdownTab" data-toggle="tab">Breakdown</a>');

  d3.select('.tab-content')
    .append('div')
    .attr({
      class: 'tab-pane',
      id: 'breakdownTab'
    });

  $('a[data-toggle="tab"][href="#breakdownTab"]').on('shown', function(e) {
    curTab = 'breakdown';
    d3.selectAll('.floatingVVLogo').style('display', 'none');
    drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent)
  });

  setup3dmap();

  /*
  mapDataLayer = new OpenLayers.Layer.Vector("data");
  map3.addLayer(mapDataLayer);
  setExtent();*/
  a = segments;
  for (var i = 0; i < route.latLngArr2.length; i++) {
    var e = route.latLngArr2[i];
    for (var j = 0; j < a.length; j++) {
      if (i == 0) {
        a[j].sd = [];
        a[j].ed = [];
      }
      if (typeof(a[j].start_latlng) !== 'undefined') {
        var sd = distLatLon(e.lat, e.lng, a[j].start_latlng[0], a[j].start_latlng[1]);
        var ed = distLatLon(e.lat, e.lng, a[j].end_latlng[0], a[j].end_latlng[1]);
        if (sd < 75) {
          a[j].sd.push({
            i: i,
            d: sd
          });
        }
        if (ed < 75) {
          a[j].ed.push({
            i: i,
            d: ed
          });
        }
      }
    }
  }
  a.forEach(function(d) {
    d.si = d.id;
    d.starts = [];
    d.ends = [];
    d.sd.sort(function(a, b) {
      return a.d - b.d
    });
    d.sd.forEach(function(e) {
      if (d3.merge(d.starts.map(function(f) {
          return d3.range(f.i - 30, f.i + 30)
        })).indexOf(e.i) == -1) {
        d.starts.push(e)
      }
    })
    d.ed.sort(function(a, b) {
      return a.d - b.d
    });
    d.ed.forEach(function(e) {
      if (d3.merge(d.ends.map(function(f) {
          return d3.range(f.i - 30, f.i + 30)
        })).indexOf(e.i) == -1) {
        d.ends.push(e)
      }
    })
    d.starts.sort(function(a, b) {
      return a.i - b.i
    })
    d.ends.sort(function(a, b) {
      return a.i - b.i
    })
    d.starts.map(function(e) {
      var o = {};
      o.s = e;
      e.end = clone(d.ends).filter(function(f) {
        f.dist = route.distanceArr2[f.i] - route.distanceArr2[e.i];
        f.diff = d.distance - f.dist;
        f.diffP = f.diff / d.distance;
        return f.i > e.i && f.dist > d.distance * 0.8 && f.dist < d.distance * 1.2
      });
    })

    d.starts = d.starts.filter(function(e) {
      return e.end.length > 0
    });

    d.starts.forEach(function(e) {
      e.end.sort(function(a, b) {
        return a.diff - b.diff
      })
    });

    if (d.starts.length > 0) {
      d.startIndex = {
        d: d.starts[0].d,
        i: d.starts[0].i
      };
      d.endIndex = {
        d: d.starts[0].end[0].d,
        i: d.starts[0].end[0].i
      };
      d.distIntoRoute = route.distanceArr2[d.startIndex.i];
      d.distance = route.distanceArr2[d.endIndex.i] - route.distanceArr2[d.startIndex.i];
    }
    d.k = 0;

    d.b = d.start_latitude ? bearing(d.start_latitude, d.start_longitude, d.end_latitude, d.end_longitude) : 0;
  });

  a = a.filter(function(d) {
    return typeof(d.distIntoRoute) !== 'undefined'
  });

  var r = 1;
  var ra = [];
  while (a.filter(function(d) {
      return d.starts.length > r && d.ends.length >= d.starts.length
    }).length > 0) {
    ra = d3.merge([ra, a.filter(function(d) {
      return d.starts.length > r && d.ends.length >= d.starts.length
    }).map(function(d) {
      var o = clone(d);
      o.name = d.name + ' (' + r + ')';
      o.startIndex = {
        d: d.starts[r].d,
        i: d.starts[r].i
      };
      o.endIndex = {
        d: d.starts[r].end[0].d,
        i: d.starts[r].end[0].i
      };
      o.distIntoRoute = route.distanceArr2[o.startIndex.i];
      o.distance = route.distanceArr2[o.endIndex.i] - route.distanceArr2[o.startIndex.i];
      return o;
    })]);
    r++;
  }
  a = d3.merge([a, ra]);

  /*a = d3.merge([a, a.filter(function(d) {
      return d.starts.length > 1 && d.ends.length == d.starts.length
  }).map(function(d) {
      var o = clone(d);
      o.name = d.name + ' (1)';
      o.startIndex = {
          d: d.starts[1].d,
          i: d.starts[1].i
      };
      o.endIndex = {
          d: d.starts[1].end[0].d,
          i: d.starts[1].end[0].i
      };
      o.distIntoRoute = route.distanceArr2[o.startIndex.i];
      o.distance = route.distanceArr2[o.endIndex.i] - route.distanceArr2[o.startIndex.i];
      return o;
  })]);*/

  var segStats = {};
  if (typeof(route.segmentstats) === 'undefined') {
    route.segmentstats = [];
  }
  route.segmentstats.forEach(function(d) {
    segStats[+d.i] = d;
  });

  a.sort(function(a, b) {
    return a.startIndex.i == b.startIndex.i ? a.endIndex.i - b.endIndex.i : a.startIndex.i - b.startIndex.i
  });
  a.forEach(function(d, ai) {
    var sd = d.sd.filter(function(e) {
      return e.end && e.end.length > 0
    });
    if (sd.length > 0) {
      var oldMaxG = d.maximum_grade;
      d.elevation_gain = 0;
      d.maximum_grade = -10000;
      d.elevation_high = -10000;
      d.elevation_low = 100000;
      var lastI = sd[0].i;
      var startI = sd[0].i;
      var endI = sd[0].end[0].i;
      for (i = startI; i < endI; i++) {
        d.elevation_gain += Math.max(0, route.elevationArr2[i + 1] - route.elevationArr2[i]);
        if (route.distanceArr2[i + 1] - route.distanceArr2[i] > 4) {
          d.maximum_grade = Math.max(d.maximum_grade, 100 * (route.elevationArr2[i + 1] - route.elevationArr2[lastI]) / (route.distanceArr2[i + 1] - route.distanceArr2[lastI]));
          lastI = i + 1;
        }
        d.elevation_high = Math.max(d.elevation_high, route.elevationArr2[i]);
        d.elevation_low = Math.min(d.elevation_low, route.elevationArr2[i]);
        if (i == endI - 1) {
          d.elevation_high = Math.max(d.elevation_high, route.elevationArr2[i + 1]);
          d.elevation_low = Math.min(d.elevation_low, route.elevationArr2[i + 1]);
        }
      }
      d.maximum_grade = Math.min(d.maximum_grade, oldMaxG);
      d.average_grade = 100 * (route.elevationArr2[sd[0].end[0].i] - route.elevationArr2[sd[0].i]) / (route.distanceArr2[sd[0].end[0].i] - route.distanceArr2[sd[0].i]);
    }
    d.ed = d.elevation_high - d.elevation_low;
    d.sn = d.name;
    d.num = ai + 1;
    switch (parseInt(d.climb_category)) {
      case 5:
        d.cc = " HC";
        break;
      case 4:
        d.cc = "1st";
        break;
      case 3:
        d.cc = "2nd";
        break;
      case 2:
        d.cc = "3rd";
        break;
      case 1:
        d.cc = "4th";
        break;
      default:
        d.cc = "n/a";
        break;
    }
    if (typeof(setOtherVals) !== 'undefined') {
      setOtherVals(d);
    }

    d.ket = 0;
    d.kc = 0;
    d.qet = 0;
    d.qc = 0;
    if (typeof(segStats[d.id]) !== 'undefined') {
      d.ket = +segStats[d.id]['kt'];
      d.kc = +segStats[d.id]['kc'];
      d.qet = +segStats[d.id]['qt'];
      d.qc = +segStats[d.id]['qc'];
    }
    d.kv = 0;
    d.krp = 0;
    d.kas = 0;
    d.qv = 0;
    d.qrp = 0;
    d.qas = 0;
    if (d.ket > 0) {
      if (d.cc != 'n/a') {
        d.gf = 2 + d.average_grade / 10;

        d.kv = d.ed * 3600 / d.ket;
        d.krp = d.kv / (d.gf * 100);
        d.qv = d.ed * 3600 / d.qet;
        d.qrp = d.qv / (d.gf * 100);
      }
      d.kas = d.distance / d.ket;
      d.qas = d.distance / d.qet;
    }
  })
  rowCount = 10000;
  setConfig();
  init();
  initScrollingTable(d3.select('.tableWrapper.st'), function() {
    var availableHeight = parseInt(window.innerHeight) - this.getBoundingClientRect().top - 151 - pageYOffset;
    if (availableHeight > parseInt(d3.select(this).select('.st-body table').style('height'))) {
      return null;
    }
    return Math.max(280, availableHeight) + 'px';
  });

  d3.select('#myTabs')
    .insert("li", ":first-child")
    .classed('active', true)
    .html('<a href="#svTab" data-toggle="tab">Map</a>');
  d3.select('.tab-content')
    .append('div')
    .attr({
      class: 'tab-pane in active',
      id: 'svTab'
    });
  $('a[data-toggle="tab"][href="#svTab"]').on('shown', function(e) {
    curTab = 'sv';
    $('#svTab').height(Math.max(350, window.innerHeight - document.getElementById('svTab').getBoundingClientRect().top - 50));
    d3.selectAll('.floatingVVLogo').style('display', 'none');
    loadstreetview();
    setMapDetail('G', svMap);
    setSvExtent();
    sizeChanged();
  });

  var grad = (route.elevationArr2.last() - route.elevationArr2[0]) / route.distance;
  d3.select('#myTabs')
    .append('span')
    .attr('id', 'selectedInfoPanel')
    .attr('title', 'Selection distance, elevation gain and gradient (from start to end of selection)')
    .attr('style', 'font-weight: 500; float: right; padding: 0px 5px !important')
    .html(fDist(route.distance, 3) + ' ' + fSmlDist(d3.sum(route.elevationGainArr)) + ' ' + f1dp(grad * 100) + ' %');

  //Make sure elevation is not below sea level
  var emin = d3.min(route.elevationArr2);
  if (emin < 1) {
    for (var i = 0; i < route.elevationArr2.length; i++) {
      route.elevationArr2[i] += 1 - emin;
    }
  }

  setupLatLngElv();
  curTab = 'sv';
  $('#svTab').height(Math.max(350, window.innerHeight - document.getElementById('svTab').getBoundingClientRect().top - 50));
  d3.selectAll('.floatingVVLogo').style('display', 'none');
  loadstreetview();
  setMapDetail('G', svMap);
  d3.select('#elevChart').style('visibility','hidden');
  checkForGoogleMapLoaded();
  elevChartFill = d3.selectAll('#elevChart svg').append("svg:defs")
    .append('pattern')
    .attr("id", "elevFillGrad")
    .attr({
      'x': '0',
      'y': '0',
      'width': '1',
      'height': '1',
      'patternUnits': 'objectBoundingBox'
    });
  elevChartFill.append('image').attr({
    x: '0',
    y: '0',
    width: elevBackgroundSize,
    height: elevBackgroundSize
  });

  var elevBackground = d3.select('#elevChart').append('svg').attr({
    id: 'elevBackground',
    height: elevBackgroundSize,
    width: elevBackgroundSize
  }).style({
    height: elevBackgroundSize + 'px',
    width: elevBackgroundSize + 'px'
  });
  elevBackground.append('defs').append('pattern').attr({
    id: 'elevBackgroundGrad',
    x: '0',
    y: '0',
    width: '1',
    height: '1'
  });
  elevBackground.append('rect').style({
    height: elevBackgroundSize + 'px',
    width: elevBackgroundSize + 'px',
    fill: 'url("#elevBackgroundGrad")'
  })
  route.totalDistance = route.distanceArr2Copy.last() - route.distanceArr2Copy[0];
  d3.select('#elevBackgroundGrad').selectAll('rect').data(gMapFullGradArr).enter().append('rect')
    .attr({
      y: '0',
      height: elevBackgroundSize,
      fill: function(d) {
        return d.c
      },
      x: function(d, i) {
        return (i == 0 ? 0 : 100 * (gMapFullGradArr[i - 1].x - route.distanceArr2Copy[0]) / route.totalDistance) + '%'
      },
      width: function(d, i) {
        return (i == 0 ? 100 * (d.x - route.distanceArr2Copy[0]) / route.totalDistance : 100 * (d.x - gMapFullGradArr[i - 1].x) / route.totalDistance) + '%'
      }
    });

  var svgElement = elevBackground.node();
  var svgString = new XMLSerializer().serializeToString(svgElement);
  svgElement.remove();
  var canvas = d3.select('#elevChart').append('canvas').node();
  canvas.width=elevBackgroundSize;
  canvas.height=elevBackgroundSize;
  canvas.style.display = 'none';

  var ctx = canvas.getContext("2d");
  var blob = new Blob([svgString], {
    type: 'image/svg+xml'
  });
  var URL = window.URL || window.webkitURL || window;
  var blobURL = URL.createObjectURL(blob);
  var image = new Image();
  image.onload = function() {
    ctx.drawImage(image, 0, 0, elevBackgroundSize, elevBackgroundSize);
    var png = canvas.toDataURL("image/png");
    d3.select('#elevFillGrad image')
      .attr('href', png);
    URL.revokeObjectURL(blobURL);
    canvas.remove();
  };
  image.onerror = function(a,b,c) {
    console.info('image load error');
  }
  image.src = blobURL;

  /*  elevChartFill.attr("id", "elevFillGrad")
      .attr({
        'x': '0',
        'y': '0',
        'width': '1',
        'height': '1',
        'patternUnits': 'objectBoundingBox'
      })
      .selectAll('rect')
      .data(gMapFullGradArr)
      .enter()
      .append('rect')
      .attr({
        'y': '0',
        'height': '100%'
      })
      .attr('fill', function(d) {
        return d.c
      });*/
  //.attr('stroke-opacity', 0);
  /*.append("svg:linearGradient");
  elevChartFill.attr("id", "elevFillGrad")
      .attr("x1", "0%")
      .attr("y1", "0%")
      .attr("x2", "100%")
      .attr("y2", "0%")
      .attr("spreadMethod", "pad")
      .selectAll('stop')
      .data(gMapFullGradArr)
      .enter()
      .append('stop')
      .attr('offset', function(d) {
          return (100 * (d.x + kz) / route.distance) + '%'
      })
      .attr('stop-color', function(d) {
          return d.c
      })
      .attr('stroke-opacity', 1);*/

  if (!isMobileDevice) {
    d3.select('#myTabs')
      .append('li').classed('hidden-phone', true).html('<a href="#earthTab" data-toggle="tab">Earth</a>');

    d3.select('.tab-content')
      .append('div')
      .style({
        margin: '-10px -10px -0px -10px',
        position: 'relative'
      })
      .attr({
        class: 'tab-pane',
        id: 'earthTab'
      })
      .append('div').attr('id', 'cesiumContainer');

    $('a[data-toggle="tab"][href="#earthTab"]').on('shown', function(e) {
      curTab = 'earth';
      d3.selectAll('.floatingVVLogo').style('display', 'none');

      drawCesiumPath(false);
    });
  } else {
    Cesium = null;
  }
  /*
      < stop offset = "0%"
      stop - color = "#00ff00"
      stop - opacity = "1" > < /stop>
  */
  setSvExtent();
  drawElevationChart();
  nv.utils.windowResize(sizeChanged);
  //$('#filterAcc').collapse();
  if (typeof(initRoute2) !== 'undefined') {
    initRoute2();
  }

}

function checkForGoogleMapLoaded() {
  if (d3.select('.gm-style-mtc').node() == null) {
    window.setTimeout(checkForGoogleMapLoaded, 100);
  } else {
    if (typeof(filterWaymarkerList) !== 'undefined') filterWaymarkerList();
    d3.select('#elevChart').style('visibility',null);
    if (typeof(showParkFeedTechButton) !== 'undefined') showParkFeedTechButton();
    if (typeof(zoomToWaymarker) !== 'undefined') zoomToWaymarker();
  }
}

function filtersFinishedFun() {
  initScrollingTable(d3.select('.tableWrapper.st'), function() {
    var availableHeight = parseInt(window.innerHeight) - this.getBoundingClientRect().top - 151 - pageYOffset;
    if (availableHeight > parseInt(d3.select(this).select('.st-body table').style('height'))) {
      return null;
    }
    return Math.max(280, availableHeight + 120) + 'px';
  });

  d3.selectAll('.st-head table').style('border-top-width', '0px');
  d3.selectAll('.st td:nth-child(1), .st th:nth-child(1)').style('border-left-width', '0px');

  setScrollingTableSize();

  d3.selectAll('#dataTable tbody tr')
    .classed('success', function(d) {
      return d._pr == 1
    })
    .on('click', function(d) {
      brushExtent = [d.distIntoRoute, d.distIntoRoute + d.distance];
      elevBrush.extent(brushExtent);
      d3.select('#elevChart svg .brush').call(elevBrush);
      filterLocalData();
    })
    .on('mouseover', function(d) {
      d3.select('#areaHighlight').style('display', null);
      d3.select('#areaHighlight')
        .attr("x", elevChart.xScale()(d.distIntoRoute))
        .attr("width", elevChart.xScale()(d.distance - kz));
    })
    .on('mouseout', function(d) {
      d3.select('#areaHighlight').style('display', 'none');
    });

  d3.selectAll('.tableWrapper').each(function() {
    var a = d3.select(this).select('.st-body').node();
    if (a != null) {
      d3.select(this).select('.st-head').node().scrollLeft = a.scrollLeft;
    }
  });
}

var columns = [{
    id: 0,
    title: 'Order',
    desc: 'Segment order',
    lngDesc: 'The segment order on the activity.',
    width: 'auto',
    class: 'ar',
    field: 'num',
    type: 'num',
    display: 'int',
    hideZeros: 0,
    defOrder: 'asc'
  }, {
    id: 31,
    title: 'Distance into<br/>route ' + lrgLenUnit,
    desc: 'Distance into route',
    lngDesc: 'The distance in to the route where the segment starts.',
    width: 'auto',
    class: '',
    field: 'distIntoRoute',
    type: 'num',
    display: 'lLen',
    hideZeros: 0,
    defOrder: 'desc'
  }, {
    id: 37,
    title: '★',
    desc: 'Starred',
    lngDesc: 'Starred',
    width: 'auto',
    class: '',
    field: 'if',
    type: 'list',
    display: 'list',
    hideZeros: 0,
    defOrder: 'asc'
  }, {
    id: 1,
    title: 'Name',
    desc: 'Segment name',
    lngDesc: 'The segment\'s name.',
    width: '200px',
    class: 'clipText',
    field: 'sn',
    type: 'text',
    display: 'text',
    hideZeros: 0,
    defOrder: 'asc'
  }, {
    id: 13,
    title: 'Dist<br/>' + lrgLenUnit,
    desc: 'Distance',
    lngDesc: 'The length of the segment.',
    width: 'auto',
    class: '',
    field: 'distance',
    type: 'num',
    display: 'lLen',
    hideZeros: 0,
    defOrder: 'desc'
  }, {
    id: 19,
    title: 'Cat',
    desc: 'Climb category',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'cc',
    type: 'list',
    display: 'list',
    hideZeros: 0,
    defOrder: 'asc'
  }, {
    id: 14,
    title: 'Max Elv<br/>' + smlLenUnit,
    desc: 'Max elevation',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'elevation_high',
    type: 'num',
    display: 'sLen',
    hideZeros: 0,
    defOrder: 'desc'
  }, {
    id: 23,
    title: 'Grade<br/>%',
    desc: 'Grade %',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'average_grade',
    type: 'num',
    display: '1dp',
    hideZeros: 0,
    defOrder: 'desc'
  }, {
    id: 24,
    title: 'Max Grade<br/>%',
    desc: 'Max Grade %',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'maximum_grade',
    type: 'num',
    display: '1dp',
    hideZeros: 0,
    defOrder: 'desc'
  }
  /*, {
      id: 25,
      title: 'Elv gain<br/>' + smlLenUnit,
      desc: 'Elevation gain',
      lngDesc: '',
      width: 'auto',
      class: '',
      field: 'eg',
      type: 'num',
      display: 'sLen',
      hideZeros: 0,
      defOrder: 'desc'
  }*/
  , {
    id: 26,
    title: 'Elv Gain<br/>' + smlLenUnit,
    desc: 'Elevation gain',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'elevation_gain',
    type: 'num',
    display: 'sLen',
    hideZeros: 0,
    defOrder: 'desc'
  },
  {
    id: 27,
    title: 'QOM<br/>Time',
    desc: 'QOM time',
    lngDesc: 'The elapsed time (including any stops) of the QOM.',
    width: 'auto',
    class: '',
    field: 'qet',
    type: 'num',
    display: 'lTime',
    hideZeros: 1,
    defOrder: 'asc'
  },
  {
    id: 28,
    title: 'QOM Speed<br/>' + speedUnit,
    desc: 'QOM speed',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'qas',
    type: 'num',
    display: 'speed',
    hideZeros: 1,
    defOrder: 'desc'
  },
  {
    id: 29,
    title: 'QOM<br/>VAM',
    lngDesc: '',
    desc: 'QOM VAM',
    width: 'auto',
    class: 'ar',
    field: 'qv',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
  },
  {
    id: 30,
    title: 'QOM<br/>RP',
    desc: 'QOM RP W/kg',
    lngDesc: '',
    width: 'auto',
    class: 'ar',
    field: 'qrp',
    type: 'num',
    display: '2dp',
    hideZeros: 1,
    defOrder: 'desc'
  },
  {
    id: 40,
    title: 'KOM<br/>Time',
    desc: 'KOM time',
    lngDesc: 'The elapsed time (including any stops) of the KOM.',
    width: 'auto',
    class: '',
    field: 'ket',
    type: 'num',
    display: 'lTime',
    hideZeros: 1,
    defOrder: 'asc'
  },
  {
    id: 41,
    title: 'KOM Speed<br/>' + speedUnit,
    desc: 'KOM speed',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'kas',
    type: 'num',
    display: 'speed',
    hideZeros: 1,
    defOrder: 'desc'
  },
  {
    id: 42,
    title: 'KOM<br/>VAM',
    lngDesc: '',
    desc: 'KOM VAM',
    width: 'auto',
    class: 'ar',
    field: 'kv',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
  },
  {
    id: 43,
    title: 'KOM<br/>RP',
    desc: 'KOM RP W/kg',
    lngDesc: '',
    width: 'auto',
    class: 'ar',
    field: 'krp',
    type: 'num',
    display: '2dp',
    hideZeros: 1,
    defOrder: 'desc'
  }, {
    id: 33,
    title: 'Bearing',
    desc: 'Bearing',
    lngDesc: '',
    width: 'auto',
    class: 'ar',
    field: 'b',
    type: 'num',
    display: 'arrow',
    hideZeros: 0,
    defOrder: 'asc'
  },
  {
    id: 32,
    title: 'Total<br/>Athletes',
    desc: 'Total athletes',
    lngDesc: 'The number of athletes to have completed this segment',
    width: 'auto',
    class: 'ar',
    field: 'kc',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    s_s: (isPRO ? 0 : 1),
    defOrder: 'desc'
  }
];

var confCookie = 'rcnf';
var confType = 1;
/*mapShownChecked = true;
tableShownChecked = true;
chartShownChecked = false;*/
//var defColOrder = "0,10,20,30,40".split(',');
var defColFilters = "37:All";

function getConfigs() {
  c = [{
    "id": "1",
    "athleteId": "-1",
    "type": "1",
    "name": "All",
    "colOrder": columns.map(function(d) {
      return d.id
    }).join(),
    "dataOrder": "0:0",
    "editable": false,
    "temp": false,
    "filters": defColFilters
  }];
  curConfig = getCookie(confCookie);
  if (typeof(curConfig) == 'undefined') {
    curConfig = 0;
  }
}

/*  This work is licensed under Creative Commons GNU LGPL License.

    License: http://creativecommons.org/licenses/LGPL/2.1/
   Version: 0.9
    Author:  Stefan Goessner/2006
    Web:     http://goessner.net/
*/
function parseXml(xml) {
  var dom = null;
  if (window.DOMParser) {
    try {
      dom = (new DOMParser()).parseFromString(xml, "text/xml");
    } catch (e) {
      dom = null;
    }
  } else if (window.ActiveXObject) {
    try {
      dom = new ActiveXObject('Microsoft.XMLDOM');
      dom.async = false;
      if (!dom.loadXML(xml)) // parse error ..

        window.alert(dom.parseError.reason + dom.parseError.srcText);
    } catch (e) {
      dom = null;
    }
  } else
    alert("cannot parse xml string!");
  return dom;
}

function xml2json(xml) {
  var X = {
    toObj: function(xml) {
      var o = {};
      if (xml.nodeType == 1) { // element node ..
        if (xml.attributes.length) // element with attributes  ..
          for (var i = 0; i < xml.attributes.length; i++)
            o["_" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue ? X.escape(xml.attributes[i].nodeValue) : "").toString();
        if (xml.firstChild) { // element has child nodes ..
          var textChild = 0,
            cdataChild = 0,
            hasElementChild = false;
          for (var n = xml.firstChild; n; n = n.nextSibling) {
            if (n.nodeType == 1) hasElementChild = true;
            else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
            else if (n.nodeType == 4) cdataChild++; // cdata section node
          }
          if (hasElementChild) {
            if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
              X.removeWhite(xml);
              for (var n = xml.firstChild; n; n = n.nextSibling) {
                if (n.nodeType == 3) // text node
                  o["__text"] = X.escape(n.nodeValue);
                else if (n.nodeType == 4) // cdata node
                  o = X.escape(n.nodeValue);
                else if (o[n.nodeName]) { // multiple occurence of element ..
                  if (o[n.nodeName] instanceof Array)
                    o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
                  else
                    o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
                } else // first occurence of element..
                  o[n.nodeName] = X.toObj(n);
              }
            } else { // mixed content
              if (!xml.attributes.length)
                o = X.escape(X.innerXml(xml));
              else
                o["__text"] = X.escape(X.innerXml(xml));
            }
          } else if (textChild) { // pure text
            if (!xml.attributes.length)
              o = X.escape(X.innerXml(xml));
            else
              o["__text"] = X.escape(X.innerXml(xml));
          } else if (cdataChild) { // cdata
            if (cdataChild > 1)
              o = X.escape(X.innerXml(xml));
            else
              for (var n = xml.firstChild; n; n = n.nextSibling)
                o = X.escape(n.nodeValue);
          }
        }
        if (!xml.attributes.length && !xml.firstChild) o = null;
      } else if (xml.nodeType == 9) { // document.node
        o = X.toObj(xml.documentElement);
      } else if (xml.nodeType == 8) { // comment.node
      } else
        alert("unhandled node type: " + xml.nodeType);
      return o;
    },
    toJson: function(o, name, ind) {
      var json = name ? ("\"" + name + "\"") : "";
      if (o instanceof Array) {
        for (var i = 0, n = o.length; i < n; i++)
          o[i] = X.toJson(o[i], "", ind + "\t");
        json += (name ? ":[" : "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
      } else if (o == null)
        json += (name && ":") + "null";
      else if (typeof(o) == "object") {
        var arr = [];
        for (var m in o)
          arr[arr.length] = X.toJson(o[m], m, ind + "\t");
        json += (name ? ":{" : "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
      } else if (typeof(o) == "string")
        json += (name && ":") + "\"" + o.toString() + "\"";
      else
        json += (name && ":") + o.toString();
      return json;
    },
    innerXml: function(node) {
      var s = ""
      if ("innerHTML" in node)
        s = node.innerHTML;
      else {
        var asXml = function(n) {
          var s = "";
          if (n.nodeType == 1) {
            s += "<" + n.nodeName;
            for (var i = 0; i < n.attributes.length; i++)
              s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
            if (n.firstChild) {
              s += ">";
              for (var c = n.firstChild; c; c = c.nextSibling)
                s += asXml(c);
              s += "</" + n.nodeName + ">";
            } else
              s += "/>";
          } else if (n.nodeType == 3)
            s += n.nodeValue;
          else if (n.nodeType == 4)
            s += "<![CDATA[" + n.nodeValue + "]]>";
          return s;
        };
        for (var c = node.firstChild; c; c = c.nextSibling)
          s += asXml(c);
      }
      return s;
    },
    escape: function(txt) {
      return txt.replace(/[\\]/g, "\\\\")
        .replace(/[\"]/g, '\\"')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r');
    },
    removeWhite: function(e) {
      e.normalize();
      for (var n = e.firstChild; n;) {
        if (n.nodeType == 3) { // text node
          if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
            var nxt = n.nextSibling;
            e.removeChild(n);
            n = nxt;
          } else
            n = n.nextSibling;
        } else if (n.nodeType == 1) { // element node
          X.removeWhite(n);
          n = n.nextSibling;
        } else // any other node
          n = n.nextSibling;
      }
      return e;
    }
  };
  if (xml.nodeType == 9) // document node
    xml = xml.documentElement;
  var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
  return "{" + json.replace(/\t|\n/g, "") + "}";
}

function overlayData(xmlData) {
  xmlDataObj = getLatLngFromXML(xmlData);

  var idArray = getParameterByName('a') == '' ? null : getParameterByName('a').split(',').map(function(d) {
    if (d.indexOf('|') > -1) {
      return d.split('|').map(function(e) {
        return e
      });
    }
    return d;
  });
  var la = xmlDataObj.routes;
  if (la.length > 1 && idArray === null) {
    console.info(la.map(function (d) { return d.name }))
    if (la.length > 1 && viewAdmin) {
      var promptText = prompt(la.map(function (d, i) { return i + ': ' +d.name }).join('\n') + '\nProvide comma-delimited of zero-based indexes to use e.g. 4,1\nAdd a minus sign to reverse lat/lngs e.g. -0,1\nTo set start lat/lng index (to remove overlaps) append |i to the track index e.g. 4|5');
      if (promptText != '') {
        idArray = promptText.split(',').map(function(d) {
          if (d.indexOf('|') > -1) {
            return d.split('|').map(function(e) {
              return e
            });
          }
          return d;
        });
      }
    } else {
      // if distance between end of route 0 and start of route 1 is greater than 200 m and reversing is closer then reverse order
      if (distLatLon(la[0].lle.last().lat, la[0].lle.last().lng, la[1].lle[0].lat, la[1].lle[0].lng) > 200 &&
        distLatLon(la[1].lle.last().lat, la[1].lle.last().lng, la[0].lle[0].lat, la[0].lle[0].lng) < distLatLon(la[0].lle.last().lat, la[0].lle.last().lng, la[1].lle[0].lat, la[1].lle[0].lng)) {
        console.info('route order swapped');
        if (viewAdmin) {
          alert('Route order swapped (' + la.map(function (d) { return d.name }).join(', ') +'). Total of '+la.length+' routes found.');
        }
        idArray = [1, 0];
      }
    }
  }
  if (idArray != null) {
    idArray.forEach(function(d, i) {
      var val = d;
      if (typeof(d) == 'object') {
        val = d[0];
        la[val].lle = la[val].lle.slice(+d[1]);
      }
      if (val.substr(0,1) == '-') {
        la[Math.abs(+val)].lle = la[Math.abs(+val)].lle.reverse();
      }
      la[Math.abs(+val)].order = i;
    });
    la = la.filter(function(d, i) {
      return typeof(d.order) !== 'undefined';
    });
    la.sort(function(a, b) {
      return a.order - b.order
    });
  }
  route.name = xmlDataObj.name != '' ? xmlDataObj.name : (la && la.length && la[0].name ? la[0].name : 'Route 1');
  d3.select('h1').html(route.name);

  var lle = d3.merge(la.map(function(d) {
    return d.lle
  }));

  // remove duplicates
  lle = lle.filter(function(d,i){ return i == 0 || (d.lat != lle[i-1].lat || d.lng != lle[i-1].lng) });

  route.streams = [];
  route.streams.push({
    type: 'latlng',
    data: lle.map(function(d) {
      return [d.lat, d.lng];
    })
  });
  route.streams.push({
    type: 'altitude',
    data: lle.map(function(d) {
      return d.ele;
    })
  });
  var td = 0;
  route.streams.push({
    type: 'distance',
    data: lle.map(function(d, i) {
      if (i == 0) {
        return 0
      }
      td += distLatLon(lle[i - 1].lat, lle[i - 1].lng, d.lat, d.lng);
      return td
    })
  });
  route.distance = td;
  route.segments = [];
  d3.selectAll('#myTabs, #elevChart, .tab-content').style('display', null);
  d3.select('#loadGPXContainer').style('display', 'none');

  // reset display if already set up
  if (typeof(elevChart) !== 'undefined') {
    d3.select('#elevChart').html('');
    elevChart = undefined;
    gmapDetailPolylines.forEach(function(d) {
      d.setMap(null);
    });
    fgSVPoly.setMap(null);
    bgSVPoly.setMap(null);
    hlSVPoly.setMap(null);
    gMapMarkerArr2.forEach(function(d) {
      d[0].setMap(null);
      d[1].setMap(null);
    });
    gmapMarkers.startMap.setMap(null);
    gmapMarkers.endMap.setMap(null);
    gmapMarkers.startPano.setMap(null);
    gmapMarkers.endPano.setMap(null);
  }
  localInit();
}

if (+route.id == 0) {
  d3.selectAll('#myTabs, #elevChart, .tab-content').style('display', 'none');
  if (isPRO) {
    var o = d3.select('#mainContent').append('div').attr('id', 'loadGPXContainer');
    o.append('p').classed('alert alert-info', true).append('input').attr('id', 'getGPXFile').attr('type', 'file').text('Load GPX');
    o.append('p')
      .html('On this page you can load up GPX/KML/KMZ files directly to see the maps, streetview, 3D and 2D profiles (<a target="_blank" href="http://veloviewer.com/routes/5618017">like this one</a>).  The elevation data shown will be that from the GPX file.  If no elevation data is present then it will be shown as being completely flat. Note that these routes will not be saved in VeloViewer, to do that you\'ll need to add them as Routes in Strava...');
    o.append('p').style('margin-bottom', '50px')
      .html('You can load GPX files into Strava as Routes using <a href="http://labs.strava.com/gpx-to-route/#12/-122.44503/37.73651">this tool</a>, you will then get to see the segments along the route in VeloViewer.');

    function handleFileSelect(evt) {
      var files = evt.target.files; // FileList object

      // Loop through the FileList and render image files as thumbnails.
      for (var i = 0, f; f = files[i]; i++) {

        if (!f.name.match(/.*\.(kmz|gpx|kml|tcx|xml)$/i)) {
          continue;
        }

        if (f.name.match(/.*\.kmz$/i)) {
          JSZip.loadAsync(files[i]).then(function(zip) {
            //var re = /(.jpg|.png|.gif|.ps|.jpeg)$/;
            var promises = Object.keys(zip.files)
              .map(function(fileName) {
                var file = zip.files[fileName];
                if (fileName.match(/.*\.kml$/i) != null) {
                  return file.async("text").then(function(txt) {
                    return [
                      fileName, // keep the link between the file name and the content
                      txt
                    ];
                  });
                } else {
                  return file.async("blob").then(function(blob) {
                    return [
                      fileName, // keep the link between the file name and the content
                      URL.createObjectURL(blob) // create an url. img.src = URL.createObjectURL(...) will work
                    ];
                  });
                }
              }); // `promises` is an array of promises, `Promise.all` transforms it // into a promise of arrays
            return Promise.all(promises);
            //return zip.file("doc.kml").async("text");
          }).then(function(result) {
            for (var i = 0; i < result.length; i++) {
              if (result[i][0].match(/.*\.kml$/i) != null) {
                overlayData(result[i][1]);

                window.setTimeout(function() {
                  kmzSetIconHref();
                  drawWaypoints();
                }, 500);
              } else {
                kmzImages.push(result[i]);
              }
            }
          });
        } else {

          var reader = new FileReader();

          // Closure to capture the file information.
          reader.onload = (function(theFile) {
            return function(e) {
              overlayData(e.target.result);
              drawWaypoints();
            }
          })(f);

          // Read in the image file as a data URL.
          reader.readAsText(f);
        }
      }
    }

    document.getElementById('getGPXFile').addEventListener('change', handleFileSelect, false);
  } else {
    d3.select('#mainContent').append('p').style('margin-bottom', '50px')
      .html('VeloViewer PRO and PRO+ users can load up GPX files directly on this page to see the maps, streetview, 3D and 2D profiles (<a target="_blank" href="http://veloviewer.com/routes/5618017">like this one</a>).  Head to your <a href="/update">Update page</a> to upgrade.');
  }
} else {
  if (typeof(actsSrc) === 'undefined') {
    localInit();
  }
}
