/*
Copyright 2012-2026 VeloViewer. All Rights Reserved
###V193###
*/
var xCol = 'velocity_smooth',
    yCol = 'grade_smooth',
    cCol = 'time'
sCol = 'none',
    a2 = [], activitiesTimer = null, lineLayer2 = null;
var splitAuto,
    brushExtent = [0, 0],
    brushing = false,
    chartData = [],
    mainChart;
var startIndex;
var endIndex;
var chartColors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#FF8205', '#bcbd22', '#17becf', '#7f7f7f', '#7f7f7f', '#7f7f7f', '#7f7f7f', '#7f7f7f'];
var actStrs = [],
    segShown = false,
    map2, map3, activitiesData, mapData, segment, mapExtent, fullMapExtent = null,
    highlightFeature, highlightFeature2, markerFeature, filtIndExtent, otherBrushExtent = [0, 0],
    llObj, timeObj, elevObj, spdObj, hrtObj, cadObj, tempObj, torqueObj, lenObj, grdObj, pwrObj, aveSpdObj, avePwrObj, nrmPwrObj, altGainObj, distObj, otherDistValues, mainXObj, mainXVal, wattsAvl = false;
var allCharts = [],
    sumCharts = [],
    rateLimitedMessage = false,
    rateLimited = false,
    filtersFinishedFunRun = false,
    dataProcessed = false,
    initCalled = false,
    cpref = 'sd',
    p3dLoaded = false,
    isOS = false,
    curTab = 'map',
    currentChartColor = '',
    filename = '',
    sadTT = 0;
var xType = 'distance',
    rapTime = 30,
    timeOrig = [],
    lapArr, lapDistArr;

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),
    segmentsLoaded = false,
    viewType = 'line',
    splitSize = 'auto',
    chartSplitSize = 20,
    incStopped = false,
    splitsPopulated = false,
    fStyle = {
        fill: true,
        fillOpacity: 0,
        fillColor: '#000',
        stroke: true,
        strokeColor: '#000',
        strokeOpacity: 0.8,
        strokeWidth: 3,
        pointRadius: 4
    };

document.getElementById('elevRange').value = elevMult;
if (activity.athleteId != contextAthleteId) {
    d3.select('#saveThisView').style('display', 'none');
}

d3.select('#dataTab').style('overflow-x', 'hidden');

function processData() {
    activity.sdl = new Date(activity.start_date_local.replace('Z',''));
    d3.select('body > .container-fluid').style('position', 'relative').insert('span', 'header')
        .attr('style', 'font-size: 10px;position: absolute;')
        .attr('id', 'dateTimeSpan')
        .text((typeof (activity.dv) !== 'undefined' && activity.dv != '' ? activity.dv + ' - ' : '') + fDateTime2(activity.sdl));

    fillInGaps();

    distObj = actStrs.filter(function(d) {
        return d.type == 'distance'
    })[0];
    elevObj = actStrs.filter(function(d) {
        return d.type == 'altitude'
    })[0];

    if (typeof(distObj) !== 'undefined') {
        if (typeof(elevObj) !== 'undefined') {
            elevObj.data = filterData(distObj.data.map(function(d, i) {
                return {
                    x: d,
                    y: elevObj.data[i]
                }
            })).map(function(d) {
                return d.ySmooth
            });

            grdObj = { type: 'grade_smooth' };

            grdObj.data = distObj.data.map(function(d, i) {
                return i == 0 || (distObj.data[i] - distObj.data[i - 1]) == 0 ? 0 : 100 * (elevObj.data[i] - elevObj.data[i - 1]) / (distObj.data[i] - distObj.data[i - 1])
            });

            actStrs.push(grdObj);
        }
    } else {
        // no distance so delete elevation if that exists
        if (typeof(elevObj) !== 'undefined') {
            actStrs = actStrs.filter(function(d) {
                return d.type != 'altitude'
            });
            elevObj = undefined;
        }
    }
    elevationPreSmoothed = true;

    if (actStrs.filter(function(d) {
            return d.type == 'altitude'
        }).length > 0) {
        var dClone = clone(actStrs.filter(function(d) {
            return d.type == 'altitude'
        })[0].data);
        /*  Was this code to hide turbo trainers?
        if (activity.distance < 100) {
            var id = actStrs.map(function(d) {
                return d.type
            }).indexOf('altitude');
            if (id > -1) {
                actStrs.splice(id, 1);
            }
            elevObj = undefined;
            id = actStrs.map(function(d) {
                return d.type
            }).indexOf('grade_smooth');
            if (id > -1) {
                actStrs.splice(id, 1);
            }
            id = actStrs.map(function(d) {
                return d.type
            }).indexOf('latlng');
            if (id > -1) {
                actStrs.splice(id, 1);
            }
            llObj = undefined;
            id = actStrs.map(function(d) {
                return d.type
            }).indexOf('distance');
            if (id > -1) {
                actStrs.splice(id, 1);
            }
            distObj = undefined;
        }*/
    }

    if (actStrs.filter(function(d) {
            return d.type == 'watts'
        }).length > 0) {
        var calcInd = jsonIndexOf(actStrs, 'type', 'watts_calc');
        if (calcInd > -1) {
            actStrs.splice(calcInd, 1)
        }
    }

    spdObj = actStrs.filter(function(d) {
        return d.type == 'velocity_smooth'
    })[0];

    timeObj = actStrs.filter(function(d) {
        return d.type == 'time'
    })[0];

    /*if (typeof(distObj) !== 'undefined' && typeof(spdObj) === 'undefined') {
        spdObj = { type: 'velocity_smooth' };

        spdObj.data = filterData(distObj.data.map(function(d, i) {
            var i0 = i == 0 ? 0 : i - 1;
            var i1 = i == 0 ? 1 : i;
            return (distObj.data[i1] - distObj.data[i0]) / (timeObj.data[i1] - timeObj.data[i0])
        }).map(function(d, i) {
            return {
                x: timeObj.data[i],
                y: d
            }
        }), 2).map(function(d) { return d.ySmooth });

        actStrs.push(spdObj);
    }*/

    actStrs.forEach(function(d) {
        switch (d.type) {
            case 'latlng':
                d.order = -1;
                break;
            case 'distance':
                d.axv = function(dataVal) {
                    return dataVal * lrgLenMult
                }
                d.axf = function(dataVal) {
                    return d3.format(',.1f')(dataVal) + ' ' + lrgLenUnit
                }
                d.f = function(dataVal) {
                    return d3.format(',.1f')(dataVal * lrgLenMult) + ' ' + lrgLenUnit
                }
                d.f2 = function(dataVal) {
                    return d3.format(',.3f')(dataVal * lrgLenMult) + ' ' + lrgLenUnit
                }
                d.order = -1;
                d.title = 'Distance';
                d.unit = lrgLenUnit;
                d.mult = lrgLenMult;
                d.ordinal = false;
                d.histCols = 100;
                d.ignore0 = false;
                d.wholeNumber = 1;
                break;
            case 'time':
                d.f = function(dataVal) {
                    return (dataVal * 1).toHrMinSec()
                }
                d.axv = function(dataVal) {
                    return (dataVal * 60)
                }
                d.axf = function(dataVal) {
                    //return d3.time.format('%H:%M:%S')(dataVal)
                    return (dataVal * 60).toHrMinSec()
                }
                d.f2 = function(dataVal) {
                    return (dataVal * 60).toHrMinSec()
                }
                d.order = 10;
                d.title = 'Time';
                d.unit = '';
                d.ordinal = false;
                d.mult = 1 / 60;
                d.histCols = 100;
                d.ignore0 = false;
                d.wholeNumber = 1;
                break;
            case 'altitude':
                d.f = function(dataVal) {
                    return d3.format(',.0f')(dataVal * smlLenMult) + ' ' + smlLenUnit
                }
                d.order = 0;
                d.title = 'Elevation';
                d.unit = smlLenUnit;
                d.ordinal = false;
                d.mult = smlLenMult;
                d.histCols = 100;
                d.ignore0 = false;
                d.wholeNumber = 1;
                break;
            case 'velocity_smooth':
                switch (activity.type) {
                    case 'VirtualRun':
                    case 'Run':
                        d.f = function(dataVal) {
                            return (dataVal == 0 ? 'n/a' : (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit)
                            //return (dataVal == 0 ? 'n/a' : sTime(new Date(1000 * lrgPaceMult / dataVal)) + ' ' + lrgPaceUnit)
                        }
                        d.title = 'Pace';
                        d.unit = lrgPaceUnit;
                        d.mult = 1;
                        break;
                    case 'Swim':
                        d.f = function(dataVal) {
                            return (dataVal == 0 ? 'n/a' : (smlPaceMult / dataVal).toDayHrMinSec2() + ' ' + smlPaceUnit)
                            //return (dataVal == 0 ? 'n/a' : sTime(new Date(1000 * smlPaceMult / dataVal)) + ' ' + smlPaceUnit)
                        }
                        d.title = 'Pace';
                        d.unit = smlPaceUnit;
                        d.mult = 1;
                        break;
                    default:
                        d.f = function(dataVal) {
                            return d3.format(',.1f')(dataVal * speedMult) + ' ' + speedUnit
                        }
                        d.title = 'Speed';
                        d.unit = speedUnit;
                        d.mult = speedMult;
                        break;
                }
                d.order = 1;
                d.ordinal = false;
                d.ignore0 = true;
                d.histCols = 200;
                d.wholeNumber = 0.1;
                break;
            case 'heartrate':
                d.f = function(dataVal) {
                    return d3.format(',.0f')(dataVal) + ' bpm'
                }
                d.order = 2;
                d.title = 'Heart Rate';
                d.unit = 'bpm';
                d.ordinal = true;
                d.mult = 1;
                d.histCols = 100;
                d.ignore0 = true;
                d.wholeNumber = 1;
                break;
            case 'watts':
                wattsAvl = true;
            case 'watts_calc':
                d.f = function(dataVal) {
                    return d3.format(',.0f')(dataVal) + ' W'
                }
                d.order = 3;
                d.title = 'Power' + (wattsAvl ? ' (meter)' : ' (est)');
                d.unit = 'Watts';
                d.ordinal = false;
                /*if (!wattsAvl) {
                  smoothArr = [];
                  var tempD = d.data.map(function(e) {
                    return rollingAve(e, 2)
                  })
                  d.data = tempD;
                }*/
                d.mult = 1;
                d.histCols = 100;
                d.ignore0 = true;
                d.wholeNumber = 1;
                break;
            case 'cadence':
                d.f = function(dataVal) {
                    return d3.format(',.0f')(dataVal) + ' rpm'
                }
                d.order = 4;
                d.title = 'Cadence';
                d.unit = 'rpm';
                d.ordinal = true;
                d.ignore0 = true;
                d.mult = 1;
                d.histCols = 100;
                d.ignore0 = true;
                d.wholeNumber = 1;
                break;
            case 'grade_smooth':
                d.f = function(dataVal) {
                    return d3.format(',.1f')(dataVal) + ' %'
                }
                d.order = 5;
                d.title = 'Grade';
                d.unit = '%';
                d.ordinal = false;
                smoothArr = [];
                /*var tempD = d.data.map(function(e) {
                    return rollingAve(e, 6)
                })
                d.data = tempD;*/
                d.mult = 1;
                d.histCols = 100;
                d.ignore0 = false;
                d.wholeNumber = 0.1;
                break;
            case 'temp':
                if (tempUnit == 'F') {
                    d.data = d.data.map(function(d) { return convertCtoF(d) });
                }
                d.f = function(dataVal) {
                    return d3.format(',.0f')(dataVal) + ' \xB0' + tempUnit
                }
                d.order = 6;
                d.title = 'Temperature';
                d.unit = '\xB0' + tempUnit;
                d.ordinal = true;
                d.mult = 1;
                d.histCols = 10;
                d.ignore0 = false;
                d.wholeNumber = 1;
                break;
        }

    });

    if (typeof(distObj) !== 'undefined' && typeof(elevObj) !== 'undefined') {
        if (distObj.data[0] != 0) {
            var offset = distObj.data[0];
            distObj.data = distObj.data.map(function(d) {
                return d - offset
            });
        }
        otherDistValues = distObj.data;
        mainXObj = distObj;
        mainXVal = distObj.data;

        var xy = distObj.data.map(function(d, i) {
            return {
                x: d,
                y: elevObj.data[i]
            }
        });

        xy = filterData(xy, 15).map(function(e) {
            return {
                x: e.x,
                y: e.ySmooth,
                z: 0
            }
        });

        activity.maxG = d3.max(xy, function(e, i) {
            return i == 0 ? -10 : (e.y - xy[i - 1].y) / (e.x - xy[i - 1].x)
        });

        activity.vvom = calculateVVOM(xy);

        delete xy;
    } else {
        mainXObj = timeObj;
        mainXVal = timeObj.data;
        otherDistValues = (typeof(distObj) !== 'undefined' ? distObj.data : timeObj.data);
        timeObj.order = -1;
        incStopped = true;
    }

    hrtObj = actStrs.filter(function(d) {
        return d.type == 'heartrate'
    })[0];

    cadObj = actStrs.filter(function(d) {
        return d.type == 'cadence'
    })[0];

    if (typeof(elevObj) !== 'undefined') {
        altGainObj = clone(elevObj);
        altGainObj.type = 'altitude_gain';
        altGainObj.order = 7;
        altGainObj.title = 'Elevation Gain';
        actStrs.push(altGainObj);
    }

    if (typeof(spdObj) !== 'undefined') {
        aveSpdObj = clone(spdObj);
        aveSpdObj.type = 'average_velocity';
        aveSpdObj.order = 8;
        if (aveSpdObj.title == 'Speed') {
            aveSpdObj.title = 'Average Speed';
        } else {
            aveSpdObj.title = 'Average Pace';
        }
        actStrs.push(aveSpdObj);
    }

    pwrObj = actStrs.filter(function(d) {
        return (wattsAvl ? d.type == 'watts' : d.type == 'watts_calc')
    })[0];

    if (typeof(pwrObj) !== 'undefined') {
        avePwrObj = clone(pwrObj);
        avePwrObj.type = 'average_watts';
        avePwrObj.order = 9;
        avePwrObj.title = 'Average Power';
        actStrs.push(avePwrObj);
    }

    if (typeof(distObj) !== 'undefined' && typeof(spdObj) !== 'undefined' && typeof(cadObj) != 'undefined') {
        lenObj = clone(cadObj);
        lenObj.f = function(dataVal) {
            return d3.format(',.1f')(dataVal * smlLenMult) + ' ' + smlLenUnit
        };

        actStrs.filter(function(d) {
            return d.order > cadObj.order
        }).forEach(function(d) {
            d.order++;
        });

        lenObj.order = cadObj.order + 1;
        switch (activity.type) {
            case 'Ride':
            case 'VirtualRide':
                lenObj.title = 'Gear length';
                lenObj.type = 'gear_length';
                break;
            case 'Run':
            case 'VirtualRun':
                lenObj.title = 'Stride length';
                lenObj.type = 'stride_length';
                break;
            case 'Swim':
                lenObj.title = 'Stroke length';
                lenObj.type = 'stroke_length';
                break;
            default:
                lenObj.title = 'Dist/Cadence';
                lenObj.type = 'dist_Cadence';
                break;
        }
        lenObj.unit = smlLenUnit;

        smoothArr = [];
        var tempD = cadObj.data.map(function(e, i) {
            return (i == 0 ? 0 : (cadObj.data[i] == 0 ? 0 : (distObj.data[i] - distObj.data[i - 1]) / ((cadObj.data[i] / 60) * (timeObj.data[i] - timeObj.data[i - 1]))));
        });
        var maxVal = d3.quantile(clone(tempD).sort(d3.descending), 0.01);
        for (i = 0; i < tempD.length; i++) {
            tempD[i] = (tempD[i] > maxVal ? maxVal : tempD[i]);
        }

        lenObj.data = tempD;

        lenObj.mult = 1;
        lenObj.histCols = 50;
        lenObj.ignore0 = true;
        lenObj.wholeNumber = -1;
        actStrs.push(lenObj);
    }

    if (typeof(pwrObj) !== 'undefined' && typeof(cadObj) != 'undefined') {
        torqueObj = clone(pwrObj);
        torqueObj.type = 'torque';
        torqueObj.f = function(dataVal) {
            return d3.format(',.1f')(dataVal) + ' Nm'
        };

        actStrs.filter(function(d) {
            return d.order > cadObj.order
        }).forEach(function(d) {
            d.order++;
        });

        torqueObj.order = cadObj.order + 1;
        torqueObj.title = 'Torque (Nm)';
        torqueObj.unit = 'Nm';

        smoothArr = [];
        var tempD = pwrObj.data.map(function(e, i) {
            return (cadObj.data[i] == 0 ? 0 : 9.5488 * e / cadObj.data[i]);
            //return rollingAve((cadObj.data[i] == 0 ? 0 : 9.5488 * e / cadObj.data[i]), 4);
        });
        if (!wattsAvl) {
            var maxVal = d3.quantile(clone(tempD).sort(d3.descending), 0.01);
            for (i = 0; i < tempD.length; i++) {
                tempD[i] = (tempD[i] > maxVal ? maxVal : tempD[i]);
            }
        }

        torqueObj.data = tempD;

        torqueObj.mult = 1;
        torqueObj.histCols = 50;
        torqueObj.ignore0 = true;
        torqueObj.wholeNumber = 1;
        actStrs.push(torqueObj);
    }

    actStrs.sort(function(a, b) {
        return a.order - b.order
    });


    //set these if data does't exist...
    if (typeof(spdObj) === 'undefined') {
        xCol = 'heartrate';
    }
    if (typeof(elevObj) === 'undefined') {
        if (typeof(pwrObj) === 'undefined') {
            yCol = 'cadence';
        } else {
            yCol = 'watts';
        }
    }

    d3.select('#activitiesTabLi').style('display', null);

    window.onresize = sizeChanged;
}

function setAveSpd() {
    var firstTime, firstDist, mTime = 0,
        lastTime, lastDist;
    var lastAS = 0;
    var movingT = 0;
    var acAV = allCharts.filter(function(d) {
        return d.yd.type == 'average_velocity'
    })[0];
    if (typeof(acAV) !== 'undefined') {
        acAV.d.slice(filtIndExtent[0], filtIndExtent[1] + 1).forEach(function(d, i) {
            if (i == 0) {
                lastAS = d.y = spdObj.data[d.i];
                lastTime = firstTime = timeObj.data[d.i];
                lastDist = firstDist = distObj.data[d.i];
            } else {
                if (spdObj.data[d.i] != 0) {
                    mTime += timeObj.data[d.i] - lastTime;
                    d.y = (distObj.data[d.i] - firstDist) / mTime;
                }
                lastTime = timeObj.data[d.i];
            }
        });
    }
}

function setAvePwr() {
    if (typeof(avePwrObj) !== 'undefined') {
        var lastAP = 0;
        var movingC = 0;
        var acAP = allCharts.filter(function(d) {
            return d.yd.type == 'average_watts'
        })[0];
        if (typeof(acAP) !== 'undefined') {
            acAP.d.slice(filtIndExtent[0], filtIndExtent[1] + 1).forEach(function(d, i) {
                if (i == 0) {
                    lastAP = d.y = avePwrObj.data[d.i];
                    movingC += d.t1;
                } else {
                    if (mainXObj == timeObj || spdObj.data[d.i] != 0) {
                        d.y = (lastAP * movingC + (avePwrObj.data[d.i]) * d.t1) / (movingC + d.t1);
                        lastAP = d.y;
                        movingC += d.t1;
                    }
                }
            });
        }
    }
}

function setAltGain() {
    var la = 0;
    var ag = 0;
    var acAG = allCharts.filter(function(d) {
        return d.yd.type == 'altitude_gain'
    })[0];
    if (typeof(acAG) !== 'undefined') {
        acAG.d.filter(function(e) {
            return (brushExtent[1] == 0 || (e.d >= brushExtent[0] && e.d <= brushExtent[1]))
        }).forEach(function(d, i) {
            if (i == 0) {
                la == altGainObj.data[d.i];
                d.y1 = 0;
            } else {
                if (la < altGainObj.data[d.i]) {
                    ag += altGainObj.data[d.i] - la;
                    d.y1 = altGainObj.data[d.i] - la;
                } else {
                    d.y1 = 0;
                }
            }
            d.y = ag;
            la = altGainObj.data[d.i];
        });
    }
}

function setupCharts() {
    allCharts.push({
        p: d3.select("#elevChart"),
        yd: (typeof(elevObj) !== 'undefined' && typeof(llObj) !== 'undefined' ?
            elevObj :
            (wattsAvl && typeof(pwrObj) !== 'undefined' ?
                pwrObj :
                (typeof(hrtObj) !== 'undefined' ?
                    hrtObj :
                    (typeof(pwrObj) !== 'undefined' ?
                        pwrObj :
                        (typeof(spdObj) !== 'undefined' ? spdObj : cadObj)
                    )
                )
            )
        ),
        h: 75,
        m: [5, 10, 14, 50]
    });

    actStrs.forEach(function(d) {
        d.ext = d3.extent(d.data);
    });
    actStrs.filter(function(d) {
        return d.order > -1
    }).forEach(function(d) {
        if (d.ext[0] != d.ext[1]) {
            allCharts.push({
                p: d3.select("#dataTab"),
                p1: d3.select("#zonesTab"),
                yd: d,
                h: 100,
                m: [1, 10, 1, 160],
                h1: 200,
                w1: 100,
                m1: [20, 0, 30, 5]
            });
        } else {
            if (d.type == 'velocity_smooth') {
                incStopped = true;
            }
        }
    });
    setTypeFlipper();
    allCharts.forEach(function(d, i) {
        if (i == allCharts.length - 1) {
            d.m[2] = 20;
        }
        d.d = [];
        for (var j = 0; j < d.yd.data.length; j++) {
            d.d.push({
                i: j,
                y: d.yd.data[j],
                d: mainXVal[j],
                d1: otherDistValues[j],
                t: timeObj.data[j],
                t1: (j == 0 ? 0 : timeObj.data[j] - timeObj.data[j - 1])
            });
        }

        //set up rolling average power if applicable
        if ((wattsAvl && d.yd.type == 'watts') || (!wattsAvl && d.yd.type == 'watts_calc')) {
            smoothArr = [];
            d.rap = d3.merge(d.d.map(function(f, j) {
                return /*(spdObj.data[j] == 0 ? -1 : f.y)*/ f.y.toArray(f.t1)
            })).map(function(d) {
                return (d == -1 ? -1 : Math.pow(rollingAve(d, rapTime), 4))
            });
            pwrObj.rap = d.rap;
        }

        // remove outliers
        /*if (d.yd.type == 'grade_smooth') {
            var tc = 0;
            d.d.sort(function(a, b) {
                return a.y - b.y
            });
            for (var j = 0; j < d.d.length; j++) {
                tc += d.d[j].t1;
                if (tc > 30) {
                    for (var k = 0; k < j; k++) {
                        d.d[k].y = d.d[j].y;
                    }
                    break;
                }
            }
            d.d.sort(function(a, b) {
                return b.y - a.y
            });
            tc = 0;
            for (var j = 0; j < d.d.length; j++) {
                tc += d.d[j].t1;
                if (tc > 30) {
                    for (var k = 0; k < j; k++) {
                        d.d[k].y = d.d[j].y;
                    }
                    break;
                }
            }
            d.d.sort(function(a, b) {
                return a.i - b.i
            });
        }*/

        d.isChart = false;

        d.ext = d3.extent(d.d.filter(function(e) {
            return incStopped || spdObj.data[e.i] > 0
        }), function(e) {
            return e.y
        });

        if (i > 0 && d.yd.type.indexOf('average') == -1 && d.yd.type != 'altitude_gain' && d.yd.type != 'time') {

            switch (d.yd.type) {
                case 'temp':
                    d.yd.grp = scale_linearTickRange(d.ext, d.ext[1] - d.ext[0]);
                    break;
                default:
                    d.yd.grp = scale_linearTickRange(d3.extent(d.d.filter(function(e) {
                        return incStopped || spdObj.data[e.i] > 0
                    }), function(e) {
                        return e.y * d.yd.mult
                    }), chartSplitSize);
            }

            d.yd.grp[0] = (d.ext[0] < d.yd.grp[0] ? d.yd.grp[0] - d.yd.grp[2] : d.yd.grp[0])

            d.isChart = true;
            d.sad = d3.nest()
                .key(function(e) {
                    return Math.floor(e.y * d.yd.mult / d.yd.grp[2]);
                })
                .rollup(function(e) {
                    return {
                        'l': e.length,
                        't': d3.sum(e.map(function(f) {
                            return f.t1
                        }))
                    };
                })
                .map(d.d.filter(function(e) {
                    return incStopped || spdObj.data[e.i] > 0
                }));

            d.c1 = {};
            d.c1.svg = d.p1.append("svg:svg")
                .style("cursor", "pointer")
                .on('click', function() {
                    if (currentChartColor == i) {
                        colorChart(-1);
                        currentChartColor = -1;
                    } else {
                        colorChart(i);
                    }
                });
            d.c1.svg.append('title')
                .text('Click to view top chart shaded by ' + d.yd.title);
            d.c1.g = d.c1.svg.append("svg:g");
            d.c1.svg.append("g")
                .append("text")
                .style("text-anchor", "start")
                .style("font-weight", "bold")
                .style("font-size", "12px")
                .attr('fill', chartColors[d.yd.order])
                .attr('transform', 'translate(0, 15)')
                .text(d.yd.title + ' (' + d.yd.unit + ')');
            d.c1.oh = d.h1 + d.m1[0] + d.m1[2];
            d.c1.ow = d.w1 + d.m1[1] + d.m1[3];
            d.c1.x = d3.scale.linear().domain([d.yd.grp[0], d.yd.grp[1] + d.yd.grp[2]]);
            d.c1.y = d3.scale.linear().domain([0, d3.max(d3.entries(d.sad), function(e) {
                return e.value.t
            })]);
            d.c1.xa = d3.svg.axis().scale(d.c1.x).ticks(6).tickFormat(d.f);
            //d.c1.ya = d3.svg.axis().scale(d.c1.y).orient("left");
            d.c1.d = d.c1.g.append("svg:g")
                .attr("class", "grpTotals");
            //.style('shape-rendering', 'crispEdges');
            d.c1.xag = d.c1.g.append("svg:g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + (d.h1 - 4) + ")");
            d.c1.key = d.c1.g.append("svg:g")
                .classed('key', true)
                .attr("transform", "translate(0," + (d.h1 + 22) + ")");
            //d.c1.yag = d.c1.g.append("svg:g")
            //  .attr("class", "y axis");
        }

        d.svg = d.p.append("svg:svg");

        if (i > 0) {
            d.svg.style('border-bottom', 'solid 3px #eee')
                .style('margin-bottom', '-4px');
        }
        d.g = d.svg.append("svg:g");
        d.oh = d.h + d.m[0] + d.m[2];
        /*if (mainXObj.type == 'time') {
          d.x = d3.time.scale().domain(d3.extent(mainXVal));
          d.x2 = d3.time.scale().domain(d3.extent(mainXVal.map(function(e) {
            return e / 60
          })));
        } else {*/
        d.x = d3.scale.linear().domain(d3.extent(mainXVal));
        d.x2 = d3.scale.linear().domain(d3.extent(mainXVal.map(function(e) {
            return e * mainXObj.mult
        })));
        //}

        d.y = d3.scale.linear().domain(i == 0 ? [d.ext[0] - (d.ext[1] - d.ext[0]) / 10, d.ext[1]] : d.ext).range([d.h, 0]);
        if (i == 0 || i == allCharts.length - 1) {
            d.xa = d3.svg.axis().scale(d.x2).tickSubdivide(true).tickFormat(mainXObj.axf);
        }
        if (i == 0) {
            d.l = d3.svg.line()
                //.interpolate(d.yd.type == 'grade_smooth' || d.yd.type == 'watts_calc' || d.yd.type == 'watts' ? 'basis' : 'cardinal-open')
                .x(function(e) {
                    return d.x(e.x);
                })
                .y(function(e) {
                    return d.y(e.y);
                });
            d.ya = d3.svg.axis().scale(d.y).ticks(4).orient("left").tickFormat(d.yd.f);
            d.a = d3.svg.area()
                .x(function(e) {
                    return d.x(e.x);
                })
                .y0(function(e) {
                    return d.y(e.y);
                })
                .y1(0); //d.h
            d.areaRect = d.g.append("svg:rect")
                .classed('elevationArea', true)
                .attr('stroke', 'none')
                .attr('stroke-width', '0')
                .attr('fill-opacity', '0.3')
                .attr('fill', chartColors[d.yd.order]);
            d.area = d.g.append("svg:path")
                .attr('stroke', 'none')
                .attr('stroke-width', '0')
                .attr('fill', '#ffffff');
            d.areaHighlight = d.g.append("svg:path")
                .classed('elevationArea', true)
                .attr('fill', 'black');
            d.yag = d.g.append("svg:g")
                .attr("class", "y axis");
            d.b = d3.svg.brush()
                .x(d.x)
                .on("brushstart", brushstart)
                .on("brush", brushmove)
                .on("brushend", brushend);
        } else {
            d.l = d3.svg.line()
                //.interpolate(d.yd.type == 'grade_smooth' || d.yd.type == 'watts_calc' || d.yd.type == 'watts' ? 'basis' : 'cardinal-open')
                .x(function(e) {
                    return d.x(e.d);
                })
                .y(function(e) {
                    return d.y(e.y);
                });
            d.b = d3.svg.brush()
                .x(d.x)
                .on("brushstart", brushstart)
                .on("brushend", otherbrushend);

            d.t = d.g.append('text')
                .attr("transform", "translate(" + (-d.m[3] + 10) + "," + (d.h / 2) + ")rotate(-90)")
                .style("text-anchor", "middle")
                .style("font-weight", "bold")
                .attr('fill', chartColors[d.yd.order])
                .text(d.yd.title);

            d.meant = d.g.append('text')
                .classed('meanText', true)
                .style("text-anchor", "middle")
                .attr('fill', chartColors[d.yd.order])
                .attr("transform", "translate(" + (-d.m[3] + 23) + "," + (d.h / 2) + ")rotate(-90)");

            if ( /*wattsAvl &&*/ d.yd.type == "watts" || d.yd.type == "watts_calc") {
                d.rapt = d.g.append('text')
                    .classed('rapText', true)
                    .style("text-anchor", "middle")
                    .attr('fill', chartColors[d.yd.order])
                    .attr("transform", "translate(" + (-d.m[3] + 35) + "," + (d.h / 2) + ")rotate(-90)");
            }

            d.r = d.g.append("svg:g")
                .attr("class", 'yRange')
                .attr("transform", "translate(" + ((-d.m[3] + 23) / 2) + ",0)");
            d.rt = d.r.append('title');
            d.ymm = d.r.append('line')
                .attr('stroke', chartColors[d.yd.order])
                .attr("class", 'minmax');
            d.ymin = d.r.append('line')
                .attr('stroke', chartColors[d.yd.order])
                .attr("class", 'min');
            d.ymint = d.r.append('text')
                .attr('fill', chartColors[d.yd.order])
                .style("text-anchor", "end");
            d.ymax = d.r.append('line')
                .attr('stroke', chartColors[d.yd.order])
                .attr("class", 'max');
            d.ymaxt = d.r.append('text')
                .attr('fill', chartColors[d.yd.order])
                .style("text-anchor", "end");
            d.yqrt = d.r.append('rect')
                .attr('fill', '#fff')
                .attr('stroke', chartColors[d.yd.order])
                .attr("class", 'box');
            d.ylqrtt = d.r.append('text')
                .attr('fill', chartColors[d.yd.order])
                .style("text-anchor", "start");
            d.yuqrtt = d.r.append('text')
                .attr('fill', chartColors[d.yd.order])
                .style("text-anchor", "start");
            d.ymed = d.r.append('line')
                .attr('stroke', chartColors[d.yd.order])
                .attr("class", 'median');
            d.ymedt = d.r.append('text')
                .attr('fill', chartColors[d.yd.order])
                .style("text-anchor", "end");
        }
        d.backRect = d.g.append("rect")
            .style('fill', 'white')
            .attr('transform', 'translate(-4,0)');
        d.meanLine = d.g.append("line")
            .classed('meanLine', true)
            .attr('stroke', chartColors[d.yd.order])
            .attr('stroke-dasharray', '10,10')
            .attr('stroke-opacity', '0.25')
            .style('shape-rendering', 'crispEdges');
        d.path = d.g.append("svg:path")
            .classed('dataLine line', true)
            .attr('stroke', chartColors[d.yd.order]);
        d.sag = d.g.append("svg:g")
            .attr("class", "splitAvgs");
        //.style('shape-rendering', 'crispEdges');
        if (i == 1) {
            d.sagt = d.sag.append('text')
                .style("text-anchor", "end")
                .style("font-size", "10px")
                .style("fill", "black")
                .style("fill-opacity", "0.5");
        }
        if (i == 0 || i == allCharts.length - 1) {
            d.xag = d.g.append("svg:g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + (d.h - 4) + ")");
        }
        d.mg = d.g.append('g')
            .attr('class', 'marker')
            .style('display', 'none');
        d.mg.append('line')
            .attr('stroke', chartColors[d.yd.order])
            .attr('x1', 0)
            .attr('x2', 0)
            .attr('y1', 0)
            .attr('y2', d.h);
        d.mg.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(-60,0)");
        d.mt = d.mg.append('text')
            .classed('markerTopVal', true)
            .attr('fill', chartColors[d.yd.order])
            .attr("transform", "translate(-5,10)")
            .style("text-anchor", "end");
        if (i < 2) {
            d.mg.append('rect')
                .classed('markerBtmVal', true)
                .attr('fill', 'white')
                .attr('stroke', 'black')
                .attr('fill-opacity', 0.8)
                .attr('stroke-opacity', 0.8)
                .attr('height', 15)
                .attr('width', 70)
                .attr('rx', 5)
                .attr('ry', 5)
                .attr("transform", "translate(-70," + (d.h - 15) + ")");
            d.mdt = d.mg.append('text')
                .classed('markerBtmVal', true)
                .attr('fill', chartColors[d.yd.order])
                .attr("transform", "translate(-5," + (d.h - 5) + ")")
                .style("text-anchor", "end");
        }
        d.mb = d.g.append("g")
            .attr("class", "brush");
        d.mb
            .on('mousemove', function(e) {
                markerMove(d)
            })
            .on('mouseout', markerHide);
        if (i == 0) {
            d.mbtg = d.g.append("g")
                .attr("class", "brushText")
                .style('display', 'none');
            d.mbtg.append('rect')
                .classed('brushTopVal', true)
                .attr('fill', 'white')
                .attr('stroke', 'black')
                .attr('fill-opacity', 0.8)
                .attr('stroke-opacity', 0.8)
                .attr('height', 41 + (typeof(pwrObj) ? 18 : 0) + (typeof(hrtObj) !== 'undefined' ? 9 : 0))
                .attr('width', 62)
                .attr('rx', 5)
                .attr('ry', 5)
                .attr("transform", "translate(-62,0)");
            d.mbt = d.mbtg.append('text')
                .classed('brushTopVal', true)
                .attr('fill', chartColors[d.yd.order])
                .attr("transform", "translate(-5,10)")
                .style("text-anchor", "end")
                .style("font-size", "0.8em");
            d.mbtg.append('title');
        }
    });

    dataProcessed = true;
    if (isBeta && initCalled) {
        localInit();
    }
}

function drawCharts() {
    allCharts.filter(function(d, i) {
        return curTab == 'data' || i == 0
    }).forEach(function(d, i) {
        d.ow = parseInt(d.p.style('width'));
        d.w = d.ow - d.m[1] - d.m[3];
        d.svg.attr("width", d.ow).attr("height", d.oh);
        d.g.attr("transform", "translate(" + d.m[3] + "," + d.m[0] + ")");
        d.x.range([0, d.w]);
        d.x2.range([0, d.w]);
        if (i == 0) {
            d.xag.call(d.xa);
        }
        if (i == 0) {
            d.yag.call(d.ya);

            var lData = d.d.map(function(e, j) {
                var o = e;
                e.x = e.d;
                e.z = 0;
                return o;
            });
            d.tData = lData;
            if (d.tData.length >= d.w / 2) {
                var simpVal = 10;
                d.tData = simplify(lData, simpVal, true);
                while (d.tData.length < d.w / 2 && simpVal > 0.01) {
                    d.tData = simplify(lData, simpVal, true);
                    simpVal = simpVal / 2;
                }
            }

            d.tData.forEach(function(e, j) {
                e.g = j == 0 ? 0 : (d.tData[j].y - d.tData[j - 1].y) / (d.tData[j].x - d.tData[j - 1].x);
            });

            d.path.attr("d", d.l(d.tData));
            d.area.attr("d", d.a(d.tData));
            d.areaRect
                .attr('width', d.w)
                .attr('height', d.h);
        }
        d.mb.call(d.b);
        d.mb.selectAll("rect")
            .attr("height", d.h);
    });
    allCharts.filter(function(d, i) {
        return curTab == 'zones' && d.isChart
    }).forEach(function(d, i) {
        d.c1.ow = Math.floor(parseFloat(d.p1.style('width')) / allCharts.filter(function(e, i) {
            return e.isChart
        }).length) - 1;
        d.w1 = d.c1.ow - d.m1[1] - d.m1[3];
        d.c1.svg.attr("width", d.c1.ow).attr("height", d.c1.oh);
        d.c1.g.attr("transform", "translate(" + d.m1[3] + "," + d.m1[0] + ")");
        d.c1.x.range([0, d.w1]);
        d.c1.y.range([d.h1, 0]);

        var rects = d.c1.d.selectAll('rect.fullValues')
            .data(d3.entries(d.sad), function(e) {
                return e.key
            });

        rects.enter()
            .append('rect')
            .classed('fullValues', true)
            .attr('fill', chartColors[d.yd.order])
            .attr('fill-opacity', 0.05)
            .attr('stroke', chartColors[d.yd.order])
            .attr('stroke-opacity', 0.15)
            .attr('x', function(e) {
                return d.c1.x(parseInt(e.key) * d.yd.grp[2])
            })
            .attr('width', function(e) {
                return d.c1.x((parseInt(e.key) + 1) * d.yd.grp[2]) - d.c1.x((parseInt(e.key)) * d.yd.grp[2])
            })
            .attr('y', function(e) {
                return d.c1.y(0)
            })
            .attr('height', function(e) {
                return d.c1.y(0)
            });

        rects.transition()
            .duration(0.1)
            .attr('x', function(e) {
                return d.c1.x(parseInt(e.key) * d.yd.grp[2])
            })
            .attr('width', function(e) {
                return d.c1.x((parseInt(e.key) + 1) * d.yd.grp[2]) - d.c1.x((parseInt(e.key)) * d.yd.grp[2])
            })
            .attr('y', function(e) {
                return d.c1.y(e.value.t)
            })
            .attr('height', function(e) {
                return d.c1.y(0) - d.c1.y(e.value.t)
            });

        var keyPartWidth = (d.w1 - 4) / 100;

        d.c1.key
            .selectAll('rect.keyRect')
            .attr('x', function(d) {
                return d * keyPartWidth
            })
            .attr('width', keyPartWidth)
    });
    d3.selectAll("#zonesTab .thresholdStats").remove();
    d3.select("#zonesTab").append('div').classed('thresholdStats', true).style('margin-bottom', '10px');
    filterCharts();
}

function filterCharts() {
    setBrushDash();
    if (allCharts.length == 0) {
        return;
    }
    if (chartData.length == 0) {
        actStrs.filter(function(d) {
            return d.type != 'latlng'
        }).forEach(function(d, i) {
            for (var j = 0; j < d.data.length; j++) {
                if (i == 0) {
                    chartData[j] = {};
                }
                chartData[j][d.type] = d.data[j];
            }
            d.extent = d3.extent(d.data);
        });

        chartData.forEach(function(d, i) {
            d.id = i;
            if (i == 0) {
                d.timeInc = 0;
                d.distInc = 0;
                d.velocity = 0;
            } else {
                d.timeInc = d.time - chartData[i - 1].time;
                d.distInc = d.distance - chartData[i - 1].distance;
                /*d.distLLInc = distLatLon(d.latlng[0], d.latlng[1], chartData[i - 1].latlng[0], chartData[i - 1].latlng[1]);*/
                d.velocity = d.distInc / d.timeInc;
                /*d.velocity = d.distLLInc / d.timeInc;
                  d.grade = 100 * (d.altitude - chartData[i - 1].altitude) / d.distInc;*/
            }
        });
    }

    filtIndExtent = d3.extent(allCharts[0].d.filter(function(d) {
        return brushExtent[1] == 0 || (d.d >= brushExtent[0] && d.d <= brushExtent[1])
    }).map(function(d) {
        return d.i
    }));

    /*  var fd = chartData.filter(function(d, i) {
      if (typeof(filtIndExtent[0]) === 'undefined') {
        return true;
      }
      if (typeof(filtIndExtent[0]) === 'number') {
        return brushExtent[1] == 0 || (d.d >= brushExtent[0] && d.d <= brushExtent[1])
      }
      if (d.distInc == 0 || d.timeInc >= 10 || d.velocity_smooth == 0) {
        return false;
      }
      for (var j = 0; j < filtIndExtent.length; j++) {
        if (i >= filtIndExtent[j][0] && i <= filtIndExtent[j][1]) {
          return true;
        }
      }
      return false;
    });*/
    if (curTab == 'zones') {
        if (typeof(filtIndExtent[0]) !== 'undefined') {
            allCharts.forEach(function(d, i) {
                if (d.isChart) {
                    var sad = d3.nest()
                        .key(function(e) {
                            return Math.floor(e.y * d.yd.mult / d.yd.grp[2]);
                        })
                        .rollup(function(e) {
                            return {
                                'l': e.length,
                                't': d3.sum(e.map(function(f) {
                                    return f.t1
                                }))
                            };
                        })
                        .map(d.d.slice(filtIndExtent[0], filtIndExtent[1] + 1)
                            .filter(function(e) {
                                return incStopped || spdObj.data[e.i] > 0
                            }));

                    sadTT = d3.sum(d3.entries(sad), function(d) {
                        return d.value.t
                    });

                    var rects = d.c1.d.selectAll('rect.filterValues')
                        .data(d3.entries(sad), function(e) {
                            return e.key
                        });

                    rects.enter()
                        .append('rect')
                        .classed('filterValues', true)
                        .attr('fill', chartColors[d.yd.order])
                        .attr('fill-opacity', 0.25)
                        .attr('stroke', chartColors[d.yd.order])
                        .attr('x', function(e) {
                            return d.c1.x(parseInt(e.key) * d.yd.grp[2])
                        })
                        .attr('width', function(e) {
                            return d.c1.x((parseInt(e.key) + 1) * d.yd.grp[2]) - d.c1.x((parseInt(e.key)) * d.yd.grp[2])
                        })
                        .attr('y', function(e) {
                            return d.c1.y(0)
                        })
                        .attr('height', function(e) {
                            return 0
                        })
                        .append('title');

                    rects.transition()
                        .duration(0.1)
                        .attr('x', function(e) {
                            return d.c1.x(parseInt(e.key) * d.yd.grp[2])
                        })
                        .attr('width', function(e) {
                            return d.c1.x((parseInt(e.key) + 1) * d.yd.grp[2]) - d.c1.x((parseInt(e.key)) * d.yd.grp[2])
                        })
                        .attr('y', function(e) {
                            return d.c1.y(e.value.t)
                        })
                        .attr('height', function(e) {
                            return d.c1.y(0) - d.c1.y(e.value.t)
                        })
                        .select('title')
                        .text(function(e) {
                            return d.yd.f(parseInt(e.key) * d.yd.grp[2] / d.yd.mult) + ' to ' + d.yd.f(((parseInt(e.key) + 1) / d.yd.mult) * d.yd.grp[2]) + ' - ' + timeObj.f(e.value.t) + ' (' + f1dp(100 * e.value.t / sadTT) + '%)';
                        });

                    rects.exit().remove();

                    d.c1.xag.call(d.c1.xa);

                    if (d.yd.type == 'watts' && contextAthleteId == athleteId) {
                        if (typeof(ftp) !== 'undefined' && ftp != null && ftp > 0) {
                            var data = d.d.slice(filtIndExtent[0], filtIndExtent[1] + 1)
                                .filter(function(e) {
                                    return incStopped || spdObj.data[e.i] > 0
                                });
                            var dataBT = data.filter(function(d) { return d.y < ftp });
                            var dataAT = data.filter(function(d) { return d.y >= ftp });
                            var percAboveThreshold = fPercent0(dataAT.length / data.length);
                            var avgBelowThreshold = dataBT.length == 0 ? 'n/a' : fInt(d3.mean(dataBT, function(d) { return d.y })) + ' W';
                            var avgAboveThreshold = dataAT.length == 0 ? 'n/a' : fInt(d3.mean(dataAT, function(d) { return d.y })) + ' W';
                            var dFtp = fInt(ftp) + ' W';
                        } else {
                            var percAboveThreshold = 'n/a';
                            var avgBelowThreshold = 'n/a';
                            var avgAboveThreshold = 'n/a';
                            var dFtp = 'n/a';
                        }
                        d3.select('#zonesTab .thresholdStats').html('ftp (<a href="https://www.strava.com/settings/performance" target="_blank">set in Strava</a>, Premium only) = ' + dFtp + '<br/>% time above threshold = ' + percAboveThreshold + '<br/>Avg. power above threshold = ' + avgAboveThreshold + '<br/>Avg power below threshold = ' + avgBelowThreshold);
                    }
                    //d.c1.yag.call(d.c1.ya);
                }
            });
        }
    }
    if (curTab == 'data') {
        setAveSpd();
        setAvePwr();
        setAltGain();
        allCharts.filter(function(d, i) {
            return i > 0
        }).forEach(function(d, i) {
            d.fd = d.d.slice(filtIndExtent[0], filtIndExtent[1] + 1)
                .filter(function(e) {
                    return incStopped || spdObj.data[e.i] > 0
                });
            d.x.domain(brushExtent[1] == 0 ? d3.extent(mainXVal) : brushExtent);
            d.x2.domain(brushExtent[1] == 0 ? d3.extent(mainXVal.map(function(e) {
                return e * mainXObj.mult
            })) : [brushExtent[0] * mainXObj.mult, brushExtent[1] * mainXObj.mult]);
            d.y.domain(d.fd.length > 0 ? d3.extent(d.fd.map(function(d) {
                return d.y
            })) : [0, 0]);

            if (d.fd.length > 0) {
                d.path.attr("d", d.l(d.fd
                    .filter(function(e, j) {
                        return j % Math.ceil(d.fd.length / (d.w / 1)) == 0
                    })));

                d.min = d3.min(d.fd, function(e) {
                    return e.y
                });
                d.max = d3.max(d.fd, function(e) {
                    return e.y
                });
                d.mean = (d.yd.type == 'velocity_smooth' ?
                    (d.fd[d.fd.length - 1].d1 - d.fd[0].d1) / (d3.sum(d.fd, function(e) {
                        return (e.i != d.fd[d.fd.length - 1].i ? (e.y > 0 ? e.t1 : 0) : 0)
                    })) :
                    d3.mean(d3.merge(d.fd.map(function(f) {
                        return f.y.toArray(f.t1)
                    }))));
                d.median = d3.quantile(d3.merge(d.fd.map(function(f) {
                    return f.y.toArray(f.t1)
                })).sort(d3.ascending), 0.5);
                d.lqrt = d3.quantile(d3.merge(d.fd.map(function(f) {
                    return f.y.toArray(f.t1)
                })).sort(d3.ascending), 0.25);
                d.uqrt = d3.quantile(d3.merge(d.fd.map(function(f) {
                    return f.y.toArray(f.t1)
                })).sort(d3.ascending), 0.75);
                d.backRect
                    .attr('x', 0)
                    .attr('width', d.w + 8)
                    .attr('y', 0)
                    .attr('height', d.h);
                d.meanLine
                    .attr('x1', 0)
                    .attr('x2', d.w)
                    .attr('y1', d.y(d.mean))
                    .attr('y2', d.y(d.mean));
                d.ymm
                    .attr('x1', 5)
                    .attr('x2', 5)
                    .attr('y1', d.y(d.max))
                    .attr('y2', d.y(d.min));
                d.ymin
                    .attr('x1', 0)
                    .attr('x2', 10)
                    .attr('y1', d.y(d.min))
                    .attr('y2', d.y(d.min));
                d.ymint
                    .text(d.yd.f(d.min))
                    .attr('transform', 'translate(-5,' + d.y(d.min) + ')');
                d.ymax
                    .attr('x1', 0)
                    .attr('x2', 10)
                    .attr('y1', d.y(d.max))
                    .attr('y2', d.y(d.max));
                d.ymaxt
                    .text(d.yd.f(d.max))
                    .attr('transform', 'translate(-5,' + (d.y(d.max) + 8) + ')');
                d.ymed
                    .attr('x1', 0)
                    .attr('x2', 10)
                    .attr('y1', d.y(d.median))
                    .attr('y2', d.y(d.median));
                d.ymedt
                    .text(d.yd.f(d.median))
                    .attr('transform', 'translate(-5,' + Math.max(Math.min((d.y(d.median) + 4), d.y(d.min) - 10), d.y(d.max) + 18) + ')');
                d.yqrt
                    .attr('x', 0)
                    .attr('y', d.y(d.uqrt))
                    .attr('width', 10)
                    .attr('height', d.y(d.lqrt) - d.y(d.uqrt));
                d.ylqrtt
                    .text(d.yd.f(d.lqrt))
                    .attr('transform', 'translate(15,' + Math.max(Math.min(d.y(d.lqrt) + 4, d.y(d.min)), d.y(d.max) + 18) + ')');
                d.yuqrtt
                    .text(d.yd.f(d.uqrt))
                    .attr('transform', 'translate(15,' + Math.min(Math.max(d.y(d.uqrt) + 4, d.y(d.max)), d.y(d.min) - 10) + ')');
                d.meant
                    .text(d.yd.type == 'time' ? 'split: ' + d.yd.f(d3.extent(d.fd, function(e) {
                        return e.t
                    }).span()) : 'Mean: ' + d.yd.f(d.mean));
                if ( /*wattsAvl &&*/ d.yd.type == 'watts' || d.yd.type == 'watts_calc') {
                    smoothArr = [];
                    d.rapVal = timeObj.data[filtIndExtent[1]] - timeObj.data[filtIndExtent[0]] > rapTime ? Math.pow(d3.mean(d.rap.slice(timeObj.data[filtIndExtent[0]] + rapTime, timeObj.data[filtIndExtent[1]] + 1)), 1 / 4) : 0;
                    //(filtIndExtent[1] - filtIndExtent[0] > rapTime ? Math.pow(d3.mean(d.rap.slice(filtIndExtent[0] + rapTime, filtIndExtent[1] + 1)), 1 / 4) : 0);
                    d.rapt.text('NP: ' + d.yd.f(d.rapVal));
                }
                d.rt.text('Mean: ' + d.yd.f(d.mean) + ', Minimum: ' + d.yd.f(d.min) +
                    ', Lower quartile: ' + d.yd.f(d.lqrt) + ', Median: ' + d.yd.f(d.median) + ', Upper quartile: ' + d.yd.f(d.uqrt) + ', Maximum: ' + d.yd.f(d.max));

                var sas = 1000;
                var lapCurArr = [];
                switch (splitSize) {
                    case 'auto':
                        sas = Math.max(0.1, scale_linearTickRange([d.x.domain()[0] * mainXObj.mult, d.x.domain()[1] * mainXObj.mult], Math.round(d.w / 40))[2]) / mainXObj.mult;
                        break;
                    case 'half':
                        sas = (d.x.domain()[1] - d.x.domain()[0]) / 2;
                        break;
                    case 'third':
                        sas = (d.x.domain()[1] - d.x.domain()[0]) / 3;
                        break;
                    case 'quarter':
                        sas = (d.x.domain()[1] - d.x.domain()[0]) / 4;
                        break;
                    case 'laps':
                        sas = -1;
                        break;
                    default:
                        sas = parseFloat(splitSize);
                }

                splitAuto.text('Auto ' + mainXObj.f(Math.max(0.1, scale_linearTickRange([d.x.domain()[0] * mainXObj.mult, d.x.domain()[1] * mainXObj.mult], Math.round(d.w / 40))[2]) / mainXObj.mult));

                if (i == 0) {
                    d.sagt.attr("transform", "translate(" + d.w + ",10)")
                        .text(sas == -1 ? 'Laps' : mainXObj.f(sas));
                }

                switch (viewType) {
                    case 'box':
                        if (i == 0) {
                            d.sagt.style('display', null);
                        }
                        var sad = d3.nest()
                            .key(function(e) {
                                return sas == -1 ? d3.bisectLeft(lapDistArr, e.d) : Math.floor((e.d - brushExtent[0]) / sas);
                            })
                            .rollup(function(e) {
                                var item = sas == -1 ? d3.bisectLeft(lapDistArr, e[0].d) : Math.floor(d3.min(e.map(function(f) {
                                    return f.d
                                })) / sas);
                                return {
                                    'l': e.length,
                                    'd0': sas == -1 ? Math.max(brushExtent[0], (item == 0 ? 0 : lapDistArr[item - 1])) : item * sas + brushExtent[0] % sas,
                                    'd1': Math.min((brushExtent[0] == brushExtent[1] ? distObj.data[distObj.data.length - 1] : brushExtent[1]), sas == -1 ? lapDistArr[item] : (item + 1) * sas + brushExtent[0] % sas),
                                    'tExt': d3.extent(e.map(function(f) {
                                        return f.t
                                    })),
                                    'min': d3.min(e.map(function(f) {
                                        return f.y
                                    })),
                                    'max': d3.max(e.map(function(f) {
                                        return f.y
                                    })),
                                    'lq': d3.quantile(d3.merge(e.map(function(f) {
                                        return f.y.toArray(f.t1)
                                    })).sort(d3.ascending), 0.25),
                                    'med': d3.quantile(d3.merge(e.map(function(f) {
                                        return f.y.toArray(f.t1)
                                    })).sort(d3.ascending), 0.5),
                                    'uq': d3.quantile(d3.merge(e.map(function(f) {
                                        return f.y.toArray(f.t1)
                                    })).sort(d3.ascending), 0.75),
                                    'mean': (d.yd.type == 'velocity_smooth' ?
                                        (e.length == 0 ? 0 :
                                            (e.length == 1 ? e[0].y :
                                                (e[e.length - 1].d1 - e[0].d1) / (d3.sum(e, function(f, j) {
                                                    return /*(f.y > 0 && j > 0 ? f.t1 : 0)*/ (f.i != e[e.length - 1].i ? (f.y > 0 ? f.t1 : 0) : 0)
                                                })))) :
                                        d3.mean(d3.merge(e.map(function(f) {
                                            return f.y.toArray(f.t1)
                                        }))))
                                };
                            })
                            .map(d.fd);

                        var sadg = d.sag.selectAll('g.splitAvg')
                            .data(d3.entries(sad), function(e) {
                                return 'b' + e.key
                            });

                        sadg.enter()
                            .append('g')
                            .classed('splitAvg', true)
                            .attr('height', d.h)
                            .each(function(e) {
                                var item = d3.select(this);
                                item.append('line')
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'minmax');
                                item.append('line')
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'min');
                                item.append('line')
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'max');
                                item.append('rect')
                                    .attr('fill', chartColors[d.yd.order])
                                    .attr('fill-opacity', 0.3)
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'box');
                                item.append('line')
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'median');
                                item.append('rect')
                                    .classed('textRect', true)
                                    .attr('fill', 'white')
                                    .attr('stroke', 'black')
                                    .attr('fill-opacity', 0.8)
                                    .attr('stroke-opacity', 0.8)
                                    .attr('width', 100)
                                    .attr('height', 65)
                                    .attr('rx', 5)
                                    .attr('ry', 5)
                                    .style('display', 'none')
                                    .style('shape-rendering', 'auto')
                                    .attr("transform", "translate(-50,0)");
                                item.append('text')
                                    .classed('textRect', true)
                                    .style('display', 'none')
                                    .style("text-anchor", "middle")
                                    .attr("transform", "translate(0,10)");
                            });

                        sadg
                            /*.attr('width', function(e) {
                                                            return d.x(e.value.d1 - e.value.d0)
                                                        })*/
                            .attr('transform', function(e, i) {
                                return 'translate(' + d.x(e.value.d0) + ',0)'
                            });

                        sadg.select('line.minmax')
                            .attr('x1', function(e) {
                                return d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2
                            }) //d.x(brushExtent[0] + sas) / 2)
                            .attr('x2', function(e) {
                                return d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2
                            })
                            .attr('y1', function(e) {
                                return d.y(e.value.max)
                            })
                            .attr('y2', function(e) {
                                return d.y(e.value.min)
                            });
                        sadg.select('line.min')
                            .attr('x1', function(e) {
                                return (d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2) - 5
                            }) //(d.x(brushExtent[0] + sas) / 2) - 5)
                            .attr('x2', function(e) {
                                return (d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2) + 5
                            })
                            .attr('y1', function(e) {
                                return d.y(e.value.min)
                            })
                            .attr('y2', function(e) {
                                return d.y(e.value.min)
                            });
                        sadg.select('line.max')
                            .attr('x1', function(e) {
                                return (d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2) - 5
                            })
                            .attr('x2', function(e) {
                                return (d.x(brushExtent[0] + e.value.d1 - e.value.d0) / 2) + 5
                            })
                            .attr('y1', function(e) {
                                return d.y(e.value.max)
                            })
                            .attr('y2', function(e) {
                                return d.y(e.value.max)
                            });
                        sadg.select('rect.box')
                            .attr('x', 0)
                            .attr('y', function(e) {
                                return d.y(e.value.uq)
                            })
                            .attr('width', function(e) {
                                return Math.max(1, d.x(brushExtent[0] + e.value.d1 - e.value.d0) - 2)
                            }) //d.x(brushExtent[0] + sas) - 2)
                            .attr('height', function(e) {
                                return d.y(e.value.lq) - d.y(e.value.uq)
                            });
                        sadg.select('line.median')
                            .attr('x1', 0)
                            .attr('x2', function(e) {
                                return d.x(brushExtent[0] + e.value.d1 - e.value.d0) - 2
                            })
                            .attr('y1', function(e) {
                                return d.y(e.value.med)
                            })
                            .attr('y2', function(e) {
                                return d.y(e.value.med)
                            });

                        sadg.select('title')
                            .text(function(e) {
                                return 'Mean: ' + d.yd.f(e.value.mean) + ', Minimum: ' + d.yd.f(e.value.min) +
                                    ', Lower quartile: ' + d.yd.f(e.value.lq) + ', Median: ' + d.yd.f(e.value.med) + ', Upper quartile: ' + d.yd.f(e.value.uq) + ', Maximum: ' + d.yd.f(e.value.max)
                            })

                        sadg.exit().remove();

                        d.path.attr("d", d.l(d.fd
                                .filter(function(e, j) {
                                    return j % Math.ceil(d.fd.length / (d.w / 1)) == 0
                                })))
                            .style('display', 'none');
                        break;

                    case 'bar':
                        if (i == 0) {
                            d.sagt.style('display', null);
                        }

                        var sad = d3.nest()
                            .key(function(e) {
                                return sas == -1 ? d3.bisectLeft(lapDistArr, e.d) : Math.floor((e.d - brushExtent[0]) / sas);
                            })
                            .rollup(function(e) {
                                var item = sas == -1 ? d3.bisectLeft(lapDistArr, e[0].d) : Math.floor(d3.min(e.map(function(f) {
                                    return f.d
                                })) / sas);
                                return {
                                    'l': e.length,
                                    'd0': sas == -1 ? Math.max(brushExtent[0], (item == 0 ? 0 : lapDistArr[item - 1])) : item * sas + brushExtent[0] % sas,
                                    'd1': Math.min((brushExtent[0] == brushExtent[1] ? distObj.data[distObj.data.length - 1] : brushExtent[1]), sas == -1 ? lapDistArr[item] : Math.min(mainXObj.ext[1], (item + 1) * sas + brushExtent[0] % sas)),
                                    'tExt': d3.extent(e.map(function(f) {
                                        return f.t
                                    })),
                                    'min': d3.min(e.map(function(f) {
                                        return f.y
                                    })),
                                    'max': d3.max(e.map(function(f) {
                                        return f.y
                                    })),
                                    'mean': (d.yd.type == 'velocity_smooth' ?
                                        (e.length == 0 ? 0 :
                                            (e.length == 1 ? e[0].y :
                                                (e[e.length - 1].d1 - e[0].d1) / (d3.sum(e, function(f, j) {
                                                    return /*(f.y > 0 && j > 0 ? f.t1 : 0)*/ (f.i != e[e.length - 1].i ? (f.y > 0 ? f.t1 : 0) : 0)
                                                })))) :
                                        d3.mean(d3.merge(e.map(function(f) {
                                            return f.y.toArray(f.t1)
                                        }))))
                                };
                            })
                            .map(d.fd);

                        var sadg = d.sag.selectAll('g.splitAvg')
                            .data(d3.entries(sad), function(e) {
                                return 'm' + e.key
                            });

                        sadg.enter()
                            .append('g')
                            .classed('splitAvg', true)
                            .attr('height', d.h)
                            .each(function(e) {
                                var item = d3.select(this);
                                item.append('rect')
                                    .attr('fill', chartColors[d.yd.order])
                                    .attr('fill-opacity', 0.3)
                                    .attr('stroke', chartColors[d.yd.order])
                                    .attr("class", 'box');
                                item.append('rect')
                                    .classed('textRect', true)
                                    .attr('fill', 'white')
                                    .attr('stroke', 'black')
                                    .attr('fill-opacity', 0.8)
                                    .attr('stroke-opacity', 0.8)
                                    .attr('width', 100)
                                    .attr('height', ( /*wattsAvl &&*/ d.yd.type == 'watts' || d.yd.type == 'watts_calc' ? 35 : 25))
                                    .attr('rx', 5)
                                    .attr('ry', 5)
                                    .style('display', 'none')
                                    .style('shape-rendering', 'auto')
                                    .attr("transform", "translate(-50,0)");
                                item.append('text')
                                    .classed('textRect', true)
                                    .style('display', 'none')
                                    .style("text-anchor", "middle")
                                    .attr("transform", "translate(0,10)");
                            });

                        sadg
                            /*.attr('width', function(e) {
                                return d.x(brushExtent[0] + e.value.d1 - e.value.d0)
                            })*/
                            .attr('transform', function(e, i) {
                                return 'translate(' + d.x(e.value.d0) + ',0)'
                            });

                        sadg.select('rect.box')
                            .attr('x', 0)
                            .attr('y', function(e) {
                                if (d.yd.type != 'grade_smooth') {
                                    return d.y(e.value.mean)
                                } else {
                                    if (e.value.mean >= 0) {
                                        return d.y(e.value.mean)
                                    } else {
                                        return d.y(Math.min(0, d.max))
                                    }
                                }
                            })
                            .attr('width', function(e) {
                                return Math.max(1, d.x(brushExtent[0] + (e.value.d1 - e.value.d0)) - 2)
                            })
                            .attr('height', function(e) {
                                if (d.yd.type != 'grade_smooth') {
                                    return d.h - d.y(e.value.mean)
                                } else {
                                    if (e.value.mean >= 0) {
                                        return d.y(Math.max(0, d.min)) - d.y(e.value.mean)
                                    } else {
                                        return d.y(e.value.mean) - d.y(Math.min(0, d.max))
                                    }
                                }
                            });

                        sadg.select('title')
                            .text(function(e) {
                                return 'Mean: ' + d.yd.f(e.value.mean)
                            })

                        sadg.exit().remove();

                        d.path.attr("d", d.l(d.fd
                                .filter(function(e, j) {
                                    return j % Math.ceil(d.fd.length / (d.w / 1)) == 0
                                })))
                            .style('display', 'none');
                        break;

                    case 'line':
                        if (i == 0) {
                            d.sagt.style('display', 'none');
                        }

                        d.sag.selectAll('g.splitAvg').remove();

                        d.path.attr("d", d.l(d.fd
                            .filter(function(e, j) {
                                return j % Math.ceil(d.fd.length / (d.w / 1)) == 0
                            })));

                        break;
                }
            }
            if (i == allCharts.length - 2) {
                d.xag.call(d.xa);
            }

            d.b = d3.svg.brush()
                .x(d.x)
                //.on("brush", otherbrushmove)
                .on("brushend", otherbrushend);
            d.mb.call(d.b);
            d.mb.selectAll("rect")
                .attr("height", d.h);
        })
    }
    if (curTab == 'p3d') {
        segment.latLngArr2 = segment.latLngArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        segment.elevationArr2 = segment.elevationArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        segment.distanceArr2 = segment.distanceArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        set3dSegment();
    }
    if (curTab == 'map3d') {
        segment.latLngArr2 = segment.latLngArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        segment.elevationArr2 = segment.elevationArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        segment.distanceArr2 = segment.distanceArr2Copy.slice(filtIndExtent[0], filtIndExtent[1] + 1);
        draw3dmap(!map3ddrawn);
    }
    if (curTab == 'breakdown') {
        drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent)
    }
    if (curTab == 'sv') {
        setSvExtent();
        setMapDetail('G', svMap);
        setGMapDistMarkers();
    }
    if (curTab == 'earth') {
        drawCesiumPath(true);
    }
    if (curTab == 'charts') {
        if (mainChart == null) {
            d3.select("#chartsTab")
                .append('div')
                .classed('row-fluid', true)
                .each(function() {
                    d3.select(this)
                        .append('div')
                        .classed('span10', true)
                        .append('div')
                        .classed('well', true)
                        .style('margin-bottom', '10px')
                        .attr('id', 'mainChartContainer');
                    d3.select(this)
                        .append('div')
                        .attr('id', 'optionsContainer')
                        .classed('span2', true);
                });

            setMapChartHeight();

            d3.select("#mainChartContainer")
                .datum(chartData)
                .call(mainChart = vvChart()
                    .type('ScatterHeat')
                    //.opacity(0.2)
                    .xTickFormat(actStrs.filter(function(d) {
                        return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                    })[0].f)
                    .yTickFormat(actStrs.filter(function(d) {
                        return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                    })[0].f)
                    /*.sTickFormat(actStrs.filter(function(d) {
                      return d.type == sCol
                    })[0].f)*/
                    .cTickFormat(actStrs.filter(function(d) {
                        return d.type == (cCol == 'velocity' ? 'velocity_smooth' : cCol)
                    })[0].f)
                    .x(function(d) {
                        return +d[xCol];
                    })
                    .y(function(d) {
                        return +d[yCol];
                    })
                    .color(function(d) {
                        return (mainChart.type() == 'ScatterHeat' ? +d[cCol + (cCol == 'time' || cCol == 'dist' ? 'Inc' : '')] : +d[cCol]);
                    })
                    /*.size(function(d) {
                      return +d[sCol];
                    })*/
                    /*.histColor(chartColors[actStrs.filter(function(d) {
                      return d.type == yCol
                    })[0].order])*/
                    .xIgnoreZero(actStrs.filter(function(d) {
                        return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                    })[0].ignore0)
                    .yIgnoreZero(actStrs.filter(function(d) {
                        return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                    })[0].ignore0)
                    .xWholeNumber(actStrs.filter(function(d) {
                        return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                    })[0].wholeNumber)
                    .yWholeNumber(actStrs.filter(function(d) {
                        return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                    })[0].wholeNumber)
                    .width('auto')
                    .height('auto')
                    .xHistCols(actStrs.filter(function(d) {
                        return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                    })[0].histCols)
                    .yHistCols(actStrs.filter(function(d) {
                        return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                    })[0].histCols)
                    /*.mouseMove(function(d) {
          fd = actData.filter(function(e) {
            return e[xCol] >= d.x0 && e[xCol] < d.x1 &&
              e[yCol] >= d.y0 && e[yCol] < d.y1;
          });
        })*/
                    .xChanged(function(d) {
                        xCol = d;
                        mainChart.xTickFormat(actStrs.filter(function(d) {
                                return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                            })[0].f)
                            .x(function(d) {
                                return +d[xCol];
                            })
                            .xHistCols(actStrs.filter(function(d) {
                                return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                            })[0].histCols)
                            .xIgnoreZero(actStrs.filter(function(d) {
                                return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                            })[0].ignore0)
                            .xWholeNumber(actStrs.filter(function(d) {
                                return d.type == (xCol == 'velocity' ? 'velocity_smooth' : xCol)
                            })[0].wholeNumber);
                    })
                    .yChanged(function(d) {
                        yCol = d;
                        mainChart.yTickFormat(actStrs.filter(function(d) {
                                return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                            })[0].f)
                            .y(function(d) {
                                return +d[yCol];
                            })
                            .yHistCols(actStrs.filter(function(d) {
                                return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                            })[0].histCols)
                            .yIgnoreZero(actStrs.filter(function(d) {
                                return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                            })[0].ignore0)
                            .yWholeNumber(actStrs.filter(function(d) {
                                return d.type == (yCol == 'velocity' ? 'velocity_smooth' : yCol)
                            })[0].wholeNumber);
                        /*.histColor(chartColors[actStrs.filter(function(d) {
                            return d.type == yCol
                          })[0].order]);*/
                    })
                    .cChanged(function(d) {
                        cCol = d;
                        if (cCol == 'none') {
                            mainChart.sTickFormat(function(d) {
                                    return ''
                                })
                                .color(null)
                                .colorIgnoreZero(false);
                        } else {
                            mainChart.cTickFormat(actStrs.filter(function(d) {
                                    return d.type == (cCol == 'velocity' ? 'velocity_smooth' : cCol)
                                })[0].f)
                                .color(function(d) {
                                    return (mainChart.type() == 'ScatterHeat' ? +d[cCol + (cCol == 'time' || cCol == 'dist' ? 'Inc' : '')] : +d[cCol]);
                                })
                            /*.colorIgnoreZero(actStrs.filter(function(d) {
                                return d.type == cCol
                              })[0].ignore0)*/
                            ;
                        }
                    })
                    .sChanged(function(d) {
                        sCol = d;
                        if (sCol == 'none') {
                            mainChart.sTickFormat(function(d) {
                                    return ''
                                })
                                .size(null)
                                .sizeIgnoreZero(false);
                        } else {
                            mainChart.sTickFormat(actStrs.filter(function(d) {
                                    return d.type == (sCol == 'velocity' ? 'velocity_smooth' : sCol)
                                })[0].f)
                                .size(function(d) {
                                    return +d[sCol];
                                })
                                .sizeIgnoreZero(actStrs.filter(function(d) {
                                    return d.type == (sCol == 'velocity' ? 'velocity_smooth' : sCol)
                                })[0].ignore0);
                        }
                    })
                    .gradColorRange([
                        '#CCCCFF',
                        '#0000FF',
                        '#00FFFF',
                        '#00FF00',
                        '#FFFF00',
                        '#FF0000'
                    ]));

            document.getElementById('vvChartType').value = mainChart.type();
            document.getElementById('vvChartX').value = xCol;
            document.getElementById('vvChartY').value = yCol;
            document.getElementById('vvChartC').value = cCol;
            document.getElementById('vvChartS').value = sCol;
        }
        mainChart.filterExtent(filtIndExtent).redraw();
    }
    if (curTab == 'activities') {
        drawActivities()
    }
}

/*
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 = activity.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(activity.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 setMapMarker(dx) {
    if (typeof(segment) === 'undefined') return;
  var ind = d3.bisectLeft(distObj.data, dx);
  ind = ind == segment.latLngArr2Copy.length - 1 ? ind - 1 : ind;

  d3.select('#dateTimeSpan')
      .text((typeof (activity.dv) !== 'undefined' && activity.dv != '' ? activity.dv + ' - ' : '') + fDateTime2(new Date(activity.sdl.getTime() + timeObj.data[ind] * 1000)));

    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);
            }
            isPanningSV = true;
            ind = ind == segment.latLngArr2Copy.length - 1 ? ind - 1 : ind;
            setSVPano(segment.latLngArr2Copy[ind], segment.latLngArr2Copy[ind + 1], true);
            window.setTimeout(function() {
                isPanningSV = false;
            }, 2000);
        }
    }
}

function brushstart() {
    //brushing = true;
}

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);
    })

    filterCharts();
    setBrushText();
}

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

/*function otherbrushmove() {
  markerMove(otherBrushExtent[0] != d3.event.target.extent()[0] ? d3.event.target.extent()[0] : d3.event.target.extent()[1]);
  otherBrushExtent = d3.event.target.extent();
}*/

function otherbrushend() {
    brushing = false;
    if (d3.event.target.extent()[0] == d3.event.target.extent()[1]) {
        brushExtent = [0, 0];
    } else {
        brushExtent = d3.event.target.extent();
    }
    allCharts[0].b.extent(brushExtent);
    allCharts[0].mb.call(allCharts[0].b);

    filterCharts();
    setBrushText();
    setBrushDash();
}

function setBrushText() {
    if (brushExtent[1] == 0 || brushExtent[0] == brushExtent[1]) {
        allCharts[0].mbtg.style('display', 'none');
    } else {
        if (typeof(filtIndExtent[0]) !== 'undefined') {
            allCharts[0].mbtg.style('display', null);
            allCharts[0].mbtg.attr('transform', 'translate(' + allCharts[0].x(brushExtent[0]) + ', 0)');
            allCharts[0].mbt.selectAll('tspan').remove();

            var tData = allCharts[0].d.slice(filtIndExtent[0], filtIndExtent[1] + 1);
            var mtData = tData.filter(function(d) {
                return incStopped || spdObj.data[d.i] > 0
            });
            var dist = mainXObj.f2(brushExtent[1] - brushExtent[0]);
            var et = tData[tData.length - 1].t - tData[0].t;
            var mt = Math.min(et, d3.sum(mtData, function(d) {
                return d.t1
            }));
            var vam = typeof(segment) === 'undefined' || typeof(elevObj) === 'undefined' || typeof(elevObj.data) === 'undefined' || typeof(timeObj) === 'undefined' || typeof(timeObj.data) === 'undefined' ? 'n/a' : (elevObj.data[filtIndExtent[1]] - elevObj.data[filtIndExtent[0]]) / ((timeObj.data[filtIndExtent[1]] - timeObj.data[filtIndExtent[0]]) / (60*60));
            if (typeof(pwrObj) !== 'undefined' && /*wattsAvl &&*/ et >= (20 * 60)) {
                var meanWatts = d3.mean(d3.merge(allCharts.filter(function(d) {
                        return d.yd.type == 'watts' + (wattsAvl ? '' : '_calc')
                    })[0].d.slice(filtIndExtent[0], filtIndExtent[1] + 1)
                    .filter(function(e) {
                        return incStopped || spdObj.data[e.i] > 0
                    }).map(function(f) {
                        return f.y.toArray(f.t1)
                    })));
                var rapWatts = Math.pow(d3.mean(pwrObj.rap.slice(timeObj.data[filtIndExtent[0]] + rapTime, timeObj.data[filtIndExtent[1]] + 1)), 1 / 4);
                var pftp = fInt(meanWatts * Math.min(1, (0.05 / 4) * ((et / 60) - 20) / 10 + 0.95)) + ' W';
                var rapftp = fInt(rapWatts * Math.min(1, (0.05 / 4) * ((et / 60) - 20) / 10 + 0.95)) + ' W';
            } else {
                var pftp = 'n/a';
                var rapftp = 'n/a';
            }
            if (typeof(hrtObj) !== 'undefined' && et >= (20 * 60)) {
                var meanHR = d3.mean(d3.merge(allCharts.filter(function(d) {
                        return d.yd.type == 'heartrate'
                    })[0].d.slice(filtIndExtent[0], filtIndExtent[1] + 1)
                    .filter(function(e) {
                        return incStopped || spdObj.data[e.i] > 0
                    }).map(function(f) {
                        return f.y.toArray(f.t1)
                    }))); //d3.mean(hrtObj.data.slice(filtIndExtent[0], filtIndExtent[1] + 1));
                var hrftp = fInt(meanHR * Math.min(1, (0.05 / 4) * ((et / 60) - 20) / 10 + 0.95)) + ' bpm';
            } else {
                var hrftp = 'n/a';
            }

            allCharts[0].mbt.append('tspan')
                .text(dist);
            allCharts[0].mbt.append('tspan')
                .attr('x', 0)
                .attr('dy', 9)
                .text('ET: ' + timeObj.f(et));
            allCharts[0].mbt.append('tspan')
                .attr('x', 0)
                .attr('dy', 9)
                .text('MT: ' + timeObj.f(mt));
            if (typeof(pwrObj) !== 'undefined' /*&& wattsAvl*/ ) {
                allCharts[0].mbt.append('tspan')
                    .attr('x', 0)
                    .attr('dy', 9)
                    .text('FTP: ' + pftp);
                allCharts[0].mbt.append('tspan')
                    .attr('x', 0)
                    .attr('dy', 9)
                    .text('NP FTP: ' + rapftp);
            }
            if (typeof(hrtObj) !== 'undefined') {
                allCharts[0].mbt.append('tspan')
                    .attr('x', 0)
                    .attr('dy', 9)
                    .text('FTP: ' + hrftp);
            }
            if (vam != 'n/a') {
                allCharts[0].mbt.append('tspan')
                    .attr('x', 0)
                    .attr('dy', 9)
                    .text('VAM: ' + fInt(vam));
            }
            allCharts[0].mbtg.select('title')
                .text('Distance: ' + dist + '\nElapsed time: ' + timeObj.f(et) + '\nMoving time: ' + timeObj.f(mt) + (typeof(pwrObj) !== 'undefined' /*&& wattsAvl*/ ? '\nFTP Power: ' + pftp + '\nFTP Normalised Power: ' + rapftp : '') + (typeof(hrtObj) !== 'undefined' ? '\nFTP Heart Rate: ' + hrftp : '') + (vam != 'n/a' ? '\nVertical Ascent in Meters: ' + fInt(vam) + ' m/h' : ''));
        }
    }
}

function markerHide() {
    d3.selectAll('.marker')
        .style('display', 'none');
    d3.selectAll('#dataTab .splitAvg .textRect')
        .style('display', 'none');

    if (viewType == 'bar' || viewType == 'box') {
        d3.selectAll('#dataTab .dataLine')
            .style('display', 'none');
    }
}

function markerMove(d) {
    d3.selectAll('.marker')
        .style('display', null);

    var x;
    if (typeof(d3.event.target.x) !== 'undefined' && typeof(d3.event.target.x.baseVal) !== 'undefined' && d3.event.target.x.baseVal.value == -3) {
        x = (d3.event.target.parentElement ? d3.event.target.parentElement : d3.event.target.parentNode).transform.baseVal.getItem('translate').matrix.e + d3.mouse(d3.event.target)[0];
    } else {
        x = d3.mouse(d3.event.target)[0];
    }
    var dx = d.x.invert(x);

    for (var j = 0; j < (curTab == 'data' ? allCharts.length : 1); j++) {
        var e = allCharts[j];
        if (j == 0 || brushExtent[1] == 0 || (dx >= brushExtent[0] && dx <= brushExtent[1])) {
            e.mg.attr('transform', 'translate(' + e.x(dx) + ',0)');
            if (j > 0 && !brushing) {
                switch (viewType) {
                    case 'box':
                    case 'bar':
                        //if (d3.event.target.parentElement.parentElement == e.g[0][0]) {
                        //e.g.selectAll('.marker')
                        //  .style('display', 'none');
                        e.g.selectAll('.markerTopVal').style('display', 'none');

                        e.sag.selectAll('.splitAvg')
                            .sort(function(f) {
                                return (dx < f.value.d0 || dx >= f.value.d1 ? -1 : 1)
                            })
                            .filter(function(f) {
                                return dx < f.value.d0 || dx >= f.value.d1
                            })
                            .selectAll('.textRect')
                            .style('display', 'none');

                        var title = e.sag.selectAll('.splitAvg').filter(function(f) {
                            return dx >= f.value.d0 && dx < f.value.d1
                        });

                        if (title.select('rect')[0].length > 0) {

                            title.selectAll('.textRect')
                                .style('display', null);

                            title.select('rect.textRect')
                                .attr("transform", "translate(" + (-50 + title.select('rect').attr('width') / 2) + ",0)");

                            title.selectAll('text.textRect tspan').remove();
                            title.select('text.textRect')
                                .attr("transform", "translate(" + (title.select('rect').attr('width') / 2) + ",10)")
                                .append('tspan')
                                .text(function(f) {
                                    return mainXObj.f(f.value.d0) + ' - ' + mainXObj.f(f.value.d1)
                                });
                            if (viewType == 'bar') {
                                title.select('text.textRect')
                                    .append('tspan')
                                    .attr('x', 0)
                                    .attr('dy', 10)
                                    .text(function(f) {
                                        return (e.yd.type == 'time' ? 'Split: ' + e.yd.f(f.value.tExt.span()) : 'Mean: ' + e.yd.f(f.value.mean))
                                    });

                                if ( /*wattsAvl &&*/ e.yd.type == 'watts' || e.yd.type == 'watts_calc') {
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'NP: ' + e.yd.f(Math.pow(d3.mean(e.rap.slice(f.value.tExt[0], f.value.tExt[1]).filter(function(e) {
                                                return e > -1
                                            })), 1 / 4))
                                        });
                                }
                            } else {
                                if (e.yd.type == 'time') {
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Split: ' + e.yd.f(f.value.tExt.span())
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Max: ' + e.yd.f(f.value.max)
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Min: ' + e.yd.f(f.value.min)
                                        });
                                } else {
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Max: ' + e.yd.f(f.value.max)
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Up Qrt: ' + e.yd.f(f.value.uq)
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Median: ' + e.yd.f(f.value.med)
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Lwr Qrt: ' + e.yd.f(f.value.lq)
                                        });
                                    title.select('text.textRect')
                                        .append('tspan')
                                        .attr('x', 0)
                                        .attr('dy', 10)
                                        .text(function(f) {
                                            return 'Min: ' + e.yd.f(f.value.min)
                                        });
                                }
                            }
                        }
                        /*} else {
                          e.mt.text(e.yd.f(e.d[d3.bisect(e.d.map(function(g) {
                            return g.d
                          }), dx) - 1].y));
                        }*/
                        break;
                    default:
                        e.g.selectAll('.markerTopVal').style('display', null);

                        e.mt.text(e.yd.f(e.d[Math.max(0, d3.bisect(e.d.map(function(g) {
                            return g.d
                        }), dx) - 1)].y));
                }
            } else {
                var f = e;
                if (currentChartColor != '' && currentChartColor != -1) {
                    f = allCharts[currentChartColor];
                }
                e.mt.text(f.yd.f(f.d[Math.max(0, d3.bisect(f.d.map(function(g) {
                    return g.d
                }), dx) - 1)].y));
            }
            if (j < 2) {
                e.mdt.text(mainXObj.f2(dx));
            }
        } else {
            e.mg.attr('transform', 'translate(-1000,0)');
        }
    }

    if (viewType == 'bar' || viewType == 'box') {
        d.path.style('display', null);
    }
    setMapMarker(dx);
}

function getData() {
    var s = document.createElement('script');
    if (activity.dataPrivatePopulated) {
        s.src = 'https://s3.veloviewer.com/' + activity.dataPrivateKey;
    } else {
        s.src = '/api/getActivityStreamsNew.php?i=' + activity.id + '&a=' + activity.athleteId + '&r=all&p=' + (loggedInAthleteId == contextAthleteId ? 0 : 1);
    }
    filename = s.src;
    s.async = true;
    document.getElementsByTagName('head')[0].appendChild(s);

    var s1 = document.createElement('script');
    if (activity.dataLapsPopulated) {
        s1.src = 'https://s3.veloviewer.com/' + activity.dataLapsKey;
    } else {
        s1.src = '/api/activity/' + activity.id + '/getStreams/laps';
    }
    filename = s1.src;
    s1.async = true;
    document.getElementsByTagName('head')[0].appendChild(s1);
}

function lapsLoaded(obj) {
  if (typeof(obj.error) === 'undefined') {
    lapArr = obj;
    if (actStrs.length > 0) {
        popLaps();
    }
  }
}

function popLaps() {
    if (typeof(distObj) !== 'undefined' && distObj.data.length > 0 && lapArr.length > 0) {
        lapArr[lapArr.length - 1][1] = Math.max(lapArr[lapArr.length - 1][1], timeOrig.length - 1);
        lapArr = lapArr.map(function(d) {
            return d3.merge([d, [timeObj.data.indexOf(timeOrig[d[0]]), timeObj.data.indexOf(timeOrig[d[1]])]])
        });
        lapDistArr = lapArr.map(function(d) {
            return distObj.data[d[3]]
        });
        d3.select('#typeFlipperLapsOption').style('display', lapArr.length == 0 ? 'none' : null);
    }
}

function streamLoaded1(obj) {
    if (obj.error) {
        d3.select('#loadingDiv p.lead').html('Public activity detail data is not yet available for this athlete.');
        return;
    }
    if (typeof(obj.latlngdp) === 'undefined') {
        activity.dataPrivatePopulated = false;
        var s = document.createElement('script');
        s.src = '/api/getActivityStreamsNew.php?force=1&i=' + activity.id + '&a=' + activity.athleteId + '&r=all&p=' + (loggedInAthleteId == contextAthleteId ? 0 : 1);
        filename = s.src;
        s.async = true;
        document.getElementsByTagName('head')[0].appendChild(s);
        return;
    }
    /*if (gettingPublic) {
        if (typeof(obj['latlng']) !== 'undefined') {
            var item = {
                type: 'latlng'
            };
            item.data = decompressN(obj['latlng'], 5, 2);
            actStrsPublic.push(item);
        }
    }*/
    //if (!gettingPublic || typeof(activity.dataPrivateKey) === 'undefined') {
    var dt = ['time', 'latlng', 'distance', 'altitude', 'velocity_smooth', 'heartrate', 'cadence', 'watts', 'watts_calc', 'temp', /*'grade_smooth'*/ ];
    for (var i = 0; i < dt.length; i++) {
        if (typeof(obj[dt[i]]) !== 'undefined') {
            if (dt[i] == 'watts_calc' && obj[dt[i]].match(/^\?*$/) !== null) {
                continue;
            }
            var item = {
                type: dt[i]
            };
            if (dt[i] == 'latlng') {
                item.data = decompressN(obj[dt[i]], typeof(obj.latlngdp) !== 'undefined' ? obj.latlngdp : 5, 2);
            } else {
                item.data = decompressN(obj[dt[i]], 1, 1);
            }
            actStrs.push(item);
        }
    }

    llObj = actStrs.filter(function(d) {
        return d.type == 'latlng'
    })[0];

    if (activity.trainer == 1) {
        var id = actStrs.map(function(d) {
            return d.type
        }).indexOf('latlng');
        if (id > -1) {
            actStrs.splice(id, 1);
        }
    }

    if (typeof(llObj) === 'undefined') {
        d3.selectAll('#myTabs li')
            .each(function(d, i) {
                var item = d3.select(this);

                if (['#svTab', '#earthTab', '#p3dTab', '#map3d'].indexOf(item.select('a').attr('href')) > -1) {
                    item.style('display', 'none');
                }
            });
        $('#myTabs li:eq(3) a').tab('show');
        curTab = 'data';
    } else {
        llObj.data2 = [];
        for (var i = 0; i < llObj.data.length / 2; i++) {
            llObj.data2.push({
                lat: llObj.data[i * 2],
                lng: llObj.data[i * 2 + 1]
            })
        }
    }

    processData();

    if (typeof(lapArr) !== 'undefined') {
        popLaps();
    }

    d3.select('#loadingDiv').style('display', 'none');
    d3.select('#mainContent').style('display', null);

    //initMap('3');

    if (typeof(llObj) !== 'undefined') {
        d3.select('#myTabs')
            .append('li').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');
            d3.select('#typeFlipperDiv').style('display', 'none');
            drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent, timeObj.data)
        });

        setup3dmap();
    } else {
        d3.selectAll('#activitiesTabLi, #activitiesTab, #segmentsTabLi, #segmentsTab').style('display', 'none');
    }

    if (typeof(llObj) !== 'undefined') {

        d3.select('#myTabs')
            .append('li').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');
            d3.select('#typeFlipperDiv').style('display', 'none');
            drawCesiumPath(false);
        });

        /*d3.select('#myTabs')
            .append('li').html('<a href="#svTab" data-toggle="tab">Street View</a>');
        d3.select('.tab-content')
            .append('div')
            .attr({
                class: 'tab-pane',
                id: 'svTab'
            });*/
        $('a[data-toggle="tab"][href="#svTab"]').on('shown', function(e) {
            curTab = 'sv';
            $('#svTab').height(Math.max(350, window.innerHeight - 274));
            d3.selectAll('.floatingVVLogo').style('display', 'none');
            d3.select('#typeFlipperDiv').style('display', 'none');
            loadstreetview();
            setSvExtent();
            setMapDetail('G', svMap);
        });

        curTab = 'sv';
        $('#svTab').height(Math.max(350, window.innerHeight - 274));
        d3.selectAll('.floatingVVLogo').style('display', 'none');
        setExtent();
        if (typeof(segment) !== 'undefined') {
            loadstreetview();
            setMapDetail('G', svMap);
        }
    }

    setupCharts();

    window.setTimeout(function() {
        if (isBeta) {
            setSegmentIndexes();
        }
        sizeChanged();
        var colorChartIndex = allCharts.map(function(d) {
            return d.yd.type
        }).lastIndexOf(allCharts[0].yd.type == 'altitude' ? 'grade_smooth' : allCharts[0].yd.type);
        if (colorChartIndex == -1) {
            colorChartIndex = allCharts.map(function(d) {
                return d.yd.type
            }).lastIndexOf('watts');
        }
        if (colorChartIndex == -1) {
            colorChartIndex = allCharts.map(function(d) {
                return d.yd.type
            }).lastIndexOf('heartrate');
        }
        if (colorChartIndex > -1) {
            colorChart(colorChartIndex);
        }
        setSummary();
    }, 10);
    //set3dSegment();
    /*} else {
        gettingPublic = false;
        getData();
    } */
}

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

    drawCharts();
    if (curTab == 'data') {
        setMapChartHeight();
    }
    if (curTab == 'segments') {
        setMapChartHeight();
    }
    if (curTab == 'charts') {
        setMapChartHeight();
    }
    if (curTab == 'map') {
        setMapChartHeight();
        map3.zoomToExtent(mapExtent);
    }
    if (curTab == 'activities') {
        setMapChartHeight();
        map2.fitBounds([
            [activity.minLat, activity.minLng],
            [activity.maxLat, activity.maxLng]
        ]);
    }
    if (curTab == 'p3d') {
        set3dSegment();
    }
    if (curTab == 'breakdown') {
        drawBreakdown(d3.select('#breakdownTab'), segment.latLngArr2Copy, segment.distanceArr2Copy, bdsmel, filtIndExtent)
    }
    if (curTab == 'earth') {
        d3.select('.cesium-widget canvas').style('height', (window.innerHeight - document.getElementById('earthTab').getBoundingClientRect().top - 50) + 'px');
    }
    $('#svTab').height(Math.max(350, window.innerHeight - 274));
}


function setImages() {
    if (!SVGObjects || SVGObjects.length == 0) {
        setTimeout(setImages, 100);
        return;
    }
    var l_curTab = curTab;
    var tabChanged = false;
    if (profileUrl2d == '' && parseFloat(d3.select('body').style('width')) > 800) {
        tabChanged = true;
        curTab = 'p3d';
        d3.select('#mapTab').classed('in active', false);
        d3.select('#p3dTab').classed('in active', true);

        spinXVal3d = spinXVal;
        spinYVal3d = spinYVal;
        spinLightXVal3d = spinLightXVal;
        spinLightYVal3d = spinLightYVal;

        var zExt = d3.extent(segment.elevationArr2);
        is3d = false;
        spinXVal = 116;
        spinYVal = -16;
        spinLightXVal = 86;
        spinLightYVal = -21;
        zoom = zoom2d;
        elevMult = Math.min(5, segment.distance / (4 * (zExt[1] - zExt[0])));

        set3dSegment(10);
        displayImage();

        spinXVal = spinXVal3d;
        spinYVal = spinYVal3d;
        spinLightXVal = spinLightXVal3d;
        spinLightYVal = spinLightYVal3d;
        zoom = zoom3d;
    }
    if (profileUrl3d == '' && parseFloat(d3.select('body').style('width')) > 800) {
        tabChanged = true;
        curTab = 'p3d';
        d3.select('#mapTab').classed('in active', false);
        d3.select('#p3dTab').classed('in active', true);

        is3d = true;
        set3dSegment(10);

        if (profileUrl3d == '') {
            displayImage();
        }
    }
    if (tabChanged) {
        curTab = l_curTab;
    }
    d3.select('#p3dTab').classed('in active', false);
    d3.select('#' + curTab + 'Tab').classed('in active', true);
}

function checkExplorer() {
    if (!explorerL) {
        setTimeout(checkExplorer, 500);
    } else {
        getActMapTiles(activity, explorerZoom, 0, false);
        var explorerCompressed = encodeURI(compress(activity.tiles.map(function(d) {
            return [d.x, d.y]
        }), 0, 2));
        if (contextAthleteId == loggedInAthleteId && (typeof(texplorer[activity.id]) === 'undefined' || texplorer[activity.id] != explorerCompressed)) {
            $.ajax({
                url: '/api/saveExplorerTilesActivity.php?i=' + activity.id + '&t=' + explorerCompressed,
                async: true
            });
        }
    }
}

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

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

    activity.minLat = latExtent[0];
    activity.maxLat = latExtent[1];
    activity.minLng = lngExtent[0];
    activity.maxLng = lngExtent[1];
    segment = clone(route);

    checkExplorer();
}

function setExtent() {
    var linePoints = [];
    llObj = actStrs.filter(function(d) {
        return d.type == 'latlng'
    })[0];

    if (typeof(llObj) !== 'undefined') {
        var llData = llObj.data;

        activity.distanceArr2 = distObj.data;
        activity.elevationArr2 = elevObj.data;
        activity.latLngArr3 = llData;
        activity.latLngArr2 = [];

        for (i = 0; i < activity.distanceArr2.length; i++) {
            activity.latLngArr2.push({
                lat: activity.latLngArr3[i * 2],
                lng: activity.latLngArr3[i * 2 + 1]
            });
        }

        //if (typeof(activity.dataPrivateKey) === 'undefined' || activity.private == 1) {
        startIndex = 0;
        endIndex = activity.latLngArr2.length - 1;
        /*} else {
            var sll = {
                lat: actStrsPublic[0].data[0],
                lng: actStrsPublic[0].data[1]
            };
            var ell = {
                lat: actStrsPublic[0].data[actStrsPublic[0].data.length - 2],
                lng: actStrsPublic[0].data[actStrsPublic[0].data.length - 1]
            };

            for (startIndex = 0; startIndex < activity.latLngArr2.length; startIndex++) {
                if (activity.latLngArr2[startIndex].lat == sll.lat && activity.latLngArr2[startIndex].lng == sll.lng) {
                    break;
                }
            }
            for (endIndex = activity.latLngArr2.length - 1; endIndex >= 0; endIndex--) {
                if (activity.latLngArr2[endIndex].lat == ell.lat && activity.latLngArr2[endIndex].lng == ell.lng) {
                    break;
                }
            }
        }*/

        activity.latLngArr2Copy = clone(activity.latLngArr2);
        activity.elevationArr2Copy = clone(activity.elevationArr2);
        activity.distanceArr2Copy = clone(activity.distanceArr2);

        checkExplorer();

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

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

        /*activity.latLngArr2.forEach(function(d, i) {
            e = d;
            if (isOS) {
                var OSll = getOSPos(d.lat, d.lng);
                e.lat = OSll.lat;
                e.lng = OSll.lng;
            }
            nextTP = new OpenLayers.LonLat(e.lng, e.lat).transform((isOS ? toProjection : fromProjection), toProjection);

            linePoints.push(new OpenLayers.Geometry.Point(nextTP.lon, nextTP.lat));
        });

        activity.linePoints = linePoints;*/

        segment = clone(activity);

        segment.lla = segment.latLngArr2;
        segment.da = segment.distanceArr2;
        segment.ea = segment.elevationArr2;

        if (typeof(activity.dataPrivateKey) !== 'undefined') {
            segment.latLngArr2 = segment.latLngArr2.slice(startIndex, endIndex);
            segment.elevationArr2 = segment.elevationArr2.slice(startIndex, endIndex);
            segment.distanceArr2 = segment.distanceArr2.slice(startIndex, endIndex);
            segment.latLngArr2Copy = segment.latLngArr2Copy.slice(startIndex, endIndex);
            segment.elevationArr2Copy = segment.elevationArr2Copy.slice(startIndex, endIndex);
            segment.distanceArr2Copy = segment.distanceArr2Copy.slice(startIndex, endIndex);

        }
        var startDist = segment.distanceArr2[0];
        for (var i = 0; i < segment.distanceArr2.length; i++) {
            segment.distanceArr2[i] -= startDist;
            segment.distanceArr2Copy[i] -= startDist;
        }

        if (segment.latLngArr2.length > 0) {
            setImages();
        }

        segment.latLngArr2 = segment.latLngArr2Copy = segment.lla;
        segment.distanceArr2 = segment.distanceArr2Copy = segment.da;
        segment.elevationArr2 = segment.elevationArr2Copy = segment.ea;

        /*if (typeof(map3) !== 'undefined' && typeof(map3.controls.length) !== 'undefined') {
            mapExtent = new OpenLayers.Bounds(activity.minLng, activity.minLat, activity.maxLng, activity.maxLat).transform((isOS ? toProjection : fromProjection), toProjection);
            fullMapExtent = mapExtent.clone();

            map3.zoomToExtent(mapExtent);

            var lineString = new OpenLayers.Geometry.LineString(linePoints);
            var bgmf = new OpenLayers.Feature.Vector(lineString, null, mapLineBGDef);
            var fgmf = new OpenLayers.Feature.Vector(lineString.clone(), null, mapLineFGDef);
            highlightFeature = new OpenLayers.Feature.Vector(lineString.clone(), null, mapLineBGSel);


            lineLayer3.addFeatures([bgmf, highlightFeature, fgmf]);

            setMapDetail('O', lineLayer3);

            var startPos = new OpenLayers.LonLat(activity.latLngArr2[0].lng, activity.latLngArr2[0].lat).transform((isOS ? toProjection : fromProjection), toProjection);
            var endPos = new OpenLayers.LonLat(activity.latLngArr2[activity.latLngArr2.length - 1].lng, activity.latLngArr2[activity.latLngArr2.length - 1].lat).transform((isOS ? toProjection : fromProjection), toProjection);

            var size = new OpenLayers.Size(21, 25);
            var offset = new OpenLayers.Pixel(-(size.w / 2), -size.h);
            var startIcon = new OpenLayers.Icon(https + '://' + assetPrefix + '/img/marker-green.png', size, offset);
            var endIcon = new OpenLayers.Icon(https + '://' + assetPrefix + '/img/marker-finish.png', size, offset);

            markersLayer.addMarker(new OpenLayers.Marker(startPos, startIcon));
            markersLayer.addMarker(new OpenLayers.Marker(endPos, endIcon));
        }*/
    }
}

function setMapChartHeight() {
    $('#mapChartContainer2').height(Math.max(350, window.innerHeight - 274));
    $('#mapDomObj2').height($('#mapChartContainer2').height() - 11);
    if (typeof(map2) !== 'undefined') {
        setTimeout(function() {
            map2.invalidateSize()
        }, 400);
    }

    $('#mainChartContainer').height(Math.max(350, window.innerHeight - 300));

    if ($(window).width() < 768) {
        $('#dataTab').height('auto');
        $('#dataTab').css('overflow-y', 'visible')
    } else {
        $('#dataTab').height(Math.max(350, window.innerHeight - 274));
        $('#dataTab').css('overflow-y', 'auto')
    }
}

function scaleExtent(domain) {
    var start = domain[0],
        stop = domain[domain.length - 1];
    return start < stop ? [start, stop] : [stop, start];
}

function scale_linearTickRange(domain, m) {
    var extent = scaleExtent(domain),
        span = extent[1] - extent[0],
        step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)),
        err = m / span * step;
    if (err <= .15) step *= 10;
    else if (err <= .35) step *= 5;
    else if (err <= .75) step *= 2;
    extent[0] = Math.ceil(extent[0] / step) * step;
    extent[1] = Math.floor(extent[1] / step) * step + step * .5;
    extent[2] = step;
    return extent;
}

function swapViewType(type) {
    viewType = type;
    d3.selectAll('g.splitAvg').remove();
    allCharts.forEach(function(d) {
        d.path.style('display', null)
    });
    filterCharts();
}

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

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

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

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

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

$('a[data-toggle="tab"][href="#splitsTab"]').on('shown', function(e) {
    curTab = 'splits';
    d3.selectAll('.floatingVVLogo').style('display', 'none');
    drawAllSplits();
    calSplits();
    d3.select('#typeFlipperDiv').style('display', 'none');
});

$('a[data-toggle="tab"][href="#activitiesTab"]').on('shown', function(e) {
    curTab = 'activities';
    d3.selectAll('.floatingVVLogo').style('display', 'none');
    window.setTimeout(function() {
        setMapChartHeight();
        drawActivities();
    }, 100);
    d3.select('#typeFlipperDiv').style('display', 'none');
});

$('a[data-toggle="tab"][href="#summaryTab"]').on('shown', function(e) {
    curTab = 'summary';
    d3.selectAll('.floatingVVLogo').style('display', 'none');
    setDlWidth();
    d3.select('#typeFlipperDiv').style('display', 'none');
});

d3.select('#zonesTab')
    .append('p')
    .text('Click charts to shade top graph by that chart\'s values.' + (window.chrome ? ' Currently Google Chrome has an issue that can mean the gradient above can look a bit wrong. Let Mr Google know you want it fixing!' : ''));

function setTypeFlipper() {
    d3.select('#typeFlipperDiv')
        .append('select')
        .attr('id', 'typeFlipper')
        .attr('disabled', 'disabled')
        .style('float', 'right')
        .style('margin-left', '10px')
        .style('margin-bottom', '0px')
        .style('width', '100px')
        .on('change', function(d) {
            splitSize = document.getElementById('typeFlipper').options[document.getElementById('typeFlipper').selectedIndex].value;
            filterCharts();
        })
        .each(function(d) {
            var item = d3.select(this);

            splitAuto = item.append('option')
                .attr('value', 'auto')
                .text('Auto');

            item.append('option')
                .attr('id', 'typeFlipperLapsOption')
                .style('display', typeof(lapArr) === 'undefined' ? 'none' : (lapArr.length == 0 ? 'none' : null))
                .attr('value', 'laps')
                .text('Laps');

            item.append('option')
                .attr('value', 'half')
                .text('Halves');

            item.append('option')
                .attr('value', 'third')
                .text('Thirds');

            item.append('option')
                .attr('value', 'quarter')
                .text('Quarters');

            if (mainXObj.type == 'time') {
                item.append('option')
                    .attr('value', 10)
                    .text('10 s');

                item.append('option')
                    .attr('value', 30)
                    .text('30 s');

                item.append('option')
                    .attr('value', 60)
                    .text('1 min');

                item.append('option')
                    .attr('value', 120)
                    .text('2 min');

                item.append('option')
                    .attr('value', 300)
                    .text('5 min');

                item.append('option')
                    .attr('value', 600)
                    .text('10 min');
            } else {
                item.append('option')
                    .attr('value', 0.1 / lrgLenMult)
                    .text('0.1 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 0.25 / lrgLenMult)
                    .text('0.25 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 0.5 / lrgLenMult)
                    .text('0.5 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 1 / lrgLenMult)
                    .text('1 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 2.5 / lrgLenMult)
                    .text('2.5 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 5 / lrgLenMult)
                    .text('5 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 10 / lrgLenMult)
                    .text('10 ' + lrgLenUnit);
                
                item.append('option')
                    .attr('value', 25 / lrgLenMult)
                    .text('25 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 50 / lrgLenMult)
                    .text('50 ' + lrgLenUnit);

                item.append('option')
                    .attr('value', 100 / lrgLenMult)
                    .text('100 ' + lrgLenUnit);
             }
        })

    d3.select('#typeFlipperDiv')
        .append('div')
        .style('float', 'right')
        .classed('btn-group', true)
        .attr('data-toggle', 'buttons-radio')
        .each(function(d) {
            var item = d3.select(this);

            item.append('button')
                .attr('data-val', 'line')
                .classed('btn btn-primary btn-small active', true)
                .text('Line');

            item.append('button')
                .attr('data-val', 'bar')
                .classed('btn btn-primary btn-small', true)
                .text('Bar');

            item.append('button')
                .attr('data-val', 'box')
                .classed('btn btn-primary btn-small', true)
                .text('Box');
        });

    d3.selectAll('#typeFlipperDiv button')
        .on('click', function(d) {
            swapViewType(d3.select(this).attr('data-val'));
            if (d3.select(this).attr('data-val') == 'line') {
                d3.select('#typeFlipper').attr('disabled', 'disabled');
            } else {
                d3.select('#typeFlipper').attr('disabled', null);
            }
        })
}

function notAllowed() {
    if (activity.athleteId != contextAthleteId) {
        d3.select('h1').text('This activity isn\'t yet available in VeloViewer');
        d3.select('#loadingDiv')
            .classed('lead', true)
            .style('margin-bottom', '20px')
            .html('This activity is yet to be synced with VeloViewer. Head to your <a href="/update">Update page</a> to sync your data from Strava or to the <a href="http://blog.veloviewer.com/about/">About page</a> to see what VeloViewer has to offer.');
    } else {
        d3.select('h1').text('Oops! There has been a problem');
        d3.select('#loadingDiv')
            .classed('lead', true)
            .style('margin-bottom', '20px')
            .text('There was a problem getting your data. Please refresh the page and see if that sorts it.');
    }
}

function notPRO() {
    d3.select('h1').text('This activity isn\'t yet available in VeloViewer');
    d3.select('#loadingDiv')
        .classed('lead', true)
        .style('margin-bottom', '20px')
        .html('This activity is yet to be synced with VeloViewer. Head to your <a href="/update">Update page</a> to sync your data from Strava or to the <a href="http://blog.veloviewer.com/about/">About page</a> to see what VeloViewer has to offer. If you aren\'t yet a PRO user then you will only be able to view the details of the initial 25 activities that were synched on your first visit.  Upgrade to PRO to view all activities in VeloViewer and to support the continued running of the site.');
}

if (activity.athleteId == -1 || activity.athleteId == -100) {
    if (activity.athleteId == -1) {
        notAllowed();
    } else {
        notPRO();
    }
} else {
    if (activity.manual == 0) {
        getData();
    } else {
        d3.selectAll('#myTabs li')
            .each(function(d, i) {
                var item = d3.select(this);

                if (i < 5) {
                    item.style('display', 'none');
                }
            });
        d3.select('#loadingDiv').style('display', 'none');
        d3.select('#mainContent').style('display', null);
        $('#myTabs li:eq(5) a').tab('show');
        setSummary();
    }
}

function setSummary() {
    var outerDiv = d3.selectAll('#summaryTab')
        .append('div')
        .classed('well', true);
    var dl = outerDiv.append('dl')
        .classed('dl-horizontal', true);

    dl.append('dt').text('Name:');
    dl.append('dd').text(activity.name);
    dl.append('dt').text('Type:');
    dl.append('dd').text(activity.type);
    dl.append('dt').text('When:');
    dl.append('dd').text(fDate(new Date(activity.when)));
    dl.append('dt').text('VVOM:').attr('title', 'VVOM (VeloViewer Objective Measure) - a value to represent the difficulty of the parcours based on it\'s distance, gradients and altitude.');
    dl.append('dd').text(f2dp(activity.vvom)).attr('title', 'VVOM (VeloViewer Objective Measure) - a value to represent the difficulty of the parcours based on it\'s distance, gradients and altitude.');
    if (activity.city != '') {
        dl.append('dt').text('Where:');
        dl.append('dd').text(activity.city == null && activity.state == null ? 'n/a' : activity.city + ', ' + activity.state);
    }
    if (activity.distance > 0) {
        dl.append('dt').text('Distance:');
        dl.append('dd').text(d3.format(',.1f')(activity.distance * lrgLenMult) + ' ' + lrgLenUnit);
    }
    if (activity.elevationGain > 0) {
        dl.append('dt').text('Elevation gain:');
        dl.append('dd').text(f1dp(activity.elevationGain * smlLenMult) + ' ' + smlLenUnit);
    }
    dl.append('dt').text('Elapsed time:');
    dl.append('dd').text(activity.elapsedTime.toHrMinSec());
    dl.append('dt').text('Moving time:');
    dl.append('dd').text(activity.movingTime.toHrMinSec());
    dl.append('dt').text('Gear:');
    dl.append('dd').html(activity.gear == null || activity.gear == '' ? 'n/a' : activity.gear);
    dl.append('dt').text('Device/App:');
    dl.append('dd').html(typeof(activity.dv) === 'undefined' || activity.dv == null || activity.dv == '' ? 'n/a' : activity.dv);

    dl.append('dt').text('Links:');
    dl.append('dd')
        .append('a')
        .attr('href', 'http://www.strava.com/activities/' + activity.id)
        .attr('target', '_blank')
        .text('View on Strava');

    if (typeof(llObj) !== 'undefined' && loggedInAthleteId == activity.athleteId) {
        if (activity.kmlPopulated) {
            dl.append('dd')
                .append('a')
                .attr('href', 'https://s3.veloviewer.com/' + activity.kmlKey)
                .text('View in Google Earth (kml)');
        } else {
            dl.append('dd')
                .append('a')
                .attr('href', '#')
                .attr('id', 'genKml')
                .on('click', function() {
                    $.ajax({
                        url: '/api/createActivityKml.php?f=' + encodeURIComponent(activity.dataPrivateKey) + '&n=' + encodeURIComponent(activity.name),
                        async: true,
                        success: function(result, textStatus, request) {
                            d3.select('#genKml')
                                .attr('href', 'https://s3.veloviewer.com/' + activity.kmlKey)
                                .text('View in Google Earth (kml)')
                                .on('click', null);
                        },
                        error: function() {
                            alert('Unfortunately something has gone wrong. If this keeps happening then let me know.')
                        }
                    });
                    return false;
                })
                .text('Generate KML file to view in Google Earth');
        }
    }

    if (typeof(activity.dataPrivateKey) !== 'undefined') {
        if (!isFree) {
            outerDiv.append('button')
                .classed('btn btn-primary', true)
                .on('click', function() {
                    if (isBeta) {
                        $.ajax({
                            url: '/api/deleteActivity1/' + activity.id,
                            success: function() {
                                alert('Activity removed. Please head to your Update page where you will be prompted to re-upload this activity and any new segments or placing changes will be found.');
                            }
                        });
                    }
                    /*$.ajax({
                      url: '/api/activity/' + activity.id + '/upload' + dataVer + '?f=' + activity.flagged + '&p=' + activity.private + '&l=' + (typeof(activity.lastChanged) !== 'undefined' ? activity.lastChanged.replace(' ', 'T') + 'Z' : tacts[activity.id].lc) + '&n=2',
                      async: true,
                      success: function(result, textStatus, request) {
                        var obj = result.parseJSON();
                        if (typeof(obj.newEfforts) !== 'undefined') {
                          $.ajax({
                            url: '/api/update/',
                            async: true
                          });

                          if (obj.newEfforts > 0) {
                            alert('Details and segments updated. There are ' + obj.newEfforts + ' new segments. You will need to "Get Everything" on the update page to find out what they are and get their placings.');
                          } else {
                            alert('Details and segments updated.');
                          }
                          location.reload();
                        } else {
                          if (typeof(obj.effIds) !== 'undefined') {
                            $.ajax({
                              url: '/api/processActs.php',
                              async: false
                            });
                            $.ajax({
                              url: '/api/processEffs.php',
                              async: false
                            });
                            $.ajax({
                              url: '/api/update/',
                              async: true
                            });
                            var newEffIds = obj.effIds.split(',');
                            var oldEffIds = teffs[activity.id].map(function(d) {
                              return d._i
                            });
                            var intersect = oldEffIds.intersect(newEffIds);
                            if (newEffIds.length - intersect.length > 0) {
                              alert('Details and segments refreshed. There are ' + (newEffIds.length - intersect.length) + ' new segments. You will need to "Get Everything" on the update page to find out what they are and get their placings.');
                            } else {
                              alert('Details and segments refreshed. Refresh page to see any changes.');
                            }
                          } else {
                            alert('Unfortunately there has been a problem updating the data. If this keeps happening then let me know.')
                          }
                        }
                      },
                      error: function() {
                        alert('Unfortunately there has been a problem updating the data. If this keeps happening then let me know.')
                      }
                    });*/
                })
                .text('Update activity');
            outerDiv.append('span')
                .style('margin-left', '5px')
                .html('(Pressing this button will actually remove this activity from VeloViewer but allow you to re-upload it to refresh details, segments and placings on your <a href="/update">Update page</a>)');
        } else {
            outerDiv.append('p')
                .html('Updating activities to correct changed data or segments is only available to PRO users.');
        }
    }
}

/*d3.selectAll('#segmentsTab')
  .append('p')
  .classed('lead', true)
  .html('Not yet implemented.');*/

var commonSplits = [{
    type: 't',
    x: 5
}, {
    type: 't',
    x: 10
}, {
    type: 't',
    x: 30
}, {
    type: 't',
    x: 60
}, {
    type: 't',
    x: 2 * 60
}, {
    type: 't',
    x: 5 * 60
}, {
    type: 't',
    x: 8 * 60
}, {
    type: 't',
    x: 10 * 60
}, {
    type: 't',
    x: 20 * 60
}, {
    type: 't',
    x: 30 * 60
}, {
    type: 't',
    x: 60 * 60
}, {
    type: 'd1',
    x: 0.1 / lrgLenMult
}, {
    type: 'd1',
    x: 0.25 / lrgLenMult
}, {
    type: 'd1',
    x: 0.5 / lrgLenMult
}, {
    type: 'd1',
    x: 1 / lrgLenMult
}, {
    type: 'd1',
    x: 5 / lrgLenMult
}, {
    type: 'd1',
    x: 10 / lrgLenMult
}, {
    type: 'd1',
    x: 21097.5
}, {
    type: 'd1',
    x: 42195
}];

commonSplits.forEach(function(d, i) {
    d.id = i;
});

var splits = [];

function getInterval(data, type, ext, minMax, yVal, grpType, isSpeed, count) {
    var bisect = d3.bisector(function(d) {
        return d[type]
    }).left;
    for (var i = 0; i < data.length; i++) {
        var endOfInt = bisect(data, data[i][type] + ext, i);
        data[i].tmpEnd = endOfInt;
        data[i].tmpLen = 0;
        if (typeof(data[endOfInt]) !== 'undefined') {
            data[i].tmpLen = data[endOfInt][type] - data[i][type];
            if (isSpeed) {
                data[i].tmp = (data[endOfInt].d1 - data[i].d1) / (data[endOfInt].t - data[i].t);
            } else {
                data[i].tmp = d3[grpType](data.slice(i, endOfInt), function(d) {
                    return d[yVal]
                });
            }
        }
        if (endOfInt >= data.length - 1) {
            i = data.length;
        }
    }
    data = data.filter(function(d) {
        return typeof(d.tmp) !== 'undefined'
    }).sort(function(a, b) {
        return (minMax == 'min' ? a.tmp - b.tmp : b.tmp - a.tmp)
    });
    data = data.filter(function(d) {
        return d.tmpLen >= ext
    });
    if (data.length > 0) {
        var ra = [];
        var da = clone(data);
        for (var i = 0; i < count; i++) {
            ra.push(clone(da[0]));
            da = da.filter(function(d) {
                return d.i > da[0].tmpEnd || d.tmpEnd < da[0].i
            });
            if (da.length == 0) {
                i = count;
            }
        }
        return ra;
    }
    return null;
}

var splitVals = [];

function calSplits() {
    /*  if (!splitsPopulated) {
      splitsPopulated = true;*/
    setAltGain();

    splitVals = allCharts.filter(function(d, i) {
        return i > 0 && (d.yd.type == 'heartrate' || d.yd.type == 'velocity_smooth' || d.yd.type == 'cadence' || d.yd.type == 'watts_calc' || d.yd.type == 'watts' || d.yd.type == 'altitude_gain')
    });
    splitVals = d3.merge([allCharts.filter(function(d, i) {
        return i > 0 && d.yd.type == 'velocity_smooth'
    }), splitVals]);
    splitVals = splitVals.map(function(d, i) {
        var obj = d;
        if (typeof(d.splits) === 'undefined') {
            d.splits = clone(splits);
        } else {
            d.splits = d3.merge([d.splits, clone(splits).filter(function(e) {
                return d.splits.map(function(f) {
                    return f.id
                }).indexOf(e.id) == -1
            })])
        }
        d.splits = d.splits.filter(function(d) {
            return typeof(distObj) !== 'undefined' || d.type == 't'
        });
        return obj;
    });

    /*var velSplitId = splitVals.map(function(d){return d.yd.type}).indexOf('velocity_smooth');
    if (velSplitId > -1) {
      var dtSplit = clone(splitVals[velSplitId]);
      dtSplit.yd.type = 'dist_time';
      splitVals.unshift();
    }*/

    drawSplits();
    window.setTimeout(popSplits, 50);
    //}
}

var splitCount = 1;

function popSplits() {
    for (var i = 0; i < splitVals.length; i++) {
        for (var j = 0; j < splitVals[i].splits.length; j++) {
            if (typeof(splitVals[i].splits[j].obj) === 'undefined') {
                var start = new Date();
                splitVals[i].splits[j].obj = getInterval(clone(splitVals[i].d), splitVals[i].splits[j].type, splitVals[i].splits[j].x, 'max', (splitVals[i].yd.type == 'altitude_gain' ? 'y1' : 'y'), (splitVals[i].yd.type == 'altitude_gain' ? 'sum' : 'mean'), splitVals[i].yd.type == 'velocity_smooth', splitCount);
                splitVals[i].splits[j].ms = new Date() - start;

                //console.debug(splitVals[i].yd.type + ' - ' + splitVals[i].splits[j].type + ' - ' + i + ' - ' + splitVals[i].splits[j].x + ' - ' + (new Date() - start));

                for (var k = 0; k < splitCount; k++) {
                    if (splitVals[i].splits[j].obj == null || splitVals[i].splits[j].obj.length - 1 < k) {
                        if (i == 0 && splitVals[i].yd.type == 'velocity_smooth') {
                            d3.select('#split_' + splitVals[i].yd.type + '_' + (i + 1) + '_' + k + '_' + splitVals[i].splits[j].id)
                                .text('n/a')
                                .style('display', k > 0 ? 'none' : null);
                        }
                        d3.select('#split_' + splitVals[i].yd.type + '_' + i + '_' + k + '_' + splitVals[i].splits[j].id)
                            .text('n/a')
                            .style('display', k > 0 ? 'none' : null);
                    } else {
                        if (wattsAvl && splitVals[i].yd.type == 'watts' && splitVals[i].splits[j].type == 't' && splitVals[i].splits[j].x == 8 * 60 && splitVals[i].splits[j].obj.length > 1) {
                            d3.select('#ftp8min').text('FTP: ' + fInt(0.9 * (splitVals[i].splits[j].obj[0].tmp + splitVals[i].splits[j].obj[1].tmp) / 2) + ' W');
                        }
                        if (wattsAvl && splitVals[i].yd.type == 'watts' && splitVals[i].splits[j].type == 't' && splitVals[i].splits[j].x == 20 * 60 && splitVals[i].splits[j].obj.length > 0) {
                            d3.select('#ftp20min').text('FTP: ' + fInt(0.95 * splitVals[i].splits[j].obj[0].tmp) + ' W');
                        }

                        /*if (splitVals[i].yd.type == 'heartrate' && splitVals[i].splits[j].type == 't' && splitVals[i].splits[j].x == 8 * 60 && splitVals[i].splits[j].obj.length > 1) {
                            d3.select('#lthr8min').text('LTHR: ' + fInt(0.92 * (splitVals[i].splits[j].obj[0].tmp + splitVals[i].splits[j].obj[1].tmp) / 2) + ' bpm');
                        }*/
                        if (splitVals[i].yd.type == 'heartrate' && splitVals[i].splits[j].type == 't' && splitVals[i].splits[j].x == 20 * 60 && splitVals[i].splits[j].obj.length > 0) {
                            d3.select('#lthr20min').text('LTHR: ' + fInt(0.98 * splitVals[i].splits[j].obj[0].tmp) + ' bpm');
                        }

                        if (i == 0 && splitVals[i].yd.type == 'velocity_smooth') {
                            d3.select('#split_' + splitVals[i].yd.type + '_' + i + '_' + k + '_' + splitVals[i].splits[j].id)
                                .text((splitVals[i].splits[j].type == 't' ?
                                    distObj.f2(splitVals[i].d[splitVals[i].splits[j].obj[k].tmpEnd].d1 - splitVals[i].d[splitVals[i].splits[j].obj[k].i].d1) :
                                    (splitVals[i].d[splitVals[i].splits[j].obj[k].tmpEnd].t - splitVals[i].d[splitVals[i].splits[j].obj[k].i].t).toHrMinSec()));

                            d3.select('#split_' + splitVals[i].yd.type + '_' + (i + 1) + '_' + k + '_' + splitVals[i].splits[j].id)
                                .text(splitVals[i].yd.f(splitVals[i].splits[j].obj[k].tmp));
                        } else {
                            d3.select('#split_' + splitVals[i].yd.type + '_' + i + '_' + k + '_' + splitVals[i].splits[j].id)
                                .text(splitVals[i].yd.f(splitVals[i].splits[j].obj[k].tmp));
                        }
                    }
                }
                if (splitVals[i].splits[j].obj != null) {
                    d3.selectAll('.splitVal')
                        .style('background', null);

                    d3.select('#split_' + splitVals[i].yd.type + '_' + i + '_' + 0 + '_' + splitVals[i].splits[j].id)
                        .style('background', 'lightgray')
                        .transition()
                        .duration(1000)
                        .style('background', 'white')
                        .transition()
                        .duration(0)
                        .style('background', null);

                    brushExtent = [splitVals[i].splits[j].obj[0].d, splitVals[i].d[splitVals[i].splits[j].obj[0].tmpEnd].d];

                    allCharts[0].b.extent(brushExtent);
                    allCharts[0].mb.call(allCharts[0].b);

                    filterCharts();
                    setBrushText();
                }

                window.setTimeout(popSplits, 50);
                setScrollingTableSize();
                return;
            }
        }
    }
}

function drawSplits() {
    var thead, tbody, t = d3.select('#splitsTab div.splitsDiv .dataTable');

    if (t[0][0] == null) {
        t = d3.select('#splitsTab div.splitsDiv')
            .append('table')
            .classed('table dataTable table-striped table-condensed table-bordered table-hover', true);

        thead = t.append('thead')
            .append('tr');
        tbody = t.append('tbody');
    } else {
        thead = t.select('thead tr');
        tbody = t.select('tbody');
    }

    var ths = thead.selectAll('th')
        .data([null].concat(splits.filter(function(d) {
            return typeof(distObj) !== 'undefined' || d.type == 't'
        })), function(e) {
            return (e == null ? -1 : e.id)
        });

    ths.enter()
        .append('th')
        .text(function(d) {
            if (d == null) {
                return 'Best'
            }
            return (d.type == 't' ? (d.x < 60 ? d.x + ' s' : (d.x / 60).round(2) + ' min') : (d.x * lrgLenMult).round(2) + ' ' + lrgLenUnit);
        });

    var trs = tbody.selectAll('tr')
        .data(splitVals);

    trs.enter()
        .append('tr')
        .append('td')
        .text(function(d, i) {
            if (i == 0 && d.yd.type == 'velocity_smooth') {
                return 'Dist/Times'
            } else {
                return d.yd.title
            }
        });

    trs.each(function(d, i) {
        var item = d3.select(this);

        var tds = item.selectAll('td')
            .data(d.splits, function(e) {
                return e.id
            });

        etds = tds.enter()
            .append('td');

        if (d.yd.type == 'watts') {
            etds.filter(function(e) {
                    return e.type == 't' && e.x == 8 * 60
                }).append('span')
                .style('float', 'right')
                .attr('id', 'ftp8min');

            etds.filter(function(e) {
                    return e.type == 't' && e.x == 20 * 60
                }).append('span')
                .style('float', 'right')
                .attr('id', 'ftp20min');
        }

        if (d.yd.type == 'heartrate') {
            etds.filter(function(e) {
                    return e.type == 't' && e.x == 8 * 60
                }).append('span')
                .style('float', 'right')
                .attr('id', 'lthr8min');

            etds.filter(function(e) {
                    return e.type == 't' && e.x == 20 * 60
                }).append('span')
                .style('float', 'right')
                .attr('id', 'lthr20min');
        }

        for (var j = 0; j < splitCount; j++) {
            etds.append('a')
                .classed('splitVal', true)
                .attr('data-index', j)
                .attr('id', function(e) {
                    return 'split_' + d.yd.type + '_' + i + '_' + j + '_' + e.id
                })
                .style('cursor', 'pointer')
                .on('click', function(f) {
                    if (typeof(f.obj) === 'undefined') {
                        return;
                    }
                    var j = parseInt(d3.select(this).attr('data-index'));
                    d3.selectAll('.splitVal')
                        .style('background', null);
                    d3.select(this)
                        .style('background', 'lightgray');

                    brushExtent = [f.obj[j].d, d.d[f.obj[j].tmpEnd].d];

                    allCharts[0].b.extent(brushExtent);
                    allCharts[0].mb.call(allCharts[0].b);

                    filterCharts();
                    setBrushText();
                })
                .on('mouseover', function(f) {
                    if (typeof(f.obj) === 'undefined') {
                        return;
                    }
                    var j = parseInt(d3.select(this).attr('data-index'));
                    allCharts[0].areaHighlight.style('display', null);
                    allCharts[0].areaHighlight.attr("d", allCharts[0].a(allCharts[0].d.slice(f.obj[j].i, f.obj[j].tmpEnd)
                        .filter(function(e, j) {
                            return j % Math.ceil(allCharts[0].d.length / (allCharts[0].w / 1)) == 0
                        })));
                })
                .on('mouseout', function(f) {
                    allCharts[0].areaHighlight.style('display', 'none');
                })
                .text('...');
        }
        /*.text(function(f) {
              return f.obj == null ? 'n/a' : d.yd.f(f.obj.tmp)
            })*/
    });

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

    setScrollingTableSize();
}

function drawCommonSplits() {
    commonSplits = commonSplits.filter(function(d) {
        return wattsAvl || d.x != 8 * 60
    });

    var btns = addSplitPre.selectAll('button.addCommonSplitBtn')
        .data(commonSplits
            .filter(function(d) {
                return (typeof(distObj) !== 'undefined' || d.type == 't') && splits.map(function(e) {
                    return e.id
                }).indexOf(d.id) == -1
            }),
            function(d) {
                return d.id
            });

    btns.enter()
        .append('button')
        .classed('btn btn-primary addCommonSplitBtn', true)
        .on('click', function(d) {
            if (d.x == 8 * 60) {
                splitCount = 2;
            }
            splits.push(d);
            calSplits();
            drawCommonSplits();
        })
        .text(function(d) {
            return (d.type == 't' ? (d.x < 60 ? d.x + ' s' : (d.x / 60).round(2) + ' min' + ((d.x == 20 * 60 || d.x == 8 * 60) && wattsAvl ? ' (FTP)' : '')) : (d.x * lrgLenMult).round(2) + ' ' + lrgLenUnit);
        });

    btns.exit().remove();
}

var addSplitPre, addSplit, splitsDrawn = false;

function drawAllSplits() {
    if (!splitsDrawn) {

        // defualt splits
        if (wattsAvl) {
            splits.push(commonSplits[0]);
            splits.push(commonSplits[3]);
            splits.push(commonSplits[5]);
            splits.push(commonSplits[8]);
        } else {
            splits.push(commonSplits[3]);
            splits.push(commonSplits[5]);
            splits.push(commonSplits[12]);
            splits.push(commonSplits[13]);
        }

        splitsDrawn = true;

        d3.select('#splitsTab')
            .append('div').style('margin-bottom', '10px')
            .classed('tableWrapper st', true).style('height', '80px')
            .append('div').classed('st-body splitsDiv', true);

        addSplitPre = d3.select('#splitsTab').
        append('div');
        addSplitPre.append('span')
            .classed('hidden-phone', true)
            .text('Add common split: ');

        drawCommonSplits();

        addSplit = d3.select('#splitsTab').
        append('div')
            .style('margin-top', '5px')
            .style('margin-bottom', '5px');
        addSplit.append('span')
            .text('Custom split: ');
        addSplit.append('input')
            .attr('id', 'addSplitValue')
            .style('margin-bottom', '0px')
            .style('width', '75px')
            .attr('type', 'number');
        var addSplitSelect = addSplit.append('select')
            .attr('id', 'addSplitSelect')
            .style('margin-bottom', '0px')
            .style('width', '75px');
        addSplitSelect.append('option')
            .attr('value', 't-s')
            .text('secs');
        addSplitSelect.append('option')
            .attr('value', 't-m')
            .text('mins');
        if (typeof(distObj) !== 'undefined') {
            addSplitSelect.append('option')
                .attr('value', 'd1')
                .text('km');
            addSplitSelect.append('option')
                .attr('value', 'd2')
                .text('mile');
        }
        addSplit.append('select')
            .attr('id', 'splitCountSelect')
            .style('margin-bottom', '0px')
            .style('width', '100px')
            .selectAll('option')
            .data(d3.range(1, 51))
            .enter()
            .append('option')
            .attr('value', function(d) {
                return d
            })
            .text(function(d) {
                return d + ' split' + (d > 1 ? 's' : '')
            });
        addSplit.append('button')
            .classed('btn btn-primary', true)
            .on('click', function() {
                var type = document.getElementById('addSplitSelect').value;
                splitCount = parseInt(document.getElementById('splitCountSelect').value);
                var divider = 1;
                if (type.indexOf('-') > 0) {
                    var typeArr = type.split('-');
                    type = typeArr[0];
                }
                if (type == 't') {
                    if (typeArr[1] == 'm') {
                        divider = 1 / 60;
                    }
                } else {
                    if (type == 'd1') {
                        divider = 0.001;
                    } else {
                        type = 'd1';
                        divider = 0.000621371192;
                    }
                }

                var value = document.getElementById('addSplitValue').value;
                if (isNaN(parseFloat(value))) {
                    alert('You must enter a valid number.');
                } else {
                    value = parseFloat(value);

                    splits.push({
                        type: type,
                        x: value / divider,
                        id: Math.max(d3.max(splits, function(d) {
                            return d.id
                        }) + 1, d3.max(commonSplits, function(d) {
                            return d.id
                        }) + 1)
                    });
                    calSplits();
                }
            })
            .text('Add');
    }
}

function colorChart(chartIndex) {
    d3.selectAll('.keyRect').remove();

    if (chartIndex > -1) {
        var isGrade = allCharts[chartIndex].yd.type == 'grade_smooth';
        currentChartColor = chartIndex;
        if (typeof(allCharts[0].grad) === 'undefined') {
            allCharts[0].grad = allCharts[0].svg.append("svg:defs").append("svg:linearGradient");
        }
        var color;
        var colDom;
        var divider;
        if (typeof(distObj) !== 'undefined' && typeof(elevObj) !== 'undefined') {
            divider = distObj.data.last();
        } else {
            divider = activity.elapsedTime;
        }

        var gradData = allCharts[chartIndex].d;

        allCharts[0].grad.attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "100%")
            .attr("y2", "0%")
            .attr("spreadMethod", "pad");

        allCharts[0].areaRect.style("fill", "url(#gradient)")
            .style('fill-opacity', 1);

        allCharts[0].grad.selectAll('stop').remove();

        var prevI = -1;
        if (isGrade) {
            var stopData = d3.merge(allCharts[0].tData.slice(1).map(function(d, i) {
                return [{
                    d: allCharts[0].tData[i].d,
                    g: allCharts[0].tData[i + 1].g
                }, {
                    d: allCharts[0].tData[i + 1].d - 0.001,
                    g: allCharts[0].tData[i + 1].g
                }]
            }));
        } else {
            var lData = gradData.map(function(d) {
                return {
                    x: d.d,
                    y: d.y,
                    z: 0,
                    i: d.i
                }
            });

            var stopData = lData;
            if (lData.length >= 1000) {
                var simpVal = 10;
                stopData = simplify(lData, simpVal, true);
                while (stopData.length < 1000 && simpVal > 0.01) {
                    stopData = simplify(lData, simpVal, true);
                    simpVal = simpVal / 2;
                }
            }

            stopData = stopData.map(function(d) {
                if (prevI != -1) {
                    e = gradData.slice(prevI, d.i);
                    var obj = {
                        d: gradData[d.i].d,
                        y: (allCharts[chartIndex].type == 'velocity_smooth' ?
                            (e.length == 0 ? 0 :
                                (e.length == 1 ? e[0].y :
                                    (e[e.length - 1].d1 - e[0].d1) / (d3.sum(e, function(f, j) {
                                        return (f.i != e[e.length - 1].i ? (f.y > 0 ? f.t1 : 0) : 0)
                                    })))) :
                            d3.mean(d3.merge(e.map(function(f) {
                                return f.y.toArray(f.t1)
                            }))))
                    };
                    prevI = d.i;
                    return obj;
                }
                prevI = d.i;
                return {
                    d: 0,
                    y: d.y
                };
            });
        }

        colDom = d3.scale.linear()
            .range(d3.extent(stopData.map(function(d) {
                return d.g ? d.g * 100 : d.y
            })))
            .domain([0, 4]);

        if (allCharts[chartIndex].yd.type == 'grade_smooth') {
            color = colScale;
        } else {
            color = d3.scale.linear()
                .range([
                    'rgb(255, 255, 178)',
                    'rgb(254, 204, 92)',
                    'rgb(253, 141, 60)',
                    'rgb(240, 59, 32)',
                    'rgb(189, 0, 38)'
                ]);

            color.domain([colDom(0), colDom(1), colDom(2), colDom(3), colDom(4)]);
        }

        /*
gradData.filter(function(e, j) {
      return j % Math.ceil(allCharts[0].d.length / (allCharts[0].w / 1)) == 0
    })
    */

        var stops = allCharts[0].grad.selectAll('stop')
            .data(stopData).enter()
            .append('svg:stop')
            .attr("offset", function(d) {
                return (100 * d.d / divider) + '%'
            })
            .attr("stop-color", function(d) {
                return color(isGrade ? d.g : d.y)
            })
            .attr("stop-opacity", 1);

        var keyPartWidth = (allCharts[chartIndex].w1 - 4) / 100;

        allCharts[chartIndex].c1.key
            .selectAll('rect')
            .data(d3.range(0, 100, 1))
            .enter()
            .append('rect')
            .classed('keyRect', true)
            .attr('x', function(d) {
                return d * keyPartWidth
            })
            .attr('width', keyPartWidth)
            .attr('height', 10)
            .style('fill', function(d) {
                return color(colDom(d / 25) / (isGrade ? 100 : 1))
            });
    } else {
        allCharts[0].areaRect
            .style('fill-opacity', 0.3)
            .style('fill', chartColors[allCharts[0].yd.order]);
    }
}

var segmentsPrepared = false;

function prepareSegments(segs) {
    if (!segmentsPrepared) {
        d3.select('#segmentDataLoading').remove();
        segmentsPrepared = true;
        d3.selectAll('#dataTable').classed('table-bordered', true);
        segs.forEach(function(d) {
            if (!isBeta) {
                d.eSt = (parseDateTime(segs[0].esd) - parseDateTime(activity.when2)) / 1000;
                d.startIndex = d3.bisectLeft(allCharts[0].d.map(function(e) {
                    return e.t
                }), (parseDateTime(d.esd) - parseDateTime(activity.when2)) / 1000);
                d.endIndex = d3.bisectLeft(allCharts[0].d.map(function(e) {
                    return e.t
                }), d.eet + (parseDateTime(d.esd) - parseDateTime(activity.when2)) / 1000);
            }
            if (typeof(hrtObj) !== 'undefined') {
                //d.ehr = d3.mean(hrtObj.data.slice(d.startIndex, d.endIndex));
                d.ehr = d3.mean(d3.merge(allCharts.filter(function(d) {
                    return d.yd.type == hrtObj.type
                })[0].d.slice(d.startIndex, d.endIndex).map(function(f) {
                    return f.y.toArray(f.t1)
                })));
            } else {
                var index = columns.map(function(e) {
                    return e.field
                }).indexOf('ehr');
                if (index > -1) {
                    columns.splice(index, 1);
                }
            }
            if (typeof(cadObj) !== 'undefined') {
                d.ecad = d3.mean(d3.merge(allCharts.filter(function(d) {
                    return d.yd.type == cadObj.type
                })[0].d.slice(d.startIndex, d.endIndex).map(function(f) {
                    return f.y.toArray(f.t1)
                })));
            } else {
                var index = columns.map(function(e) {
                    return e.field
                }).indexOf('ecad');
                if (index > -1) {
                    columns.splice(index, 1);
                }
            }
            if (typeof(pwrObj) !== 'undefined') {
                d.ep = d3.mean(d3.merge(allCharts.filter(function(d) {
                    return d.yd.type == pwrObj.type
                })[0].d.slice(d.startIndex, d.endIndex).map(function(f) {
                    return f.y.toArray(f.t1)
                })));
            } else {
                var index = columns.map(function(e) {
                    return e.field
                }).indexOf('ep');
                if (index > -1) {
                    columns.splice(index, 1);
                }
            }
        });

        if (!isBeta) {
            drawSegments();
        }
    } else {
        setScrollingTableSize();
    }
}

function drawSegments() {
    var thr = d3.select('#dataTable thead tr');
    var ths = thr.selectAll('th')
        .data(columns, function(d) {
            return d.id
        });

    ths.enter()
        .append('th')
        .html(function(d) {
            return d.title
        })
        .attr('title', function(d) {
            return d.lngDec
        });

    ths.exit().remove();

    var tb = d3.select('#dataTable tbody');
    var trs = tb.selectAll('tr')
        .data(segments, function(d) {
            return d.eid
        });

    trs.enter()
        .append('tr')
        .classed('success', function(d) {
            return d.eprr == 1
        })
        .on('click', function(d) {
            brushExtent = [distObj.data[d.startIndex], distObj.data[d.endIndex]];

            allCharts[0].b.extent(brushExtent);
            allCharts[0].mb.call(allCharts[0].b);

            filterCharts();
            setBrushText();
        })
        .on('mouseover', function(d) {
            allCharts[0].areaHighlight.style('display', null);
            allCharts[0].areaHighlight.attr("d", allCharts[0].a(allCharts[0].d.slice(d.startIndex, d.endIndex)
                .filter(function(e, j) {
                    return j % Math.ceil(allCharts[0].d.length / (allCharts[0].w / 1)) == 0
                })));
        })
        .on('mouseout', function(d) {
            allCharts[0].areaHighlight.style('display', 'none');
        })
        .each(function(d) {
            var item = d3.select(this);

            var tds = item.selectAll('td')
                .data(columns, function(e) {
                    return e.id
                });

            tds.enter()
                .append('td')
                .html(function(e) {
                    if (e.field == '') {
                        return e.f(d);
                    } else {
                        return e.f(d[e.field]);
                    }
                });
            tds.exit().remove();
        });

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

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

    setScrollingTableSize();
}

if (locked) {
    d3.select('#saveThisView').style('display', 'none');
} else {
    d3.select('#saveThisView')
        .on('click', function() {
            $('#pleaseWaitDialog').modal();
            setTimeout(function() {
                displayImage();
            }, 100);
        });
}

function DTW1(a, b) {
    var dtw = [],
        a1 = a.a,
        alatmin = a.latmin,
        alatmax = a.latmax,
        alngmin = a.lngmin,
        alngmax = a.lngmax,
        blatmin = 1000,
        blatmax = -1000,
        blngmin = 1000,
        blngmax = -1000;

    aDist = a.dist;

    var b1 = [];
    for (var i = 0; i + 2 < b.length / 2; i++) {
        blatmin = Math.min(b[i * 2], blatmin);
        blatmax = Math.max(b[i * 2], blatmax);
        blngmin = Math.min(b[i * 2 + 1], blngmin);
        blngmax = Math.max(b[i * 2 + 1], blngmax);
        b1[i] = {
            ll0: [b[i * 2], b[i * 2 + 1]],
            ll1: [b[i * 2 + 2], b[i * 2 + 3]]
        }
        b1[i].d = distLatLon(b1[i].ll0[0], b1[i].ll0[1], b1[i].ll1[0], b1[i].ll1[1]);
    }

    if (alatmin > blatmax ||
        alatmax < blatmin ||
        alngmin > blngmax ||
        alngmax < blngmin) {
        return {
            meanDist: 100000,
            dir: 1
        };
    }

    for (var i = 0; i < a1.length; i++) {
        dtw[i] = {
            ll: a1[i].ll0,
            d: []
        };
        for (var j = 0; j < b1.length; j++) {
            dtw[i].d[j] = {
                i: j,
                b: b1[j],
                d0: distLatLon(a1[i].ll0[0], a1[i].ll0[1], b1[j].ll0[0], b1[j].ll0[1]),
                d1: distLatLon(a1[i].ll0[0], a1[i].ll0[1], b1[j].ll1[0], b1[j].ll1[1]),
                d: 1000000,
                da: 1000
            };
            if (dtw[i].d[j].d0 > 5 * Math.max(1000, b1[j].d) && dtw[i].d[j].d1 > 5 * Math.max(1000, b1[j].d)) {
                /*dtw[i].d[j].d = 1000000;
                dtw[i].d[j].da = 1000000;*/
                j += 2;
            } else {
                if (dtw[i].d[j].d0 > b1[j].d || dtw[i].d[j].d1 > b1[j].d) {
                    dtw[i].d[j].d = Math.min(dtw[i].d[j].d0, dtw[i].d[j].d1);
                } else {
                    dtw[i].d[j].d = dtw[i].d[j].d0 * b1[j].d / (dtw[i].d[j].d0 + dtw[i].d[j].d1);
                    dtw[i].d[j].d = Math.sqrt(dtw[i].d[j].d0 * dtw[i].d[j].d0 - dtw[i].d[j].d * dtw[i].d[j].d);
                }

                //dtw[i].d[j].da = dtw[i].d[j].d * a1[i].d / (aDist/2);
            }
        }
        dtw[i].m = dtw[i].d.sort(function(x, y) {
            return x.d - y.d
        })[0];
    }

    //return dtw;

    return {
        meanDist: d3.mean(dtw, function(d) {
            return d.m.d
        }),
        dir: dtw[0].m.i <= dtw[dtw.length - 1].m.i
    };
}

var outlineA0 = null,
    outlineA = {};

function drawActivities() {
    if (isBeta && a2.length == 0) {
        loadActivities2();
    }
    if (a2.length == 0) {
        d3.selectAll('#activitiesTab table tbody tr').remove();
        d3.select('#activitiesTab table tbody').append('tr').append('td').html('Your activities are not yet available. Please visit your <a href="/summary" style="display:inline" target="_blank">summary</a> or <a href="/activities" target="_blank" style="display:inline">activities</a> to populate then return/refresh.');
        return;
    }
    if (brushExtent[0] != brushExtent[1] && brushExtent[1] > 0) {
        if (typeof(map2) === 'undefined') {
            initMap('2');
            map2.fitBounds([
                [activity.minLat, activity.minLng],
                [activity.maxLat, activity.maxLng]
            ]);
        }

        window.setTimeout(function() {
            d3.selectAll('#activitiesTab table tbody tr').remove();
            d3.select('#activitiesTab table tbody').append('tr').append('td').html('Crunching numbers… (not the fastest if you\'ve loads of data or on slower devices e.g. iPad 2)');

            d3.values(outlineA).forEach(function(d) {
                map2.removeLayer(d);
            });
            outlineA = {};

            if (outlineA0 != null && map2.hasLayer(outlineA0)) map2.removeLayer(outlineA0);
            outlineA0 = new L.Polyline(segment.latLngArr2.slice(filtIndExtent[0], filtIndExtent[1] + 1).map(function(d) {
                return [d.lat, d.lng]
            }), {
                color: '#0000FF',
                opacity: 1,
                weight: 3
            });
            map2.addLayer(outlineA0);
            map2.fitBounds([
                [activity.minLat, activity.minLng],
                [activity.maxLat, activity.maxLng]
            ]);

            if (activitiesTimer != null) {
                clearTimeout(activitiesTimer);
                activitiesTimer = null;
            }

            activitiesTimer = window.setTimeout(function() {
                var thead, tbody, t = d3.select('#activitiesTab table');
                t.style('table-layout', 'fixed');
                m = d3.select('#activitiesTab #activitiesMap');

                thead = t.select('thead tr');
                tbody = t.select('tbody');

                a1 = {
                    a: [],
                    latmin: 1000,
                    latmax: -1000,
                    lngmin: 1000,
                    lngmax: -1000
                }

                mapData = simplify(activity.latLngArr2Copy
                        .slice(filtIndExtent[0], filtIndExtent[1] + 1)
                        .map(function(d) {
                            return {
                                x: d.lng,
                                y: d.lat,
                                z: 0
                            };
                        }), 0.000078, true)
                    .map(function(d) {
                        return [d.y, d.x];
                    });

                for (var i = 0; i < mapData.length; i++) {
                    a1.latmin = Math.min(mapData[i][0], a1.latmin);
                    a1.latmax = Math.max(mapData[i][0], a1.latmax);
                    a1.lngmin = Math.min(mapData[i][1], a1.lngmin);
                    a1.lngmax = Math.max(mapData[i][1], a1.lngmax);
                    a1.a[i] = {
                        ll_1: (i == 0 ? null : mapData[i - 1]),
                        ll0: mapData[i],
                        ll1: (i == mapData.length - 1 ? mapData[i - 1] : mapData[i + 1])
                    }
                    if (i == 0) {
                        a1.a[i].d = distLatLon(a1.a[i].ll0[0], a1.a[i].ll0[1], a1.a[i].ll1[0], a1.a[i].ll1[1]);
                    } else {
                        if (i == mapData.length - 1) {
                            a1.a[i].d = distLatLon(a1.a[i].ll0[0], a1.a[i].ll0[1], a1.a[i].ll_1[0], a1.a[i].ll_1[1]);
                        } else {
                            a1.a[i].d = (distLatLon(a1.a[i].ll0[0], a1.a[i].ll0[1], a1.a[i].ll1[0], a1.a[i].ll1[1]) / 2) + (distLatLon(a1.a[i].ll0[0], a1.a[i].ll0[1], a1.a[i].ll_1[0], a1.a[i].ll_1[1]) / 2);
                        }
                    }
                }
                a1.dist = d3.sum(a1.a, function(d) {
                    return d.d
                });

                for (var i = 0; i < a2.length; i++) {
                    if (a2[i].map != '') {
                        a2[i].dtw = DTW1(a1, a2[i].map2);
                    }
                }
                tbody.selectAll('tr').remove();

                activitiesData = [];
                var distMean = [3, 5, 10, 20, 50, 100];
                var i = 0;

                while (activitiesData.length < 4 && i < distMean.length) {
                    activitiesData = a2.filter(function(d) {
                        return typeof(d.dtw) !== 'undefined' && d.dtw.meanDist < distMean[i]
                    });
                    i++;
                }

                activitiesData.sort(function(a, b) {
                    return a.dtw.meanDist - b.dtw.meanDist
                });

                /*if (activitiesData.lenght < 5) {
                  activitiesData = a.filter(function(d) {
                    return typeof(d.dtw) !== 'undefined' && d.dtw.meanDist < 10
                  }).sort(function(a, b) {
                    return a.dtw.meanDist - b.dtw.meanDist
                  });
                }*/

                if (activitiesData.length == 0) {
                    d3.select('#activitiesTab table tbody').append('tr').append('td').html('No matches :-( Try making the selection a little longer.');
                } else {
                    tbody.selectAll('tr')
                        .data(activitiesData)
                        .enter()
                        .append('tr')
                        .attr('id', function(d) {
                            return 'outlineTR' + d.i
                        })
                        .on('mouseover', function(d, i, j) {
                            d3.select(this).classed('info', true);
                            d.mapFeature.setStyle(lm_mapLineHighlight);
                            d.mapFeature.bringToFront();
                            outlineA0.bringToFront();
                        })
                        .on('mouseout', function(d, i, j) {
                            d3.select(this).classed('info', false);
                            d.mapFeature.setStyle(lm_mapLine);
                            outlineA0.bringToFront();
                        })
                        .each(function(d) {
                            var tr = d3.select(this);
                            tr.append('td')
                                .append('a')
                                .attr('title', d.an + ' (' + fDate(parseDateTime(d.ls)) + ')') // + ' - ' + d.dtw.meanDist)
                                .attr('href', '/activities/' + d.i)
                                .attr('target', '_blank')
                                .html((d.dtw.dir ? '<span style="color:green">&#9654;</span>' : '&#9664;') + ' ' + d.an + ' (' + fDate(parseDateTime(d.ls)) + ')');
                        });

                    activitiesData.forEach(function(d, i) {
                        if (typeof(d.mapFeature) === 'undefined') {
                            if (!d.map2) d.map3 = decompressN(d.map, 5, 2);
                            d.mapFeature = new L.Polyline(d3.range(0, d.map2.length / 2).map(function(e) {
                                return [d.map2[e * 2], d.map2[e * 2 + 1]]
                            }), lm_mapLine);
                            d.mapFeature.vv_id = d.i;
                            d.mapFeature.bindPopup('<a href="/activities/' + d.i + '" target="_blank">' + d.an + '</a>');

                            d.mapFeature.on('mouseover', function(e) {
                                d3.select('#outlineTR' + e.target.vv_id).classed('info', true);
                                e.target.setStyle(lm_mapLineHighlight);
                                e.target.bringToFront();
                                outlineA0.bringToFront();
                            });
                            d.mapFeature.on('mouseout', function(e) {
                                d3.select('#outlineTR' + e.target.vv_id).classed('info', false);
                                e.target.setStyle(lm_mapLine);
                                outlineA0.bringToFront();
                            });
                        }

                        map2.addLayer(d.mapFeature);
                        outlineA[d.i] = d.mapFeature;
                    });
                }
                outlineA0.bringToFront();
            }, 1000);
        }, 10);
    } else {
        if (activitiesTimer != null) {
            clearTimeout(activitiesTimer);
            activitiesTimer = null;
        }

        d3.selectAll('#activitiesTab table tbody tr').remove();
        d3.select('#activitiesTab table tbody').append('tr').append('td').style('white-space', 'pre-wrap').html('Make a selection to find overlaps. The longer the selection, the slower it will be!');

        if (typeof(map2) === 'undefined') {
            initMap('2');
        }
        map2.fitBounds([
            [activity.minLat, activity.minLng],
            [activity.maxLat, activity.maxLng]
        ]);

        d3.values(outlineA).forEach(function(d) {
            map2.removeLayer(d);
        });
        outlineA = {};
        if (outlineA0 != null && map2.hasLayer(outlineA0)) map2.removeLayer(outlineA0);
    }
}

var f = function(d) {
    return d
};
var fp = function(d) {
    return suf(d)
};
var fBool = function(d) {
    return (d ? 'Yes' : 'No')
};
var fSpeed = function(d) {
    return (d ? 'Yes' : 'No')
};

var s_sNotice = (!s_s ? '<p style="font-size:1.2em;' + (isFree ? 'margin-top:8px;' : '') + '""><b>Important note:</b> Segment placings are <a href="' + s_sBlogLink + '" target="_blank">only available to Strava Subscribers</a>. VeloViewer users can get a <a href="https://promo.strava.com/veloviewer/" target="_blank">60 day free Strava Premium trial here</a>.</p>' : '');
if (isFree && activity.athleteId != -100) {
    d3.select('#tourInfo')
        .style('display', null)
        .classed('alert alert-info', true)
        .html('<h4>VeloViewer Tour Notes</h4>' + s_sNotice + '<p' + (s_s ? ' style="margin-top:8px;"' : '') + '>The main reason I wanted to build this page was I wanted to see full-screen maps and charts. Remember to check out the different map image options below the map including OS maps for those from the UK.</p><p>Sharable 3D route profiles, in-depth segments list (with up-to-date positions) and so much more. Just click the various tabs to explore everything on offer.</p><p><b>Go to your Update page at any time to <a href="/update">Upgrade to PRO</a> to view your entire Strava history and segments in VeloViewer (and to hide these notes).</b></p>');
}

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: 27,
    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: '150px',
    class: 'clipText',
    field: 'sn',
    type: 'text',
    display: 'text',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 2,
    title: 'Elapsed<br/>Time',
    desc: 'Elapsed time',
    lngDesc: 'The elapsed time (including any stops).',
    width: 'auto',
    class: '',
    field: '_et',
    type: 'num',
    display: 'lTime',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 3,
    title: 'Stopped<br/>Time',
    desc: 'Stopped time',
    lngDesc: 'The stopped time.',
    width: 'auto',
    class: '',
    field: 'stt',
    type: 'num',
    display: 'lTime',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 4,
    title: 'PR Pos',
    desc: 'Your PR Position',
    lngDesc: '.',
    width: 'auto',
    class: 'ar',
    field: 'pr',
    type: 'num',
    display: 'int',
    hideZeros: 0,
    s_s: 1,
    defOrder: 'asc'
}, {
    id: 5,
    title: 'Tries',
    desc: 'Number of tries',
    lngDesc: '.',
    width: 'auto',
    class: 'ar',
    field: 'tr',
    type: 'num',
    display: 'int',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 40,
    title: 'PR Score',
    desc: 'PR Score',
    lngDesc: 'This score shows how well this effort compares to your other efforts on the same segment.',
    width: 'auto',
    class: 'ar',
    field: 'prs',
    type: 'num',
    display: '2dp',
    hideZeros: 0,
    s_s: 1,
    defOrder: 'desc'
}, {
    id: 6,
    title: 'Behind<br/>PR',
    desc: 'Time behind your PR',
    lngDesc: 'The amount of time you are behind your PR.',
    width: 'auto',
    class: '',
    field: 'prl',
    type: 'num',
    display: 'lTime',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 7,
    title: 'Behind<br/>PR %',
    desc: '% behind your PR',
    lngDesc: 'The percentage of time you are behind your PR.',
    width: 'auto',
    class: '',
    field: 'pprl',
    type: 'num',
    display: '1dp',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 8,
    title: 'Behind<br/>Leader',
    desc: 'Time behind leader',
    lngDesc: 'The amount of time you are behind the leader.',
    width: 'auto',
    class: '',
    field: '_bl',
    type: 'num',
    display: 'lTime',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 10,
    title: 'Behind<br/>Leader %',
    desc: '% behind leader',
    lngDesc: 'The percentage of time you are behind the leader.',
    width: 'auto',
    class: '',
    field: '_pbl',
    type: 'num',
    display: '1dp',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 11,
    title: 'KOM Pos',
    desc: 'Your PR Position',
    lngDesc: '',
    width: 'auto',
    class: 'ar',
    field: 'r',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    s_s: 1,
    defOrder: 'asc'
}, {
    id: 12,
    title: 'Athlete<br/>Count',
    desc: 'Athlete count',
    lngDesc: '.',
    width: 'auto',
    class: 'ar',
    field: 'tac',
    type: 'num',
    display: 'int',
    hideZeros: 0,
    s_s: (isPRO ? 0 : 1),
    defOrder: 'asc'
}, {
    id: 13.5,
    title: 'PR Pos Score',
    desc: 'PR Position Score',
    lngDesc: 'Your PR\'s position score is based on your position against the total number of athletes to have completed the segment, close to 100 is good, 0 is bad!',
    width: 'auto',
    class: 'ar',
    field: 's',
    type: 'num',
    display: '2dp',
    hideZeros: 0,
    s_s: 1,
    defOrder: 'desc'
}, {
    id: 13,
    title: 'Dist<br/>' + lrgLenUnit,
    desc: 'Distance',
    lngDesc: 'The length of the segment.',
    width: 'auto',
    class: '',
    field: 'd',
    type: 'num',
    display: 'lLen',
    hideZeros: 0,
    defOrder: 'desc'
}, {
    id: 14,
    title: 'Max Elv<br/>' + smlLenUnit,
    desc: 'Max elevation',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'eh',
    type: 'num',
    display: 'sLen',
    hideZeros: 0,
    defOrder: 'desc'
}, {
    id: 15,
    title: 'Speed<br/>' + speedUnit,
    desc: 'Avg speed',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: '_as',
    type: 'num',
    display: 'speed',
    hideZeros: 0,
    defOrder: 'desc'
}, {
    id: 16,
    title: 'Pace<br/>' + lrgPaceUnit,
    desc: 'Avg pace ' + lrgPaceUnit,
    lngDesc: '',
    width: 'auto',
    class: '',
    field: '_paLrg',
    type: 'lrgPace',
    display: 'sTime',
    hideZeros: 0,
    defOrder: 'asc'
}, {
    id: 17,
    title: 'Pace<br/>' + smlPaceUnit,
    desc: 'Avg pace ' + smlPaceUnit,
    lngDesc: '',
    width: 'auto',
    class: '',
    field: '_paSml',
    type: 'smlPace',
    display: 'sTime',
    hideZeros: 0,
    defOrder: 'asc'
},
{ id: 33, title: 'VAM', desc: 'VAM m/hr', lngDesc: '', width: 'auto', class: 'ar', field: 'v', type: 'num', display: 'int', hideZeros: 1, defOrder: 'desc' },
{
    id: 18,
    title: 'Heart<br/>Rate',
    desc: 'Heart rate bpm',
    lngDesc: 'Average heart rate bpm',
    width: 'auto',
    class: 'ar',
    field: '_ah',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 19,
    title: 'Max<br/>heart',
    desc: 'Heart rate bpm',
    lngDesc: 'Maximum heart rate bpm',
    width: 'auto',
    class: 'ar',
    field: '_mh',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 20,
    title: 'Power<br/>W',
    desc: 'Power Watts',
    lngDesc: 'Average power in Watts',
    width: 'auto',
    class: 'ar',
    field: '_aw',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 21,
    title: 'NP<br/>W',
    desc: 'Normalised power',
    lngDesc: 'Normalised power Watts. Only for efforts > 20 minutes',
    width: 'auto',
    class: 'ar',
    field: 'rap',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 22,
    title: 'Cadence<br/>rpm',
    desc: 'Cadence rpm',
    lngDesc: 'Average cadence rpm',
    width: 'auto',
    class: 'ar',
    field: '_ac',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 23,
    title: 'Grade<br/>%',
    desc: 'Grade %',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'gr',
    type: 'num',
    display: '2dp',
    hideZeros: 0,
    defOrder: 'desc'
}, {
    id: 24,
    title: 'Max Grade<br/>%',
    desc: 'Max Grade %',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'mg',
    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 change<br/>' + smlLenUnit,
    desc: 'Elevation change',
    lngDesc: '',
    width: 'auto',
    class: '',
    field: 'ed',
    type: 'num',
    display: 'sLen',
    hideZeros: 0,
    defOrder: 'desc'
}, {
    id: 30,
    title: 'FTP W',
    desc: 'FTP Watts',
    lngDesc: 'Functional Threshold Performance using actual power. Only for efforts > 20 minutes.',
    width: 'auto',
    class: 'ar',
    field: 'pftp',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 31,
    title: 'FTP NP W',
    desc: 'FTP Normalised Power',
    lngDesc: 'Functional Threshold Performance using Normalised Power. Only for efforts > 20 minutes.',
    width: 'auto',
    class: 'ar',
    field: 'rapftp',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 32,
    title: 'FTP bpm',
    desc: 'FTP bpm',
    lngDesc: 'Functional Threshold Performance using heart rate. Only for efforts > 20 minutes.',
    width: 'auto',
    class: 'ar',
    field: 'hrftp',
    type: 'num',
    display: 'int',
    hideZeros: 1,
    defOrder: 'desc'
}, {
    id: 50,
    title: 'Bearing',
    desc: 'Bearing',
    lngDesc: '',
    width: 'auto',
    class: 'ar',
    field: 'b',
    type: 'num',
    display: 'arrow',
    hideZeros: 0,
    defOrder: 'asc'
}];

var confCookie = 'scnf';
var confType = 1;
//var defColOrder = "0,10,20,30,40".split(',');

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": "2:-1000000|1000000"
    }];
    curConfig = getCookie(confCookie);
    if (typeof(curConfig) == 'undefined') {
        curConfig = 0;
    }

    /*for (i = 0; i < c.length; i++) {
      var ta = c[i].colOrder.split(',');
      c[i].colDisp = Array(defColOrder.length+1).join('.').split('').map(function(d, j) {
        return (j < ta.length ? 0 : 7)
      }).join();
      c[i].colOrder += ',' + defColOrder.filter(function(d){return ta.indexOf(d) == -1}).join();
    }*/

    //asyncComplete2(segCacheInit, 'segAsyncCount');

    /*$.ajax({
      url: '/api/getConfig/'+confType,
      async: true,
      tryCount: 0,
      retryLimit: 3,
      success: function(result) {
        var obj = result.parseJSON();

        if (typeof(obj.error) === "undefined") {
          for (var i=0; i<obj.configs.length; i++) {
            obj.configs[i].editable = true;
            obj.configs[i].temp = false;
          }
          c = d3.merge([c, obj.configs]);
          localStorage['segmentsConfig' + contextAthleteId] = JSON.stringify(c);

          curConfig = getCookie(confCookie);
          if (typeof(curConfig) == 'undefined') {
            curConfig = 1;
          }

          configLoaded = true;
          if (isBeta) {
            asyncComplete2(localInit, 'segAsyncCount');
          } else {
            asyncComplete2(segCacheInit, 'segAsyncCount');
          }
        } else {
          alert('Error GC: Oh dear, some of your data hasn\'t loaded correctly. Try refreshing the page. If you keep seeing this message then let me know.')
        }
      },
      error: function(xhr, textStatus, errorThrown) {
        handleAjaxError(xhr, textStatus, errorThrown, this);
      }
    });*/
}

function localInit() {
    initCalled = true;
    if (dataProcessed) {
        rowCount = 10000;
        a = teffs[activity.id];
        a = (typeof(a) === 'undefined' ? [] : a);

        a.forEach(function(d) {
            for (var att in d) {
                d['_' + att] = d[att];
            }
            for (var att in tsegs[d.si]) {
                d[att] = tsegs[d.si][att];
            }
        });
        a = a.filter(function(d) {
            return typeof(d.sn) !== 'undefined'
        });
        a.sort(function(a, b) { return a.z1 - b.z1 });
        var lc = 1;
        a.forEach(function(d) {
            d.num = lc++;
            d.stt = (typeof(d._mt) === 'undefined' ? 0 : Math.max(0, d._et - d._mt));
            d.prl = d._et - d.et;
            d.pprl = 100 * d.prl / d.et;
            d._bl = d._et - d.bet;
            d._pbl = 100 * d._bl / d.bet;
            d.startIndex = d.z1;
            d.endIndex = d.z2;
            d.if = (starred.indexOf(d.si) > -1 ? 1 : 0);
            d._as = d._d / d._et;
            d._pa = 1 / d._as;

            if (typeof(pwrObj) !== 'undefined' && typeof(pwrObj.rap) !== 'undefined' && /*wattsAvl &&*/ d.et >= (20 * 60)) {
                d.mw = d3.mean(pwrObj.data.slice(d.z1, d.z2 + 1));
                d.rap = Math.pow(d3.mean(pwrObj.rap.slice(d.z1 + rapTime, d.z2 + 1)), 1 / 4);
                d.pftp = d.mw * Math.min(1, (0.05 / 4) * ((d.et / 60) - 20) / 10 + 0.95);
                d.rapftp = d.rap * Math.min(1, (0.05 / 4) * ((d.et / 60) - 20) / 10 + 0.95);
            } else {
                d.pftp = null;
                d.rapftp = null;
            }
            if (typeof(hrtObj) !== 'undefined' && d.et >= (20 * 60)) {
                d.mhr = d3.mean(hrtObj.data.slice(d.z1, d.z2 + 1));
                d.hrftp = d.mhr * Math.min(1, (0.05 / 4) * ((d.et / 60) - 20) / 10 + 0.95);
            } else {
                d.hrftp = null;
            }
        });
        //prepareSegments(a);
        setSegmentIndexes();
        setConfig();
        init();

        if (s_s) {
            var soDiv = d3.select('#summaryTab')
                .insert("div", ":first-child")
                .classed('well', true);

            soData = d3.values(d3.nest()
                .key(function(e) {
                    return e._pr;
                })
                .rollup(function(e) {
                    return {
                        'i': e[0]._pr,
                        'l': e.length
                    };
                })
                .map(a))
            /*.filter(function(d) {
                return d.i <= 10
              })*/
            ;

            soDiv.append("h2")
                .text('Segment PR position counts');

            var soTab = soDiv.append('div')
                .style('overflow-y', 'auto')
                .append('table')
                .classed('table table-striped table-condensed table-hover table-bordered', true);

            soTab.append('thead')
                .selectAll('th')
                .data(soData)
                .enter()
                .append('th')
                .text(function(d) {
                    return suf(d.i);
                });

            soTab.append('tbody')
                .selectAll('th')
                .data(soData)
                .enter()
                .append('td')
                .text(function(d) {
                    return d.l;
                });

            soData = d3.values(d3.nest()
                .key(function(e) {
                    return e.r;
                })
                .rollup(function(e) {
                    return {
                        'i': e[0].r,
                        'l': e.length
                    };
                })
                .map(a.filter(function(d) {
                    return d._pr == 1
                })));

            soDiv.append("h2")
                .text('Segment overall position counts');

            var soTab = soDiv.append('div')
                .style('overflow-y', 'auto')
                .append('table')
                .classed('table table-striped table-condensed table-hover table-bordered', true);

            soTab.append('thead')
                .selectAll('th')
                .data(soData)
                .enter()
                .append('th')
                .text(function(d) {
                    return suf(d.i);
                });

            soTab.append('tbody')
                .selectAll('th')
                .data(soData)
                .enter()
                .append('td')
                .text(function(d) {
                    return d.l;
                });
        }
    }
}

function setSegmentIndexes() {
    a.forEach(function(d) {
        d.startIndex = d3.bisectLeft(allCharts[0].d.map(function(e) {
            return e.t
        }), d._sd);
        d.endIndex = d3.bisectLeft(allCharts[0].d.map(function(e) {
            return e.t
        }), d._et + d._sd);
    });
}


function loadActivities2() {
    a2 = d3.values(tacts).filter(function(d) {
        return typeof(d.map) !== 'undefined' && d.map != null
    });
    a2.forEach(function(d) {
        d.map2 = decompressN(d.map, 5, 2);
    });
}

function filtersFinishedFun() {
    initScrollingTable(d3.select('.tableWrapper.st'), function() {
        var availableHeight = parseInt(window.innerHeight) - this.getBoundingClientRect().top - 71 - 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 = [distObj.data[d.startIndex], distObj.data[d.endIndex]];

            allCharts[0].b.extent(brushExtent);
            allCharts[0].mb.call(allCharts[0].b);

            filterCharts();
            setBrushText();
        })
        .on('mouseover', function(d) {
            allCharts[0].areaHighlight.style('display', null);
            allCharts[0].areaHighlight.attr("d", allCharts[0].a(allCharts[0].d.slice(d.startIndex, d.endIndex)
                .filter(function(e, j) {
                    return j % Math.ceil(allCharts[0].d.length / (allCharts[0].w / 1)) == 0
                })));
        })
        .on('mouseout', function(d) {
            allCharts[0].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;
        }
    });
}

function fillInGaps() {
    var td = actStrs.filter(function(d) {
        return d.type == 'time'
    })[0].data;
    timeOrig = clone(td);
    var badTimes = td.map(function(d, i) { return i }).filter(function(i) { return i > 0 && td[i] == td[i - 1] });
    var startTime = td[0];
    if (startTime > 0) {
        td.forEach(function(d, i) {
            td[i] -= startTime;
        });
        if (actStrs.filter(function(d) {
                return d.type == 'distance'
            }).length > 0) {
            var dd = actStrs.filter(function(d) {
                return d.type == 'distance'
            })[0].data;
            var sd = dd[0];
            dd.forEach(function(d, i) {
                dd[i] -= sd;
            });
        }
    }

    while (badTimes.length > 0) {
        var id = badTimes.pop();
        for (var j = 0; j < actStrs.length; j++) {
            switch (actStrs[j].type) {
                case 'latlng':
                    actStrs[j].data2.splice(id, 1);
                    actStrs[j].data.splice(id * 2, 2);
                    break;
                default:
                    actStrs[j].data.splice(id, 1);
            }
        }
    }

    /*for (var i = 1; i < td.length; i++) {
        var timeDif = td[i] - td[i - 1];
        if (timeDif > 1 && timeDif < 60) {
            for (var j = 0; j < actStrs.length; j++) {
                switch (actStrs[j].type) {
                    case 'latlng':
                        var l10 = actStrs[j].data[i * 2 - 2];
                        var l11 = actStrs[j].data[i * 2];
                        var l20 = actStrs[j].data[i * 2 - 1];
                        var l21 = actStrs[j].data[i * 2 + 1];
                        var nd1 = d3.range(l10, l11, (l11 - l10) / timeDif).slice(1, timeDif);
                        var nd2 = d3.range(l20, l21, (l21 - l20) / timeDif).slice(1, timeDif);
                        var nd = d3.merge(nd1.map(function(d, i) {
                            return [nd1[i], nd2[i]]
                        }));
                        var da = [i * 2, 0].concat(d3.range(0, (timeDif - 1) * 2).map(function(d, k) {
                            return (typeof(nd[k]) !== 'undefined' ? nd[k] : (k % 2 == 0 ? l11 : l21))
                        }));
                        Array.prototype.splice.apply(actStrs[j].data, da);
                        break;
                    default:
                        var d0 = actStrs[j].data[i - 1];
                        var d1 = actStrs[j].data[i];
                        var nd = d3.range(d0, d1, (d1 - d0) / timeDif).slice(1, timeDif);
                        var da = [i, 0].concat(d3.range(0, timeDif - 1).map(function(d, k) {
                            return (typeof(nd[k]) !== 'undefined' ? nd[k] : d1)
                        }));
                        Array.prototype.splice.apply(actStrs[j].data, da);
                }
            }
        }
    }*/
}

function postImageToStrava(type, img, id, title, text) {
    //var imgData = document.getElementById('igCanvas').toDataURL("image/jpeg");
    $.post('/save/saveStravaImage.php', {
        imgdata: img.substr(img.indexOf(',') + 1).toString(),
        type: type,
        id: id,
        title: title,
        text: text,
        success: function() {
            alert('The profile image has been attached to your Strava activity.')
        }
    });
}

function attachImage() {
    var tis = clone(imageSize);
    imageSize = {
        x: 2048,
        y: 1493
    };
    displayImage();
    imageSize = tis;
    postImageToStrava('a', document.getElementById('profile3dCanvas').toDataURL("image/jpeg"), activity.id);
}


$(document).ready(function() {
    if (contextAthleteId == loggedInAthleteId) {
        d3.select('#optionsWrapper').append('button').attr({
                'id': 'addImageToActivity',
                'class': 'btn btn-primary btn-mini',
                'style': 'display:block;position:absolute;right:0px;top:21px'
            }).text('Add image to activity')
            .on('click', attachImage);
    }

    getConfigs();
});
