/*
Copyright 2012-2025 VeloViewer. All Rights Reserved
###V412###
*/

var cookieNames = document.cookie.split(/=[^;]*(?:;\s*|$)/);
for (var i = 0; i < cookieNames.length; i++) {
    if (/^summarySel(Year|Type|Gear)[0-9]/.test(cookieNames[i])) {
        setCookie(cookieNames[i], '', -1);
    }
}
var a, s, types, years, gears, gearTypes, data, seg, sSumT, targetDiv, chart, eddingtonSvg, edChart, distributionChart, distributionData, benchmarkScores, selectedClubId, edUnit = 'miles',
    leaderboardYear = new Date().getFullYear(),
    leaderboardType = '',
    leaderboardVal,
    leaderboardData = {},
    segAsyncCount = 0,
    initialised = false,
    hasPowerMeter = false,
    allYearBtn, yearBtns, allTypeBtn, typeBtns, allGearBtn, gearBtns,
    actAsyncCount = 0,
    asyncCount = 0,
    scores = [],
    selYear = (athleteId == contextAthleteId && typeof(getCookie('summarySelYear')) !== 'undefined' ? (getCookie('summarySelYear') != '' ? getCookie('summarySelYear').split('###').map(function(d) {
        return +d
    }) : []) : []),
    selType = (typeof(getCookie('summarySelType')) !== 'undefined' ? (getCookie('summarySelType') != '' ? getCookie('summarySelType').split('###') : []) : []),
    selGear = (typeof(getCookie('summarySelGear')) !== 'undefined' ? (getCookie('summarySelGear') != '' ? getCookie('summarySelGear').split('###') : []) : []),
    mapsRequired = false,
    segsRequired = false,
    zoomYearChart = (hasLocalStorage && typeof(localStorage['SummaryYearDistChartZoom']) !== 'undefined' ? +localStorage['SummaryYearDistChartZoom'] : 0),
    explorerDivisor = 1000000,
    mapsRequired = true,
    edClimbMult = 20,
    calRollRequired = false,
    projectedYearDistVal, projectedYearLastAct, projectedYearFirstAct, daysLeftInYear = Math.ceil(Math.abs((new Date() - new Date(new Date().getFullYear() + 1, 0, 1).getTime()) / (24 * 60 * 60 * 1000))),
    s_sLink = '<a title="This data is only visible to Strava Subscribers. Please click to view more information." href="' + s_sBlogLink + '" target="_blank">n/a</a>';
//incTrainer = (typeof(getCookie('it')) !== 'undefined' && getCookie('it') == '1'),
actWrk = 'activities';
actWrkSng = 'activity';
var displayValues = [{ name: 'Distance', field: 'd' },
    { name: 'Elevation', field: 'eg' },
    { name: 'Time (moving)', field: 'mt' },
    { name: 'Time (elapsed)', field: 'et' },
    { name: 'Avg speed', field: 'as' },
    { name: 'Activity count', field: 'length' },
    { name: 'Day count', field: 'length1' },
    { name: 'Distance + Elevation (m) / 100', field: 'deq' },
    { name: 'Elev/Dist', field: 'egd' },
    { name: 'Avg heart rate', field: 'ah' },
    { name: 'Cadence', field: 'ac' },
    { name: 'Calories', field: 'ca' },
    { name: 'Temperature', field: 'tm' },
    { name: 'Avg Power', field: 'aw' },
    { name: 'Weighted avg power', field: 'waw' },
    { name: 'Pwr/Hrt', field: 'ph' },
    { name: 'Speed/Hrt', field: 'sh' },
    { name: 'Heart beats/' + lrgLenUnit, field: 'hd' },
    { name: 'Relative Effort', field: 'scr', tcOnly: true },
    { name: 'Relative Effort', field: 'sc', calOnly: true },
    { name: 'Points in red', field: 'r', calOnly: true },
    { name: 'Training Load', field: 'tl' },
    { name: 'Intensity (Avg)', field: 'in' },
    { name: 'Kudos', field: 'kc' },
    { name: 'PRs', field: 'prs' },
    { name: 'PRs (tries > 1)', field: 'prs2' },
    { name: 'PR Score', field: 'prsc' },
    { name: 'PR Score All', field: 'prsc2' },
    { name: 'Streaks', field: 'streak', calOnly: true },
    { name: 'Explorer (new tiles all time)', field: 'explorer' },
    { name: 'Explorer (all tiles)', field: 'explorer2' },
    { name: 'Eddington', field: 'eddington', tcOnly: true },
    { name: 'Eddington (km)', field: 'eddingtonKm', tcOnly: true },
    { name: 'Heart Rate Zones', field: 'hz1', tcOnly: true },
    { name: 'Heart Rate Zones %', field: 'hz1p', tcOnly: true },
    { name: 'Power Brackets', field: 'pz1', tcOnly: true },
    { name: 'Power Brackets %', field: 'pz1p', tcOnly: true },
    { name: 'Pace Zones', field: 'pcz1', tcOnly: true },
    { name: 'Pace Zones %', field: 'pcz1p', tcOnly: true },
    { name: 'Run PBs (time)', field: 'rpb', tcOnly: true },
    { name: 'Run PBs (pace)', field: 'rpbp', tcOnly: true }
];
for (var i = 1; i <= 5; i++) {
    displayValues.push({
        name: 'Heart Rate Zone ' + i,
        field: 'hz' + i,
        calOnly: true
    })
}
for (var i = 1; i <= 11; i++) {
    displayValues.push({
        name: 'Power ' + (i == 1 ? '0W' : (i == 11 ? '450W and up' : ((i - 2) * 50) + ' to ' + ((i - 1) * 50))),
        field: 'pz' + i,
        calOnly: true
    })
}
for (var i = 1; i <= 6; i++) {
    displayValues.push({
        name: 'Pace Zone ' + i,
        field: 'pcz' + i,
        calOnly: true
    })
}
var climbCats = [{ "code": 'Hc', "desc": ' HC' },
    { "code": '1', "desc": '1st' },
    { "code": '2', "desc": '2nd' },
    { "code": '3', "desc": '3rd' },
    { "code": '4', "desc": '4th' }
];

var runBests = [{ title: '1 km', field: 'b1k' },
    { title: '5 km', field: 'b5k' },
    { title: '10 km', field: 'b10k' },
    { title: '15 km', field: 'b15k' },
    { title: '20 km', field: 'b20k' },
    { title: '30 km', field: 'b30k' },
    { title: '50 km', field: 'b50k' },
    { title: '1 mile', field: 'b1m' },
    { title: '10 mile', field: 'b10m' },
    { title: 'Half Marathon', field: 'bhm' },
    { title: 'Marathon', field: 'bmar' },
    { title: '50 km', field: 'b50k' },
    { title: '100 km', field: 'b100k' }
];

var calYear, month, calMonthDOW, calYearDOW, week, rect, color = d3.scale.linear()
    //.range(['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'].reverse());
    //.range(['#d73027', '#fc8d59', '#fee08b', '#d9ef8b', '#91cf60'].reverse());
    //.range(['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'].reverse());
    //.range(['#CCCCFF', '#0000FF', '#00FFFF', '#00FF00', '#FFFF00', '#FF0000'])
    .range([
        'rgb(255, 255, 178)', 'rgb(254, 204, 92)', 'rgb(253, 141, 60)', 'rgb(240, 59, 32)', 'rgb(189, 0, 38)'
    ]);

var fDay = d3.time.format("%w"),
    fWeek = d3.time.format("%W"),
    fPercent = d3.format(".1%"),
    fYrMonth = d3.time.format("%B %Y"),
    fYrMonthDOW = d3.time.format("%As %B %Y"),
    fYr = d3.time.format("%Y");
fYrDOW = d3.time.format("%As %Y");
fDate2 = fDate;
fDate3 = d3.time.format(fDate + ' (%A)');
fYrWeek = function(val) {
    var yr, wk;
    if (typeof(val) === 'string') {
        yr = parseInt(val.split('-')[0]);
        wk = parseInt(val.split('-')[1]);
    } else {
        yr = parseInt(d3.time.format("%Y")(val));
        wk = parseInt(d3.time.format("%W")(val));
    }

    var td = new Date(yr, 0, 1);
    td = new Date(td.setDate(td.getDate() + wk * 7));
    var wn = td.getWeekNumber();
    if (wn == 1 && wk > 10) {
        yr = yr + 1;
    }
    if (wn > 10 && wk == 0) {
        yr = yr - 1;
    }
    var sd = getDateOfISOWeek(wn, yr);
    var ed = clone(sd);
    var ed = new Date(ed.setDate(ed.getDate() + 6));
    var df = datePreference.match(/(%m[\/-]%d|%d[\/-]%m)/);
    var f = d3.time.format("%d/%m");
    if (df.length) {
        f = d3.time.format(df[0]);
    }
    return 'Week ' + wn + ' (' + f(sd) + ' - ' + f(ed) + '), ' + yr;
    //return 'Week ' + wn + ', ' + yr;
}

function getMonday(d) {
    d = new Date(d);
    var day = d.getDay(),
        diff = d.getDate() - day + (day == 0 ? -6 : 1); // adjust when day is sunday
    return new Date(d.setDate(diff));
}
var keyRects, keyMin, keyMax, calDataFormat,
    calWidth, calHeight, calCellSize, faData = [],
    calData, calTooltip, tcData, calVal = (hasLocalStorage && typeof(localStorage['SummaryCalChart']) !== 'undefined' ? localStorage['SummaryCalChart'] : 'd'),
    calDOW = false,
    calGroup = (hasLocalStorage && typeof(localStorage['SummaryCalChartGroup']) !== 'undefined' ? localStorage['SummaryCalChartGroup'] : 'day'),
    tcCalVal = (hasLocalStorage && typeof(localStorage['SummaryTCCalChart']) !== 'undefined' ? localStorage['SummaryTCCalChart'] : 'd'),
    tcCalGroup = (hasLocalStorage && typeof(localStorage['SummaryTCCalChartGroup']) !== 'undefined' ? localStorage['SummaryTCCalChartGroup'] : 'day');

var getTypes = function(data) {
    return d3.keys(d3.nest().key(function(d) {
        return d.type;
    }).map(data));
};
var getYears = function(data) {
    return d3.keys(d3.nest().key(function(d) {
        return d.year;
    }).map(data));
};
var getGear = function(data) {
    gearTypes = [];
    var arr = [];
    var arr2 = [];

    for (i = 0; i < data.length; i++) {
        if (typeof(data[i].g) !== 'undefined' &&
            data[i].g != null &&
            data[i].g != '' &&
            data[i].g.toLowerCase() != 'n/a' &&
            arr2.indexOf(data[i].g) == -1) {
            var type = data[i].t.replace(/(.)([A-Z])/g, '$1 $2').replace(' (commute)', '').replace(' (trainer)', ''); //(data[i].t.substr(0, 4) == 'Ride' || data[i].t.substr(0, 4) == 'Virt' ? 'Bikes' : 'Shoes');
            arr.push({
                name: data[i].g,
                type: type
            });
            arr2.push(data[i].g);
            if (gearTypes.indexOf(type) == -1) {
                gearTypes.push(type);
            }
        }
    }
    gearTypes.sort();
    return arr;
};
var filterTypes = function(data, type) {
    return data.filter(function(d) {
        return d.type == type;
    })
};
var filterYears = function(data, year) {
    return data.filter(function(d) {
        return new Date(d.startDate).getFullYear() == year;
    })
};
var filterBoth = function(data, year, type, incTrn) {
    return data.filter(function(d) {
        return ((year == 'All' || new Date(d.activityDate).getFullYear() == year) && (type == 'All' || d.type == type) && d.incTrainer == incTrn);
    })
};
var filterBoth2 = function(data, year, type, incTrn) {
    return data.filter(function(d) {
        return (d.year == (year == 'All' ? '0' : year) && (d.type == type) && d.incTrainer == incTrn);
    })
};
var filterBoth3 = function(data, year, type, gear) {
    return data.filter(function(d) {
        return (d.year.join() == year.join()) &&
            (d.type.join() == type.join()) &&
            (d.gear.join() == gear.join()) && !d.justScores;
    })
};

function popStats2(obj, data) {
    obj.actCount = data.length;
    obj.totDist = d3.sum(data.map(function(d) {
        return d.distance;
    }));
    obj.totElev = d3.sum(data.map(function(d) {
        return d.totalElevationGain;
    }));
    obj.maxDist = data.sort(function(a, b) {
        return parseFloat(b.distance) - parseFloat(a.distance);
    }).slice(0, 5);
    obj.maxElev = data.sort(function(a, b) {
        return parseFloat(b.totalElevationGain) - parseFloat(a.totalElevationGain);
    }).slice(0, 5);
}

function popStats(obj, propName, filterFunc1, filterFunc2, getFunc) {
    var i, j;

    for (i = 0; i < obj.length; i++) {
        var a1 = filterFunc1(a, obj[i].val);

        popStats2(obj[i], a1);
        var y = getFunc(a1);

        for (j = 0; j < y.length; j++) {
            var a2 = filterFunc2(a1, y[j]);

            obj[i][propName][j] = {
                "val": y[j]
            };
            popStats2(obj[i][propName][j], a2);
        }
    }
}

function getClimbStats(cat) {
    var climbStat = {
        "cat": cat
    }
    cat = (cat == 'HC' ? '&nbsp;HC' : cat);
    climbStat.count = data.s.filter(function(d) {
        return d.climbCat == cat;
    }).length;
    climbStat.topScore = data.s.filter(function(d) {
        return d.climbCat == cat;
    }).sort(function(a, b) {
        return parseFloat(b.posScore) - parseFloat(a.posScore);
    })[0];
    climbStat.maxVAM = data.s.filter(function(d) {
        return d.climbCat == cat;
    }).sort(function(a, b) {
        return parseFloat(b.vam) - parseFloat(a.vam);
    })[0];
    climbStat.avgVAM = d3.mean(data.s.filter(function(d) {
        return d.climbCat == cat;
    }).map(function(d) {
        return parseFloat(d.vam);
    }));
    climbStat.maxRP = data.s.filter(function(d) {
        return d.climbCat == cat;
    }).sort(function(a, b) {
        return parseFloat(b.relativePower) - parseFloat(a.relativePower);
    })[0];
    climbStat.avgRP = d3.mean(data.s.filter(function(d) {
        return d.climbCat == cat;
    }).map(function(d) {
        return parseFloat(d.relativePower);
    }));

    data.climbs.push(climbStat);
}

var updatingStats = false;

function getQSFilter(yearI, typeI, gearI) {
    //getQSFilter(0, 1, 43)
    var retVal = '';
    if (yearI > -1 && selYear.length > 0) {
        selYear.sort();
        retVal += yearI + ':' + (new Date(selYear[0], 0, 1).getTime()) + '|' + (new Date(selYear[selYear.length - 1], 11, 31, 23, 59, 59).getTime());
    }
    if (typeI > -1 && selType.length > 0) {
        if (retVal != '') {
            retVal += ',';
        }
        if (selType.length == 1 && selType[0] == 'Ride (commute)') {
            retVal += typeI + ':Ride,47:1';
        } else {
            if (selType.length == 1 && selType[0] == 'Ride (trainer)') {
                retVal += typeI + ':Ride,48:1';
            } else {
                if (selType.length == 1 && selType[0] == 'Run (trainer)') {
                    retVal += typeI + ':Run,48:1';
                } else {
                    retVal += typeI + ':' + (selType.length == 0 ? 'All' : selType.map(function(d) {
                        return encodeURIComponent(d.replace(' (trainer)', '').replace(' (commute)', ''))
                    }).join('|'));
                }
            }
        }
    }
    if (gearI > -1 && selGear.length > 0) {
        if (retVal != '') {
            retVal += ',';
        }
        retVal += gearI + ':' + (selGear.length == 0 ? 'All' : selGear.map(function(d) {
            return encodeURIComponent(d)
        }).join('|'));
    }
    return retVal;
}

var edData;
function drawEddington() {
    var mult = 0.000621371192;
    switch (edUnit) {
        case 'km':
            mult = 0.001;
            break;
        case 'minutes':
            mult = 1 / 60;
            break;
        case 'climb':
            mult = 1 / edClimbMult;
            break;
        case 'kindex':
            mult = 1;
            break;
        default:
            mult = 0.000621371192;
    }
    var da = d3.values(d3.nest()
        .key(function(d) {
            return Math.floor(d.dist * mult)
        })
        .rollup(function(d) {
            return {
                d: Math.floor(d[0].dist * mult),
                l: d.length,
                data: d
            }
        })
        .map(d3.values(
            d3.nest()
            .key(function(d) {
                return edUnit == 'kindex' ? d.i : fDate(d.date)
            })
            .rollup(function(d) {
                return {
                    dist: d3.sum(d, function(e) {
                        return edUnit == 'kindex' ? e.kc : (edUnit == 'minutes' ? e.mt : (edUnit == 'climb' ? e.eg : e.d))
                    }),
                    d: d,
                    l: d.length
                }
            })
            .map(a.filter(function(d) {
                return (d.f == 0 || (d.f == 1 && d.p == 1)) && ((edUnit == 'kindex' ? d.kc : (edUnit == 'minutes' ? d.mt : (edUnit == 'climb' ? d.eg : d.d))) > 0 && (selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1) && (!filterYears || selYear.length == 0 || selYear.indexOf(d.y) > -1))
            }))
        )));

    var da2 = d3.range(0, da[da.length - 1].d + 1);
    var da3 = da2.map(function(d) {
        return [d, d3.sum(da.filter(function(e) {
            return e.d >= d
        }), function(e) {
            return e.l
        })]
    });

    edData = [{
        "key": "Times completed",
        "bar": true,
        "values": da3.slice(0, 2 * data.su[edUnit == 'kindex' ? 'kIndex' : (edUnit == 'km' ? 'eddingtonKm' : (edUnit == 'minutes' ? 'eddingtonTime' : (edUnit == 'climb' ? 'eddingtonClimb' : 'eddingtonMl')))])
    }, {
        "key": "Eddington",
        "color": "rgb(255, 127, 14)",
        "values": da2.map(function(d) {
            return [d, d]
        }).slice(0, 2 * data.su[edUnit == 'kindex' ? 'kIndex' : (edUnit == 'km' ? 'eddingtonKm' : (edUnit == 'minutes' ? 'eddingtonTime' : (edUnit == 'climb' ? 'eddingtonClimb' : 'eddingtonMl')))])
    }];

    d3.select('#eddingtonModalValue').html(eddingtonTooltipContent(edData[0].values[data.su[edUnit == 'kindex' ? 'kIndex' : (edUnit == 'km' ? 'eddingtonKm' : (edUnit == 'minutes' ? 'eddingtonTime' : (edUnit == 'climb' ? 'eddingtonClimb' : 'eddingtonMl')))]]));

    nv.addGraph(function() {
        if (typeof(edChart) !== 'undefined' && typeof(edChart.tooltip) !== 'undefined') {
            d3.select('#' + edChart.tooltip.id()).remove();
        }
        eddingtonSvg.html('');
        edChart = nv.models.linePlusBarChart()
            .margin({
                top: 30,
                right: 35,
                bottom: 20,
                left: 35
            })
            .focusEnable(false)
            //We can set x data accessor to use index. Reason? So the bars all appear evenly spaced.
            .x(function(d) {
                return d[0]
            })
            .y(function(d, i) {
                return d[1]
            });

        edChart.tooltip.contentGenerator(eddingtonTooltipContent);

        edChart.xAxis.tickFormat(function(d) {
            if (edUnit == 'climb') return d * edClimbMult + ' m';
            if (edUnit == 'kindex') return d;
            return d + ' ' + edUnit;
        });

        edChart.y1Axis
            .tickFormat(d3.format(',f'));

        edChart.y2Axis
            .tickFormat(d3.format(',f'));

        var ext = edData.map(function(d) {
            return d3.extent(d.values, function(d) {
                return d[1]
            })
        });
        ext = [Math.min(ext[0][0], ext[1][0]), Math.max(ext[0][1], ext[1][1])];
        edChart.bars.forceY(ext);
        edChart.lines.forceY(ext);

        eddingtonSvg.datum(edData)
            .transition()
            .duration(0)
            .call(edChart);

        d3.select('.nv-bar-0-' + data.su[edUnit == 'kindex' ? 'kIndex' : (edUnit == 'km' ? 'eddingtonKm' : (edUnit == 'minutes' ? 'eddingtonTime' : (edUnit == 'climb' ? 'eddingtonClimb' : 'eddingtonMl')))]).attr('fill', 'rgb(255, 127, 14)')

        nv.utils.windowResize(drawEddington);

        return edChart;
    });
}

function eddingtonTooltipContent(data) {
    var l = -1;
    var labels = [];
    var isPopup = typeof(data.length) === 'undefined';
    var m = typeof(data.index) === 'undefined' ? (typeof(data.pointIndex) === 'undefined' ? data[0] : data.pointIndex) : data.index;
    var d = edData[0].values[m][1];
    var h;
    switch (edUnit) {
        case 'climb':
            if (isPopup) {
                h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + (edClimbMult * m) + ' m</strong></div>';
                h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">On ' + d + ' days you climbed at least ' + (edClimbMult * m) + ' m.</div>';
                if (d < m) {
                    h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">' + (m - d) + ' more day'+(m - d == 1 ? '' : 's')+' of ' + (edClimbMult * m) + ' m or more of climbing required.</div>';
                }
            } else {
                h = 'Current Eddington is ' + (edClimbMult * m) + ' m. On ' + d + ' days you climbed at least ' + (edClimbMult * m) + ' m. ';
                h += (m+1)-edData[0].values[m+1][1] + ' more day'+((m+1)-edData[0].values[m+1][1] == 1 ? '' : 's')+' of '+ (edClimbMult * (m+1)) + ' m or more of climbing required to increase your Eddington to '+(edClimbMult * (m+1))+' m.';
            }
            break;
        case 'kindex':
            if (isPopup) {
                h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + m + '</strong></div>';
                h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">On ' + d + ' activities you received at least ' + m + ' kudos.</div>';
                if (d < m) {
                    h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">' + (m - d) + ' more activit'+(m - d == 1 ? 'y' : 'ies')+' with ' + m + ' or more kudos required.</div>';
                }
            } else {
                h = 'Current Eddington is ' + m + '. On ' + d + ' activities you received at least ' + m + ' kudos. ';
                h += (m+1)-edData[0].values[m+1][1] + ' more activit'+(m - d == 1 ? 'y' : 'ies')+' with '+ (m+1) + ' or more kudos required to increase your Eddington to '+(m+1)+' kudos.';
            }
            break;
        default:
            if (isPopup) {
                h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + m + ' ' + edUnit + '</strong></div>';
                h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">On ' + d + ' days you covered at least ' + m + ' ' + edUnit + '.</div>';
                if (d < m) {
                    h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">' + (m - d) + ' more day'+(m - d == 1 ? '' : 's')+' of ' + m + ' ' + edUnit + ' or more required.</div>';
                }
            } else {
                h = 'Current Eddington is ' + m + ' ' + edUnit + '. On ' + d + ' days you covered at least ' + m + ' ' + edUnit+ '. ';
                h += (m+1)-edData[0].values[m+1][1] + ' more day'+((m+1)-edData[0].values[m+1][1] == 1 ? '' : 's')+' of '+ (m+1) + ' ' + edUnit + ' or more required to increase your Eddington to '+(m+1)+' ' + edUnit+ '.';
            }
    }
    if (isPopup) {
        h += '</div>';
    }
    return h;
}

function lbpw(isFriends, isClubs) {
    d3.select('#' + (isFriends ? 'friends' : (isClubs ? 'clubs' : 'leaderboard')) + 'Tab tbody').selectAll('tr').remove();
    d3.select('#' + (isFriends ? 'friends' : (isClubs ? 'clubs' : 'leaderboard')) + 'Tab tbody')
        .append('tr')
        .append('td')
        .attr('colspan', '4')
        .text('Getting data, please wait...');
    d3.select('#distributionTab p.pleaseWait').style('display', null);
    d3.select('#distributionTab svg').style('display', 'none');
    if (isFriends) {
        d3.select('.friendsInfo').text('');
    }
    if (isClubs) {
        d3.select('.clubsInfo').text('');
    }
}

function drawLeaderboard() {
    var isFriends = d3.select('#friendsTab').style('display') == 'block';
    var isClubs = d3.select('#clubsTab').style('display') == 'block';

    if (isClubs && typeof(selectedClubId) === 'undefined') {
        return;
    }

    d3.selectAll('.distYourValue, .distPosition, #distLQ, #distMedian, #distUQ').text('-');

    if (!isFriends && !isClubs) {
        if (typeof(leaderboardData[leaderboardVal.val + leaderboardType + leaderboardYear]) === 'undefined') {
            lbpw(isFriends, isClubs);
            $.ajax({
                url: '/api/getScores.php?t=' + leaderboardVal.val + '&y=' + leaderboardYear + '&at=' + leaderboardType,
                val: leaderboardVal.val,
                actType: leaderboardType,
                year: leaderboardYear,
                success: function(result) {
                    if (result.status == 'success') {
                        leaderboardData[this.val + this.actType + this.year] = decompressN(result.data, 0, 1);
                        drawLeaderboard();
                    } else {
                        alert('Whoops, something has gone wrong.');
                    }
                    if (result.refresh) {
                        $.ajax({
                            url: '/api/getScores.php?r=1&t=' + this.val + '&y=' + this.year + '&at=' + this.actType
                        });
                    }
                }
            });
            return;
        }
        var z = leaderboardData[leaderboardVal.val + leaderboardType + leaderboardYear].filter(function(d) {
            return d > 0;
        }).map(function(d) {
            return d * leaderboardVal.mult
        });

        d3.selectAll('.distTotal').text(leaderboardVal.val == 'vvs' ? 'n/a' : leaderboardVal.format(d3.sum(z)));

        var bs = benchmarkScores.filter(function(d) {
            return d.actType == leaderboardType && (d.year == '' ? 0 : d.year) == leaderboardYear
        });

        var bv = (bs.length == 1 ? bs[0][leaderboardVal.val] * leaderboardVal.mult : null);
        if (bv != null) {
            var pos = d3.bisect(z, bv);
            var bp = (bv != null ? pos / z.length : null);
            pos = z.length - pos + 1;
            d3.selectAll('.distYourValue').text(leaderboardVal.format(bv));
            d3.selectAll('.distPosition').text(d3.select('#leaderboardTab').style('display') == 'block' ? suf(pos) : fPercent(bp));
        }
    }

    if (isFriends || isClubs || d3.select('#leaderboardTab').style('display') == 'block') {
        if (typeof(leaderboardData[leaderboardVal.val + leaderboardType + leaderboardYear + 'leaderboard' + (isFriends ? 'friends' : (isClubs ? 'clubs' + selectedClubId : ''))]) === 'undefined') {
            lbpw(isFriends, isClubs);
            $.ajax({
                url: '/api/getScoreLeaderboard' + (isFriends ? 'Friends' : (isClubs ? 'Clubs' : '')) + '.php?t=' + leaderboardVal.val + '&y=' + leaderboardYear + '&at=' + leaderboardType + (isClubs ? '&c=' + selectedClubId : ''),
                isFriends: isFriends,
                isClubs: isClubs,
                clubId: selectedClubId,
                val: leaderboardVal.val,
                actType: leaderboardType,
                year: leaderboardYear,
                success: function(result) {
                    if (result.status == 'success') {
                        leaderboardData[this.val + this.actType + this.year + 'leaderboard' + (this.isFriends ? 'friends' : (this.isClubs ? 'clubs' + this.clubId : ''))] = result.data;
                        var bs = benchmarkScores.filter(function(d) {
                            return d.actType == leaderboardType && (d.year == '' ? 0 : d.year) == leaderboardYear
                        });
                        var zi = leaderboardData[this.val + this.actType + this.year + 'leaderboard' + (this.isFriends ? 'friends' : (this.isClubs ? 'clubs' + this.clubId : ''))].map(function(d) {
                            return d.i
                        }).indexOf(contextAthleteId);
                        if (bs.length > 0) {
                            if (zi > -1) {
                                leaderboardData[this.val + this.actType + this.year + 'leaderboard' + (this.isFriends ? 'friends' : (this.isClubs ? 'clubs' + this.clubId : ''))][zi].v = bs[0][leaderboardVal.val]; // * leaderboardVal.mult;
                            } else {
                                leaderboardData[this.val + this.actType + this.year + 'leaderboard' + (this.isFriends ? 'friends' : (this.isClubs ? 'clubs' + this.clubId : ''))].push({
                                    i: contextAthleteId,
                                    n: firstName + ' ' + lastName,
                                    c: '',
                                    s: '',
                                    p: 0,
                                    v: bs[0][leaderboardVal.val]
                                });
                            }
                        }
                        drawLeaderboard();
                    } else {
                        alert('Whoops, something has gone wrong.');
                    }
                    if (result.refresh) {
                        $.ajax({
                            url: '/api/getScoreLeaderboard' + (this.isFriends ? 'Friends' : (this.isClubs ? 'Clubs' : '')) + '.php?r=1&t=' + this.val + '&y=' + this.year + '&at=' + this.actType + (this.isClubs ? '&c=' + this.clubId : ''),
                        });
                    }

                }
            });
            return;
        }
        var l = leaderboardData[leaderboardVal.val + leaderboardType + leaderboardYear + 'leaderboard' + (isFriends ? 'friends' : (isClubs ? 'clubs' + selectedClubId : ''))]; //.parseJSON();
        l.sort(function(a, b) {
            return b.v - a.v
        });
        var t = d3.sum(l.map(function(d) {
            return d.v * leaderboardVal.mult
        }));
        if (l.length == 21) l = l.slice(0, 20);
        d3.select('#' + (isFriends ? 'friends' : (isClubs ? 'clubs' : 'leaderboard')) + 'Tab tbody').selectAll('tr').remove();
        var r = d3.select('#' + (isFriends ? 'friends' : (isClubs ? 'clubs' : 'leaderboard')) + 'Tab tbody').selectAll('tr')
            .data(l).enter().append('tr');
        r.append('td').classed('hidden-phone', true).html(function(d, i) {
            if (d.i == loggedInAthleteId) {
                if (isFriends) {
                    d3.select('.friendsInfo').text('You currently place ' + suf(i + 1) + ' of the ' + l.length + ' people you follow that use VeloViewer. Total: ' + (leaderboardVal.val == 'vvs' ? 'n/a' : leaderboardVal.format(t)));
                }
                if (isClubs) {
                    d3.select('.clubsInfo').text('You currently place ' + suf(i + 1) + ' of the ' + l.length + ' people that use VeloViewer in this club. Total: ' + (leaderboardVal.val == 'vvs' ? 'n/a' : leaderboardVal.format(t)));
                }
            }
            return suf(i + 1);
        });
        r.append('td').html(function(d) {
            var h = '<a style="display:inline" title="View ' + d.n + '\'s details on Strava" target="_blank" href="https://www.strava.com/athletes/' + d.i + '"><img src="https://cf.veloviewer.com/img/strava_icon.svg" style="height:16px"></a> ';
            h += (d.p ? '<a style="display:inline" title="View ' + d.n + '\'s details on VeloViewer" target="_blank" href="/athletes/' + d.i + '/summary">' : '') + d.n + (d.p ? '</a>' : '');
            if (typeof(d.f) !== 'undefined' && d.f == '0') {
                h += ' (not PRO)';
            }
            return h;
        });
        r.append('td').classed('hidden-phone', true).html(function(d) {
            return (d.c + ' ' + d.s).substr(0, 30);
        });
        r.append('td').html(function(d) {
            return leaderboardVal.format(d.v * leaderboardVal.mult);
        });
    } else {

        var lq = d3.quantile(z, 0.25);
        var med = d3.quantile(z, 0.5);
        var uq = d3.quantile(z, 0.75);
        d3.select('#distLQ').text(leaderboardVal.format(lq));
        d3.select('#distMedian').text(leaderboardVal.format(med));
        d3.select('#distUQ').text(leaderboardVal.format(uq));

        if (leaderboardVal.val == 'vvs') {
            leaderboardVal.logMult = 15;
            z = z.map(function(d) {
                return Math.exp(d / leaderboardVal.logMult)
            });
            bv = Math.exp(bv / leaderboardVal.logMult);
            lq = Math.exp(lq / leaderboardVal.logMult);
            med = Math.exp(med / leaderboardVal.logMult);
            uq = Math.exp(uq / leaderboardVal.logMult);
        }

        var zext = leaderboardVal.val == 'vvs' ? [0, Math.exp(100 / leaderboardVal.logMult)] : d3.extent(z);
        var zinc = leaderboardVal.val == 'vvs' ? (zext[1] - zext[0]) / 198 : (zext[1] - zext[0] < 200 ? 1 : (zext[1] - zext[0]) / 99);
        var histVals = d3.range(zext[0], zext[1] + 2 * zinc, zinc);
        var hdata = histVals.slice(0, histVals.length - 1).map(function(d, i) {
            return {
                x: d, // + (histVals[i + 1] - d) / 2,
                xmin: d,
                xmax: histVals[i + 1],
                y: z.filter(function(e) {
                    return e >= d && e < histVals[i + 1]
                }).length / z.length
            }
        });

        distributionData = [{
            key: "Distribution",
            bar: true,
            values: hdata,
            median: med,
            lq: lq,
            uq: uq,
            val: bv,
            vp: bp
        }];
        nv.addGraph(function() {
            if (typeof(distributionChart) !== 'undefined' && typeof(distributionChart.tooltip) !== 'undefined') {
                d3.select('#' + distributionChart.tooltip.id()).remove();
            }
            eddingtonSvg.html('');
            d3.select('#distributionTab p.pleaseWait').style('display', 'none');
            d3.select('#distributionTab svg').style('display', null);
            distributionChart = nv.models.multiBarChart()
                .margin({
                    top: 10,
                    right: 15,
                    bottom: 25,
                    left: 40
                })
                .groupSpacing(0)
                .showLegend(false)
                .x(function(d) {
                    return d.xmin
                })
                .y(function(d, i) {
                    return d.y
                });
            distributionChart.duration(0);
            distributionChart.tooltip.contentGenerator(function(data) {
                var l = -1;
                var labels = [];
                var m = typeof(data.index) === 'undefined' ? data.pointIndex : data.index;
                var d = distributionData[0].values[m][1];
                var f = distributionData[0].values[data.index];
                var h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + leaderboardVal.format(leaderboardVal.val == 'vvs' ? (f.xmin == 0 ? 0 : Math.log(f.xmin) * leaderboardVal.logMult) : f.xmin) + ' to ' + leaderboardVal.format(leaderboardVal.val == 'vvs' ? Math.log(f.xmax) * leaderboardVal.logMult : f.xmax) + '</strong></div>';
                h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px">' + fPercent(f.y) + '</div>';
                h += '</div>';
                return h;
            });

            distributionChart.xAxis.tickFormat(function(d) {
                return leaderboardVal.val == 'vvs' ? fInt(Math.log(d) * leaderboardVal.logMult) : leaderboardVal.format(d)
            });

            distributionChart.yAxis
                .tickFormat(fPercent);

            d3.select('#distributionTab svg')
                .datum(distributionData)
                .transition()
                .duration(0)
                .call(distributionChart);

            if (d3.select('#distributionTab .nv-x')[0][0] != null) {
                d3.select('#distributionTab .nv-x').attr('transform', d3.select('#distributionTab .nv-x').attr('transform').replace(/\([0-9\.-]*[ ,]/, '(-' + parseFloat(d3.select('#distributionTab .nv-group .nv-bar').attr('width')) / 2 + ','));
                d3.selectAll('#distributionTab .vv-mq, #distributionTab .vv-valg').remove();
                var sc = d3.scale.linear().domain(d3.extent(distributionChart.xScale().domain())).range(d3.extent(distributionChart.xScale().range()));
                var fh = distributionChart.yScale().range()[0] + 'px';
                var mq = d3.selectAll('#distributionTab .nv-groups')
                    .insert('g', 'g')
                    .classed('vv-mq', true)
                mq.append('rect')
                    .classed('vv-lqr', true)
                    .attr('style', 'stroke-opacity: 0.25; fill-opacity: 0.25; fill: #777; stroke: #777')
                    .attr('height', fh)
                    .attr('width', (sc(distributionData[0].median) - sc(distributionData[0].lq)) + 'px')
                    .attr('x', sc(distributionData[0].lq) + 'px');
                mq.append('rect')
                    .attr('style', 'stroke-opacity: 0.25; fill-opacity: 0.25; fill: #777; stroke: #777')
                    .classed('vv-uqr', true)
                    .attr('height', fh)
                    .attr('width', (sc(distributionData[0].uq) - sc(distributionData[0].median)) + 'px')
                    .attr('x', sc(distributionData[0].median) + 'px');
                mq.append('rect')
                    .classed('vv-med', true)
                    .attr('style', 'stroke-opacity: 1; fill-opacity: 1; fill: #777; stroke: #777')
                    .attr('height', fh)
                    .attr('width', '1px')
                    .attr('x', sc(distributionData[0].median) + 'px');
                if (distributionData[0].val != null) {
                    d3.selectAll('#distributionTab .nv-groups')
                        .append('g')
                        .classed('vv-valg', true)
                        .append('rect')
                        .classed('vv-val', true)
                        .attr('style', 'stroke-opacity: 0.5; fill-opacity: 0.5; fill: #FF032E; stroke: #FF032E')
                        .attr('height', fh)
                        .attr('width', '1px')
                        .attr('x', sc(distributionData[0].val) + 'px');
                }

                nv.utils.windowResize(function() {
                    distributionChart.update();

                    d3.select('#distributionTab .nv-x').attr('transform', d3.select('#distributionTab .nv-x').attr('transform').replace(/\([0-9\.-]*[ ,]/, '(-' + parseFloat(d3.select('#distributionTab .nv-group .nv-bar').attr('width')) / 2 + ','));
                    var sc = d3.scale.linear().domain(d3.extent(distributionChart.xScale().domain())).range(d3.extent(distributionChart.xScale().range()));
                    d3.select('#distributionTab .vv-lqr')
                        .attr('width', (sc(distributionData[0].median) - sc(distributionData[0].lq)) + 'px')
                        .attr('x', sc(distributionData[0].lq) + 'px');
                    d3.select('#distributionTab .vv-uqr')
                        .attr('width', (sc(distributionData[0].uq) - sc(distributionData[0].median)) + 'px')
                        .attr('x', sc(distributionData[0].median) + 'px');
                    d3.select('#distributionTab .vv-med')
                        .attr('x', sc(distributionData[0].median) + 'px');
                    d3.select('#distributionTab .vv-val')
                        .attr('x', sc(distributionData[0].val) + 'px');
                });
            }

            return distributionChart;
        });
    }
}

function setStarredData() {
    d3.select('#numStarredSegs')
        .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=37:1">' + fInt(starred.length) + ' starred</a>');
}

function redraw() {
    if (s.scores.length == 0) {
        if (athleteId == contextAthleteId) {
            alert('It seems like your data isn\'t up to date. Try going to your update page where you should make sure all your activities have been imported from STRAVA.');
            //location.href = "/update";
        } else {
            alert('This athlete needs to update their data for this page to display properly.');
        }
        return;
    }
    //data.a = filterBoth(a, selYear, selType);
    data.su = filterBoth3(s.summaries, selYear, selType, selGear)[0];

    data.sc = filterBoth3(s.scores, selYear, selType, selGear)[0];
    d3.select('#s_s').remove()
    if (!s_s) {
        d3.select('#scores div.well').insert('div', 'div').attr('id', 's_s').html('This score is based on your best Strava segment positions to provide a way to compare your acheivements with others. <a href="' + s_sBlogLink + '" target="_blank">Only Strava Subscribers</a> are able to see their positions so I\'m afraid this value isn\'t available to you. <a href="https://blog.veloviewer.com/veloviewer-score-how-do-you-measure-up/" target="_blank">More info on the VeloViewer Score</a>.');
    }
    d3.select('#vvScore')
        .attr('title', isNaN(data.sc.score) ? 0 : f4dp(data.sc.score))
        .html(isNaN(data.sc.score) ? 0 : f3dp(data.sc.score));
    d3.select('#avgPos')
        .html(!s_s ? s_sLink : isNaN(data.sc.avgPos) ? 'n/a' : 'Mean: ' + suf(Math.round(data.sc.avgPos)) + ', Median: ' + suf(Math.round(data.sc.avgPosMedian)) + ', Mode: ' + suf(Math.round(data.sc.avgPosMode)));
    d3.select('#avgTot')
        .html(isNaN(data.sc.avgTotal) ? 'n/a' : fInt(data.sc.avgTotal));
    d3.select('#scoreSegments')
        .html('From ' + fInt(data.sc.segFrom) + ' of <a target="_blank" href="/athlete/' + contextAthleteId + '/segments?o=3:1&f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '14:-0.25|100000">' + fInt(data.sc.segTotal) + ' segments</a>.');
    d3.select('#possibleScore')
        .html('Maximum possible score: ' + (isNaN(data.sc.possibleScore) ? '0' : f3dp(data.sc.possibleScore)) + '<br/>Minimum counting score: ' + (isNaN(data.sc.minScore) ? '0' : f3dp(data.sc.minScore)));
    d3.select('#rank1').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|1,4:2|10000000">' + fInt(data.su.rank1) + '</a>');
    d3.select('#rank2').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:2|2">' + fInt(data.su.rank2) + '</a>');
    d3.select('#rank3').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:3|3">' + fInt(data.su.rank3) + '</a>');
    d3.select('#rank5').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|5,4:2|10000000">' + fInt(data.su.rankTop5) + '</a>');
    d3.select('#rank10').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|10,4:2|10000000">' + fInt(data.su.rankTop10) + '</a>');
    d3.select('#rank25').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|25,4:2|10000000">' + fInt(data.su.rankTop25) + '</a>');
    d3.select('#rank50').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|50,4:2|10000000">' + fInt(data.su.rankTop50) + '</a>');
    d3.select('#rank100').html(!s_s ? s_sLink : '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '0:1|100,4:2|10000000">' + fInt(data.su.rankTop100) + '</a>');

    d3.select('#actCount')
        .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/activities?f=' + getQSFilter(0, 1, 43) + '">' + fInt(data.su.totalActivities) + '</a>');

    d3.select('#totDist')
        .text(fInt(data.su.totalDist * lrgLenMult) + ' ' + lrgLenUnit);

    d3.select('#totElev')
        .text(fInt(data.su.totalElev * smlLenMult) + ' ' + smlLenUnit);

    runBests.forEach(function(d, i) {
        if (data.su.runBests[i] == null) {
            d3.selectAll('.runBest' + d.field)
                .style('display', 'none');
            d3.selectAll('#runBestDD' + d.field)
                .text('&nbsp;');
        } else {
            var splitAct = data.su.runBests[i].split('|');
            d3.selectAll('.runBest' + d.field)
                .style('display', null);
            d3.selectAll('#runBestDD' + d.field)
                .html(parseInt(splitAct[1]).toHrMinSec() + ' - <a target="_blank" title="' + splitAct[2] + ' (' + fDate(tacts[+splitAct[0]].date) + ')" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + splitAct[0] + '">' + splitAct[2] + ' (' + fDate(tacts[+splitAct[0]].date) + ')</a>');
        }
    });

    d3.selectAll('#distAwardsContainer *, #elevAwardsContainer *, #timeAwardsContainer *').remove();

    d3.select('#distAwardsContainer')
        .selectAll('a')
        .data(data.su.dGoal)
        .enter()
        .append('a')
        .classed('award', true)
        .attr('href', function(d, i) {
            return '/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(0, 1, 43) + ',').replace(/^,/, '') + '5:' + d.goal + '|' + (i == data.su.dGoal.length - 1 ? 1000000 : data.su.dGoal[i + 1].goal - 1)
        });

    d3.select('#elevAwardsContainer')
        .selectAll('a')
        .data(data.su.eGoal)
        .enter()
        .append('a')
        .classed('award', true)
        .attr('href', function(d, i) {
            return '/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(0, 1, 43) + ',').replace(/^,/, '') + '6:' + d.goal + '|' + (i == data.su.eGoal.length - 1 ? 1000000 : data.su.eGoal[i + 1].goal - 1)
        });

    d3.select('#timeAwardsContainer')
        .selectAll('a')
        .data(data.su.tGoal)
        .enter()
        .append('a')
        .classed('award', true)
        .attr('href', function(d, i) {
            return '/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(0, 1, 43) + ',').replace(/^,/, '') + '8:' + d.goal + '|' + (i == data.su.tGoal.length - 1 ? 1000000 : data.su.tGoal[i + 1].goal - 1)
        });

    d3.selectAll('#distAwardsContainer, #elevAwardsContainer, #timeAwardsContainer')
        .append('div')
        .style('clear', 'both');

    d3.selectAll('.award')
        .classed('label', true)
        .style('text-align', 'center')
        .style('float', 'left')
        .style('margin-right', '5px')
        .style('margin-bottom', '5px')
        .attr('data-toggle', 'tooltip')
        .attr('data-html', true)
        .attr('title', function(d) {
            return d.format1(d.goal) + ' - ' + d.a.length + ' ' + (d.a.length == 1 ? actWrkSng : actWrk) + ':<br/>' + d.a.slice(0, 3).map(function(e) {
                return e.an + ' - ' + d.format2(e[d.param])
            }).join('<br/>');
        })
        .html(function(d) {
            var html = d.format1(d.goal) + '<br/>';
            for (var i = 0; i < Math.min(3, d.a.length); i++) {
                html += '<span style="color:gold">&#9733;</span>';
            }
            for (var i = d.a.length; i < 3; i++) {
                html += '<span style="color:#dddd">&#9733;</span>';
            }
            return html;
        });

    setTimeout(function() {
        d3.selectAll('.award')
            .style('width', d3.max(d3.selectAll('.award')[0], function(d) {
                return parseFloat(d3.select(d).style('width'))
            }) + 'px')
    }, 500);


    $('.award').tooltip();

    d3.select('#totTime')
        .text(data.su.totalTime.toHrMinSec());

    d3.select('#totKcal')
        .text(fInt(data.su.totalCal));

    var edHtml = '<span style="white-space: nowrap;" title="You have covered over ' + data.su.eddingtonMl + ' miles on at least ' + data.su.eddingtonMl + ' days.\nTo raise this value by 1 you need to cover ' + (1 + data.su.eddingtonMl) + ' miles or more ' + data.su.eddingtonMl1 + ' more time' + (data.su.eddingtonMl1 > 1 ? 's' : '') + '.\nTo raise this value by 2 you need to cover ' + (2 + data.su.eddingtonMl) + ' miles or more ' + data.su.eddingtonMl2 + ' more time' + (data.su.eddingtonMl2 > 1 ? 's' : '') + '.">';
    edHtml += (lrgLenUnit == 'km' ? data.su.eddingtonMl + ' ml</span>, <span title="You have covered over ' + data.su.eddingtonKm + ' km on at least ' + data.su.eddingtonKm + ' days.\nTo raise this value by 1 you need to cover ' + (1 + data.su.eddingtonKm) + ' km or more ' + data.su.eddingtonKm1 + ' more time' + (data.su.eddingtonKm1 > 1 ? 's' : '') + '.\nTo raise this value by 2 you need to cover ' + (2 + data.su.eddingtonKm) + ' km or more ' + data.su.eddingtonKm2 + ' more time' + (data.su.eddingtonKm2 > 1 ? 's' : '') + '.">' + data.su.eddingtonKm + ' km' : data.su.eddingtonMl) + '</span>,';
    edHtml += ' <span style="white-space: nowrap;" title="You have covered over ' + data.su.eddingtonTime + ' minutes on at least ' + data.su.eddingtonTime + ' days.\nTo raise this value by 1 you need to cover ' + (1 + data.su.eddingtonTime) + ' minutes or more ' + data.su.eddingtonTime1 + ' more time' + (data.su.eddingtonTime1 > 1 ? 's' : '') + '.\nTo raise this value by 2 you need to cover ' + (2 + data.su.eddingtonTime) + ' minutes or more ' + data.su.eddingtonTime2 + ' more time' + (data.su.eddingtonTime2 > 1 ? 's' : '') + '.">' + data.su.eddingtonTime + ' min</span>,';
    edHtml += ' <span style="white-space: nowrap;" title="You have climbed over ' + (edClimbMult * data.su.eddingtonClimb) + ' metres on at least ' + data.su.eddingtonClimb + ' days.\nTo raise this value by 1 you need to climb ' + (edClimbMult * (1 + data.su.eddingtonClimb)) + ' metres or more ' + data.su.eddingtonClimb1 + ' more time' + (data.su.eddingtonClimb1 > 1 ? 's' : '') + '.\nTo raise this value by 2 you need to climb ' + (edClimbMult * (2 + data.su.eddingtonClimb)) + ' metres or more ' + data.su.eddingtonClimb2 + ' more time' + (data.su.eddingtonClimb2 > 1 ? 's' : '') + '.\nThe climbing Eddington is based on 20m units.">' + (edClimbMult * data.su.eddingtonClimb) + ' m (' + data.su.eddingtonClimb + ')</span>,';
    edHtml += ' <span style="white-space: nowrap;" title="Century rides (100+ miles) are a classic tick, always deserving merit. If any of these are runs then kudos x100!">' + data.su.centuryCount + ' Centuries (miles)</span>';
    d3.select('#eddington')
        .html(edHtml);

    if (document.getElementById('eddingtonModal') == null) {
        addModalPopup('eddington', 'Eddington Details');
        d3.select('#eddingtonModalLabel')
            .html('<span>Eddington Details</span><div class="btn-group" data-toggle="buttons-radio" id="eddingtonBtnGrp" style="margin-left: 20px; display:inline-block; float:none"><button type="button" data-val="miles" class="btn btn-primary btn-mini active">miles</button><button type="button" data-val="km" class="btn btn-primary btn-mini">km</button><button type="button" data-val="minutes" class="btn btn-primary btn-mini">minutes</button><button type="button" data-val="climb" class="btn btn-primary btn-mini">climbing (m /' + edClimbMult + ')</button><button type="button" data-val="kindex" class="btn btn-primary btn-mini">K-index</button></div>');

        d3.selectAll('#eddingtonBtnGrp .btn')
            .on('click', function() {
                edUnit = d3.select(this).attr('data-val');
                drawEddington();
            });
        d3.select('#eddingtonModal .modal-body')
            .append('p').classed('hidden-phone', true).text('The Eddington number is a great way to gauge your activity. This chart shows how many days of each distance/time you\'d need to complete to get to a certain number. X-Axis limited to 2x your Eddington so some activities might not be shown.');
        d3.select('#eddingtonModal .modal-body')
            .append('p').attr('id', 'eddingtonModalValue');
        eddingtonSvg = d3.select('#eddingtonModal .modal-body').append('svg').style({
            width: '100%',
            height: '350px'
        });
        $('#eddingtonModal').on('shown', function() {
            drawEddington();
        });
    }
    d3.select('#kIndex')
        .text(fInt(data.su.kIndex) + ' (total Kudos: ' + fInt(data.su.kCount) + ')');

    if (typeof(data.su.explorer) !== 'undefined') {
        d3.select('#expBests .links').remove();

        // prepend links to expBests
        var linkList = d3.select('#expBests').insert("div", ":first-child").style('float', 'right');
        if (document.getElementById('explorerGlobalHeatmapLink') == null) {
            linkList.append('a').attr('id', 'explorerGlobalHeatmapLink').style('display', 'inline-block').attr('title', 'View the VeloViewer Explorer Tile Global Heatmap').attr('href', '/explorer').attr('role', 'button').attr('class', 'btn btn-mini btn-primary').attr('style', 'display: block;').html('Global Heatmap');
        }

        var dl = d3.select('#expBestsList');
        dl.html('');
        var title = '';

        title = "Total number of Explorer tiles you've passed through. To see the tiles you've covered go to your Activities page and toggle the max square or max cluster buttons in the top right of the map.";
        dl.append('dt').attr('title', title).text('Explorer tiles:');
        dl.append('dd').attr('title', title).html(fInt(data.su.explorer) + ' tiles &nbsp; <a title="View the VeloViewer Explorer Tiles on the Activities map" id="viewExplorerMapBtn" href="#">View on map</a>');

        title = "Score based on the number of Explorers who have visited each of your tiles:\n5 points if 10 or less.\n4 points if there are between 11 and 25.\n3 points if there are between 26 and 50.\n2 points if between 51 and 100.\n1 point if there are 101 or more visitors.\nView Explorer Heatmap to see how many visitors each tile has.";
        dl.append('dt').attr('title', title).text('Explorer score:');
        dl.append('dd').attr('title', title).attr('id', 'explorerScoreValue');

        title = 'Total distance travelled divided by the total number of Explorer tiles you\'ve passed through. The lower the number, the higher the proportion of your travels are spent exploring new roads/trails.';
        dl.append('dt').attr('title', title).text(lrgLenUnit + '/tile:');
        dl.append('dd').attr('title', title).html(f3dp(lrgLenMult * data.su.totalDist2 / data.su.explorer) + ' ' + lrgLenUnit + ' per tile');

        title = 'Largest connected group of tiles you have visited';
        dl.append('dt').attr('title', title).text('Max connected:');
        dl.append('dd').attr('title', title).html(fInt(data.su.tilesMaxConnected) + ' tiles (' + fPercent(data.su.tilesMaxConnected / data.su.explorer) + ' of visited)');

        title = '(Max X - Min X + 1) + (Max Y - Min Y + 1)\n' + data.su.explorerMaxConnectedSpan.text + '\nBasically how far your Max Connected Tiles span across the tiles in both directions.';
        dl.append('dt').attr('title', title).text('Max connected span:');
        dl.append('dd').attr('title', title).html(fInt(data.su.explorerMaxConnectedSpan.span) + ' tiles');

        title = 'Largest square of tiles you have visited';
        dl.append('dt').attr('title', title).text('Max square:');
        dl.append('dd').attr('title', title).html((data.su.explorerMax ? data.su.explorerMax + 'x' + data.su.explorerMax : '0x0') + ' tiles (' + fPercent((data.su.explorerMax * data.su.explorerMax) / data.su.explorer) + ' of visited)');

        title = 'Largest cluster of tiles you have visited';
        dl.append('dt').attr('title', title).text('Max cluster:');
        dl.append('dd').attr('title', title).html(fInt(data.su.explorerClumpMax) + ' tiles (' + fPercent(data.su.explorerClumpMax / data.su.explorer) + ' of visited) &nbsp; <span id="explorerKMLLink"></span>');

        title = "Score based on the number of Explorers who have visited each of your Max Cluster\'s tiles:\n5 points if 10 or less.\n4 points if there are between 11 and 25.\n3 points if there are between 26 and 50.\n2 points if between 51 and 100.\n1 point if there are 101 or more visitors.\nView Explorer Heatmap to see how many visitors each tile has.";
        dl.append('dt').attr('title', title).text('Max cluster score:');
        dl.append('dd').attr('title', title).attr('id', 'clumpScoreValue');
        updateExplorerScore();

        title = '(Max X - Min X + 1) + (Max Y - Min Y + 1)\n' + data.su.explorerClumpMaxSpan.text +'\nBasically how far your Max Cluster spans across the tiles in both directions.';
        dl.append('dt').attr('title', title).text('Max cluster span:');
        dl.append('dd').attr('title', title).html(fInt(data.su.explorerClumpMaxSpan.span) + ' tiles');
        
        title = 'Largest connected group of tiles you have visited in a single column';
        dl.append('dt').attr('title', title).text('Max connected column:');
        dl.append('dd').attr('title', title).html(data.su.tilesMaxConnectedX.length > 0 ? fInt(data.su.tilesMaxConnectedX[0].max) + ' tiles (X: ' + data.su.tilesMaxConnectedX.map(function (d) { return d.id }).join(', ') + ')' : '0');
        
        title = 'Column with most tiles visited';
        dl.append('dt').attr('title', title).text('Most ticked column:');
        dl.append('dd').attr('title', title).html(data.su.tilesTotalX.length > 0 ? fInt(data.su.tilesTotalX[0].ys.length) + ' tiles (X: ' + data.su.tilesTotalX.map(function (d) { return d.id }).join(', ') + ')' : '0');

        title = 'Largest connected group of tiles you have visited in a single row';
        dl.append('dt').attr('title', title).text('Max connected row:');
        dl.append('dd').attr('title', title).html(data.su.tilesMaxConnectedY.length > 0 ? fInt(data.su.tilesMaxConnectedY[0].max) + ' tiles (Y: ' + data.su.tilesMaxConnectedY.map(function(d) { return d.id }).join(', ') + ')' : '0');

        title = 'Row with most tiles visited';
        dl.append('dt').attr('title', title).text('Most ticked row:');
        dl.append('dd').attr('title', title).html(data.su.tilesTotalY.length > 0 ? fInt(data.su.tilesTotalY[0].xs.length) + ' tiles (Y: ' + data.su.tilesTotalY.map(function(d) { return d.id }).join(', ') + ')' : '0');

        explorerKMLDisplayLink();

        d3.select('#viewExplorerMapBtn')
            .on('click', function() {
                setCookie('ExplorerClusterShown', 1, 365);
                setCookie('ExplorerMaxSquareShown', 1, 365);
                setCookie('am', true, 365);
                document.location.href = '/athlete/' + contextAthleteId + '/activities?f=' + getQSFilter(0, 1, 43);
                d3.event.preventDefault();
                return false;
            });
    }

    if (data.su.maxDist[0] != null) {
        var html = '';
        for (var i = 0; i < data.su.maxDist.length; i++) {
            var maxDist = data.su.maxDist[i].split('|');
            switch (i) {
                case 0:
                    html += '<a target="_blank" title="' + maxDist[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxDist[0] + '">' + fInt(maxDist[1] * lrgLenMult) + ' ' + lrgLenUnit + ' - ' + maxDist[2] + '</a>';
                    break;
                case 1:
                    html += '<br/>' + suf(i + 1) + ': <a target="_blank" title="' + maxDist[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxDist[0] + '">' + fInt(maxDist[1] * lrgLenMult) + ' ' + lrgLenUnit + '</a>';
                    break;
                default:
                    html += ', ' + suf(i + 1) + ': <a target="_blank" title="' + maxDist[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxDist[0] + '">' + fInt(maxDist[1] * lrgLenMult) + ' ' + lrgLenUnit + '</a>';
            }
        }
        d3.select('#maxDist')
            .html(html);
    } else {
        d3.select('#maxDist')
            .html('n/a');
    }

    if (data.su.maxElev[0] != null) {
        var html = '';
        for (var i = 0; i < data.su.maxElev.length; i++) {
            var maxElev = data.su.maxElev[i].split('|');
            switch (i) {
                case 0:
                    html += '<a target="_blank" title="' + maxElev[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxElev[0] + '">' + fInt(maxElev[1] * smlLenMult) + ' ' + smlLenUnit + ' - ' + maxElev[2] + '</a>';
                    break;
                case 1:
                    html += '<br/>' + suf(i + 1) + ': <a target="_blank" title="' + maxElev[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxElev[0] + '">' + fInt(maxElev[1] * smlLenMult) + ' ' + smlLenUnit + '</a>';
                    break;
                default:
                    html += ', ' + suf(i + 1) + ': <a target="_blank" title="' + maxElev[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxElev[0] + '">' + fInt(maxElev[1] * smlLenMult) + ' ' + smlLenUnit + '</a>';
            }
        }
        d3.select('#maxElev')
            .html(html);
    } else {
        d3.select('#maxElev')
            .html('n/a');
    }

    if (data.su.maxTime[0] != null) {
        var html = '';
        for (var i = 0; i < data.su.maxTime.length; i++) {
            var maxTime = data.su.maxTime[i].split('|');
            var v = parseInt(maxTime[1]);
            switch (i) {
                case 0:
                    html += '<a target="_blank" title="' + maxTime[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxTime[0] + '">' + v.toHrMinSec() + ' - ' + maxTime[2] + '</a>';
                    break;
                case 1:
                    html += '<br/>' + suf(i + 1) + ': <a target="_blank" title="' + maxTime[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxTime[0] + '">' + v.toHrMinSec() + '</a>';
                    break;
                default:
                    html += ', ' + suf(i + 1) + ': <a target="_blank" title="' + maxTime[2] + '" href="/athletes/' + contextAthleteId + '/' + actWrk + '/' + maxTime[0] + '">' + v.toHrMinSec() + '</a>';
            }
        }
        d3.select('#maxTime')
            .html(html);
    } else {
        d3.select('#maxTime')
            .html('n/a');
    }

    d3.selectAll('.recent tbody tr').remove();
    var recTr = d3.select('.recent tbody')
        .selectAll('tr')
        .data(data.su.recent)
        .enter()
        .append('tr');

    recTr.append('td')
        .append('a')
        .attr('target', '_blank')
        .attr('href', function(d) {
            return '/athletes/' + contextAthleteId + '/' + actWrk + '/' + d.i
        })
        .html(function(d) {
            return d.an
        });

    recTr.append('td').text(function(d) {
        return fDate(d.date)
    });
    recTr.append('td').text(function(d) {
        return f1dp(d.d * lrgLenMult) + ' ' + lrgLenUnit
    });
    recTr.append('td').text(function(d) {
        return fInt(d.eg * smlLenMult) + ' ' + smlLenUnit
    });
    recTr.append('td').text(function(d) {
        return d.mt.toHrMinSec();
    });
    recTr.append('td').text(function(d) {
        return d.t
    });
    recTr.append('td').text(function(d) {
        return (typeof(d.g) === 'undefined' || d.null ? 'n/a' : d.g)
    });

    if (loggedInAthleteId == contextAthleteId) {
        recTr.append('td').html(function(d) {
            return '<label for="file-upload-' + d.i + '" style="margin:-2px" class="btn btn-primary btn-mini"><i class="icon-camera icon-white"></i> Add Image</label><input style="display:none" id="file-upload-' + d.i + '" type="file"/>';
        });

        data.su.recent.forEach(function(d) {
            var imageLoader = document.getElementById('file-upload-' + d.i);
            imageLoader.i = d.i;
            imageLoader.addEventListener('change', function(e) {
                var reader = new FileReader();
                reader.i = this.i;
                reader.onload = function(event) {
                    var img = new Image();
                    img.i = this.i;
                    img.onload = function() {
                        var canvas = document.createElement('canvas');
                        //document.body.appendChild(canvas);
                        canvas.width = img.width;
                        canvas.height = img.height;
                        if (img.width > 2048 || img.height > 2048) {
                            canvas.width = img.width * 2048 / Math.max(img.width, img.height);
                            canvas.height = img.height * 2048 / Math.max(img.width, img.height);
                        }
                        var ctx = canvas.getContext('2d');
                        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                        /*var vvimg = new Image();
                        vvimg.width = 175;
                        vvimg.height = 28;
                        var svg64 = btoa(vvwrsvg);
                        var b64Start = 'data:image/svg+xml;base64,';

                        // prepend a "header"
                        var image64 = b64Start + svg64;
                        vvimg.i = this.i;
                        vvimg.ctx = ctx;
                        vvimg.canvas = canvas;

                        // set it as the source of the img element
                        vvimg.onload = function() {
                            this.ctx.drawImage(vvimg, canvas.width/2 - 175/2, canvas.height - 36);*/
                        var imgData = canvas.toDataURL("image/jpeg"); //, 1);

                        $.post('/save/saveStravaImage.php', {
                            imgdata: imgData.substr(imgData.indexOf(',') + 1).toString(),
                            type: 'a',
                            id: this.i,
                            title: undefined,
                            text: undefined,
                            success: function() {
                                alert('The image has been attached to your Strava activity.')
                            }
                        });
                        /*};
                        vvimg.src = image64;*/
                    }
                    img.src = event.target.result;
                }
                reader.readAsDataURL(e.target.files[0]);
            }, false);
        });
    } else {
        recTr.append('td').html('&nbsp;');
    }

    d3.select('#numPRs')
        .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + getQSFilter(28, 5, 34) + '">' + fInt(data.su.totalSegments) + '</a> from ' + fInt(data.su.totalEfforts) + ' efforts at ' + fInt(data.su.totalUniqueSegents) + ' segments.');

    d3.select('#numPRsTries')
        .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '7:2|1000000">' + fInt(data.su.totalSegsMultiTries) + '</a>');

    setStarredData();

    if (data.su.topScore != null) {
        var topScore = data.su.topScore.split('|');
        d3.select('#topPosScore')
            .html(!s_s ? s_sLink : f3dp(topScore[1]) + ' - <a target="_blank" title="' + topScore[4] + ' - ' + suf(topScore[2]) + ' of ' + fInt(topScore[3]) + '" href="/segments/' + topScore[0] + '">' + topScore[4] + ' - ' + suf(topScore[2]) + ' of ' + fInt(topScore[3]) + '</a>');
    } else {
        d3.select('#topPosScore')
            .html(!s_s ? s_sLink : 'n/a');
    }

    d3.select('#climbsTable tbody')
        .html(null);

    var climbTrs = d3.select('#climbsTable tbody')
        .selectAll('tr')
        .data(climbCats);

    climbTrs.enter()
        .append('tr');

    climbTrs.append('td')
        .text(function(d) {
            return d.desc
        });

    climbTrs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return '<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, 34) + ',').replace(/^,/, '') + '19:' + d.desc + '">' + fInt(data.su['cat' + d.code + 'Count']) + '</a>'
        });

    climbTrs.append('td')
        .classed('ar', true)
        .html(function(d) {
            if (data.su['cat' + d.code + 'TopScore'] != null) {
                var topScore = data.su['cat' + d.code + 'TopScore'].split('|');
                return !s_s ? s_sLink : '<a target="_blank" title="' + topScore[4] + ' - ' + suf(topScore[2]) + ' of ' + fInt(topScore[3]) + '" href="/segments/' + topScore[0] + '">' + f2dp(topScore[1]) + '</a>';
            } else {
                return 'n/a';
            }
        });

    climbTrs.append('td')
        .classed('ar', true)
        .html(function(d) {
            if (data.su['cat' + d.code + 'MaxVam'] != null) {
                var maxVam = data.su['cat' + d.code + 'MaxVam'].split('|');
                return '<a target="_blank" title="' + maxVam[4] + '" href="/segments/' + maxVam[0] + '">' + fInt(maxVam[1]) + '</a>';
            } else {
                return 'n/a';
            }
        });

    climbTrs.append('td')
        .classed('ar', true)
        .text(function(d) {
            if (data.su['cat' + d.code + 'AvgVam'] != null) {
                return fInt(data.su['cat' + d.code + 'AvgVam']);
            } else {
                return 'n/a';
            }
        });

    climbTrs.append('td')
        .classed('ar', true)
        .html(function(d) {
            if (data.su['cat' + d.code + 'MaxRp'] != null) {
                var maxRp = data.su['cat' + d.code + 'MaxRp'].split('|');
                return '<a target="_blank" title="' + maxRp[4] + '" href="/segments/' + maxRp[0] + '">' + f2dp(maxRp[1]) + '</a>';
            } else {
                return 'n/a';
            }
        });

    climbTrs.append('td')
        .classed('ar', true)
        .text(function(d) {
            if (data.su['cat' + d.code + 'AvgRp'] != null) {
                return f2dp(data.su['cat' + d.code + 'AvgRp']);
            } else {
                return 'n/a';
            }
        });

    //drawZones(data.su);

    setDlWidth();
}

var yearDistChartData;
var viewBy = (hasLocalStorage && typeof(localStorage['SummaryYearDistChart']) !== 'undefined' ? localStorage['SummaryYearDistChart'] : 'yrCumDist');
var showYDProjection = (hasLocalStorage && typeof (localStorage['SummaryShowYDProjection']) !== 'undefined' ? localStorage['SummaryShowYDProjection'] == 1 : true);

viewBy = ['maxsquare', 'maxcluster'].indexOf(viewBy) > -1 ? 'yrCumDist' : viewBy;

function setYearDistanceData() {
    let tileKeys = [];
    let tilesCount = 0;
    let newKeys = [];

    yearDistData = [];

    /*var ig = false;
    if (selType.indexOf('#GEAR#') == 0) {
      ig = true;
    }
    var l_selType = selType.replace('#GEAR#', '');*/
    var i, j, aCount, aCount1, aCountLastD, dist, elev, time, timeElapsed, suffer, cal, tl, prs, prs2, edd, yrTiles, yrTiles2, atTiles = {};
    var prevMaxSquare = 0, prevMaxCluster = 0, currentYear = 0;

    yearDistData = a.filter(function(d) {
        return (selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1) && (!filterYears || selYear.length == 0 || selYear.indexOf(d.y) > -1) && (viewBy.indexOf('Explorer') == -1 || d.t.indexOf('Virtual') == -1)
    });
    yearDistData.sort(function(a, b) {
        return parseDateTime(a.s, a.tz) - parseDateTime(b.s, b.tz);
    });

    for (i = 0; i < yearDistData.length; i++) {
        if (currentYear != yearDistData[i].date.getFullYear()) {
            aCount = 0;
            aCount1 = 0;
            aCountLastD = '';
            dist = 0;
            elev = 0;
            time = 0;
            timeElapsed = 0;
            suffer = 0;
            red = 0;
            tl = 0;
            kc = 0;
            cal = 0;
            prs = 0;
            prs2 = 0;
            yrTiles2 = 0;
            edd = {};
            yrTiles = {};
            yrTiles3 = Object.keys(atTiles).length;
            currentYear = yearDistData[i].date.getFullYear();
        }

        aCount++;
        if ((typeof(yearDistData[i].ls) !== 'undefined' ? yearDistData[i].ls : yearDistData[i].s).substr(0, 10) != aCountLastD) {
            aCount1++;
            aCountLastD = (typeof(yearDistData[i].ls) !== 'undefined' ? yearDistData[i].ls : yearDistData[i].s).substr(0, 10);
        }
        dist += parseFloat(yearDistData[i].d);
        if (['AlpineSki','Snowboard'].indexOf(yearDistData[i].t) == -1) {
            elev += parseFloat(yearDistData[i].eg);
        }
        time += parseFloat(yearDistData[i].mt);
        timeElapsed += parseFloat(yearDistData[i].et);
        suffer += parseFloat(yearDistData[i].sc);
        red += parseFloat(yearDistData[i].r);
        tl += parseFloat(yearDistData[i].tl == null ? 0 : yearDistData[i].tl);
        kc += parseFloat(yearDistData[i].kc);
        cal += parseFloat(yearDistData[i].ca);
        prs += parseFloat(yearDistData[i].prs);
        prs2 += parseFloat(yearDistData[i].prs2);
        if (viewBy == 'yrEddington' || viewBy == 'yrEddingtonKm') {
            var dd = (typeof(yearDistData[i].ls) !== 'undefined' ? yearDistData[i].ls : yearDistData[i].s).substr(0, 10);
            edd[dd] = (typeof(edd[dd]) === 'undefined' ? 0 : edd[dd]) + yearDistData[i].d;
        }
        if (viewBy == 'yrExplorer') {
            if (typeof(yearDistData[i].tiles) !== 'undefined' /*&& yearDistData[i].t != 'VirtualRide' && yearDistData[i].t != 'VirtualRun'*/ ) {
                for (var j = 0; j < yearDistData[i].tiles.length; j++) {
                    if (!yearDistData[i].tiles[j].deleted) {
                        yrTiles[yearDistData[i].tiles[j].x + '-' + yearDistData[i].tiles[j].y] = 1;
                    }
                }
            }
        }
        if (viewBy == 'allTimeByYearExplorer') {
            if (typeof(yearDistData[i].tiles) !== 'undefined' /*&& yearDistData[i].t != 'VirtualRide' && yearDistData[i].t != 'VirtualRun'*/ ) {
                for (var j = 0; j < yearDistData[i].tiles.length; j++) {
                    if (!yearDistData[i].tiles[j].deleted) {
                        atTiles[yearDistData[i].tiles[j].x + '-' + yearDistData[i].tiles[j].y] = 1;
                    }
                }
            }
        }
        if (viewBy == 'yrExplorerAll') {
            yrTiles2 += typeof(yearDistData[i].tilesLength) !== 'undefined' /*&& yearDistData[i].t != 'VirtualRide' && yearDistData[i].t != 'VirtualRun'*/ ? yearDistData[i].tilesLength : 0;
        }
        if (viewBy == 'allTimeExplorer') {
            if (typeof(yearDistData[i].tiles) !== 'undefined' /*&& yearDistData[i].t != 'VirtualRide' && yearDistData[i].t != 'VirtualRun'*/ ) {
                for (var j = 0; j < yearDistData[i].tiles.length; j++) {
                    if (!yearDistData[i].tiles[j].deleted) {
                        atTiles[yearDistData[i].tiles[j].x + '-' + yearDistData[i].tiles[j].y] = 1;
                    }
                }
            }
        }

        //add custom columns
        yearDistData[i].yrCumCount = aCount;
        yearDistData[i].yrCumCount1 = aCount1;
        yearDistData[i].yrCumDist = dist;
        yearDistData[i].yrCumElev = elev;
        yearDistData[i].yrCumDeq = dist + elev * 10;
        yearDistData[i].yrCumTime = time;
        yearDistData[i].yrCumTimeElapsed = timeElapsed;
        yearDistData[i].yrCumSuffer = suffer;
        yearDistData[i].yrCumRed = red;
        yearDistData[i].yrCumTL = tl;
        yearDistData[i].yrCumKC = kc;
        yearDistData[i].yrCumCal = cal;
        yearDistData[i].yrCumPrs = prs;
        yearDistData[i].yrCumPrs2 = prs2;
        if (viewBy == 'yrExplorer') {
            yearDistData[i].yrExplorer = Object.keys(yrTiles).length;
        }
        if (viewBy == 'allTimeByYearExplorer') {
            yearDistData[i].allTimeByYearExplorer = Object.keys(atTiles).length - yrTiles3;
        }
        if (viewBy == 'yrExplorerAll') {
            yearDistData[i].yrExplorerAll = yrTiles2;
        }
        if (viewBy == 'allTimeExplorer') {
            yearDistData[i].allTimeExplorer = Object.keys(atTiles).length;
        }
        if (viewBy == 'yrEddington' || viewBy == 'yrEddingtonKm') {
            var ev = d3.values(edd);
            ev.sort(d3.descending);
            yearDistData[i][viewBy] = ev.filter(function(d, i) {
                return d * (viewBy == 'yrEddington' ? 0.000621371192 : 0.001) >= i + 1
            }).length;
        }

      if (viewBy == 'maxsquare' || viewBy == 'maxcluster') {
       if(typeof(yearDistData[i].maxsquare) === 'undefined'){
         if(yearDistData[i].tiles && yearDistData[i].tiles.length > 0 && !yearDistData[i].deleted){
            //any new tiles?
            tilesCount = tileKeys.length;
            newKeys = yearDistData[i].tiles.map(d => d.x + '-' + d.y);
            tileKeys = [...new Set([...tileKeys, ...newKeys])];
            //console.debug('['+i+']['+newKeys.toString()+']');
            if(tileKeys.length > tilesCount){
              // calc based on exploration to date of activity
              // no need to filter on date given the activities are already sorted by date
              var s = {};
              setExplorer(s, tileKeys.map(function (d) {  let t = d.split('-'); return ({ x: +t[0], y: +t[1] })}) )//yearDistData.slice(0, i));
              prevMaxSquare = typeof(s.explorerMax) !== 'undefined' ? s.explorerMax : prevMaxSquare;
              prevMaxCluster = typeof(s.explorerClumpMax) !== 'undefined' ? s.explorerClumpMax : prevMaxCluster;
            }
          }
          //set both
          yearDistData[i].maxsquare = prevMaxSquare;
          yearDistData[i].maxcluster = prevMaxCluster;
        }
      }
    }

    if (zoomYearChart) {
        yearDistData = yearDistData.filter(function(d) {
            return d.date.yearDay() <= (new Date()).yearDay() + 7
        });
    }

    filteredData = years //(selYear.length == 0 ? years : selYear)
        .map(function(d) {
            return {
                "year": d,
                "activities": yearDistData.filter(function(e) {
                    return e.date.getFullYear() == parseInt(d)
                })

            }
        });

    if (zoomYearChart == 2) {
        filteredData.forEach(function(y) {
            var lastActs = y.activities.filter(function(d) {
                return d.date.yearDay() < (new Date()).yearDay() - 30
            });
            if (lastActs.length > 0) {
                y.previousY = lastActs.last()[viewBy];
            }
            y.activities = y.activities.filter(function(d) {
                return d.date.yearDay() >= (new Date()).yearDay() - 30
            });
        });
    }

    yearDistChartData = [];

    for (i = 0; i < filteredData.length; i++) {
        var v1 = filteredData[i];
        if (v1.activities.length > 0) {
            var v2;
            var now = new Date();
            var ld1 = {
                "key": v1.year,
                "values": [{
                    "x": zoomYearChart == 2 ? Math.max(new Date(2000, 0, 1).getTime(), new Date(2000, now.getMonth(), now.getDate() - 30).getTime()) : new Date(2000, 0, 1).getTime(),
                    "y": ((viewBy == 'allTimeExplorer' || viewBy == 'maxsquare' || viewBy == 'maxcluster') && i < filteredData.length - 1 ? (function() {
                        j = i;
                        while (j < filteredData.length - 1) {
                            if (filteredData[i + 1].activities.length) {
                                return filteredData[i + 1].activities.last()[viewBy]
                            }
                            j++;
                        }
                        return 0
                    })() : (filteredData[i].previousY ? filteredData[i].previousY : 0))
                }],
                "color": vv_category20[i]
            };
            for (j = 0; j < v1.activities.length; j++) {
                v2 = v1.activities[j];
                ld1.values.push({
                    "x": parseDateTime(v2.s, v2.tz).setFullYear(2000),
                    "y": v2[viewBy],
                    d: v2
                });
            }

            if (v1.year == new Date().getFullYear()) // || selYear != 'All'
            {
                ld1.area = true;
            }

            var nd = new Date();

            ld1.values.push({
                "x": (v1.year < new Date().getFullYear() ?
                    (!zoomYearChart ?
                        new Date(2000, 11, 31, 23, 59, 59).getTime() :
                        Math.min(new Date(2000, nd.getMonth(), nd.getDate() + 1, 0, -1).getTime() + (8 * 24 * 60 * 60 * 1000), new Date(2000, 11, 31, 23, 59, 59).getTime())) :
                    new Date().setFullYear(2000)),
                "y": v2[viewBy]
            });

            yearDistChartData.push(ld1);
        }
    }

    yearDistChartData.sort(function(a, b) {
        return b.key - a.key
    });

    previousYearTrend = -1, projectedYearLastAct = -1;
    if (yearDistChartData.length > 0 && +yearDistChartData[0].key == new Date().getFullYear() && !zoomYearChart && showYDProjection) {
        var lastAct = yearDistChartData[0].values.last();
        projectedYearLastAct = lastAct;
        projectedYearFirstAct = yearDistChartData[0].values[1];
        if (yearDistChartData.length > 1) {
            var x00 = new Date(2000, 0, 1).getTime();
            var v = yearDistChartData[0].values.filter(function(d) {
                return d.y > 0
            });
            var x0 = ((v.length == 0 ? x00 : v[0].x) - x00) / 86400000;
            var x1 = (new Date().setFullYear(2000) - x00) / 86400000;
            var x2 = (new Date(2001, 0, 1).getTime() - x00) / 86400000;
            var y0 = (viewBy == "allTimeExplorer" && yearDistChartData[0].values.length > 0 ? yearDistChartData[0].values[0].y : 0);
            var y1 = lastAct.y;

            var t = yearDistChartData.slice(1, 3).map(function(d) {
                    return {
                        i: d3.bisect(d.values.map(function(e) {
                            return (e.x - x00) / 86400000
                        }), x1),
                        values: d.values
                    }
                })
                .filter(function(d) {
                    return d.i > 0
                })
                .map(function(d) {
                    var v = d.values.filter(function(d) {
                        return d.y > 0
                    });
                    return {
                        x0: v.length == 0 || (v[0].x - x00) / 86400000 > x1 ? x00 : (v[0].x - x00) / 86400000,
                        y0: (viewBy == "allTimeExplorer" && d.values.length > 0 ? d.values[0].y : 0),
                        x1: x1,
                        y1: /*d.values[d.i - 1].y == d.values[d.i - 1].y ? d.values.last().y / 2 :*/ d.values[d.i - 1].y,
                        x2: x2,
                        y2: d.values.last().y
                    }
                })
                .map(function(d) {
                    return {
                        d0: d.x1 <= d.x0 ? 0 : (d.y1 - d.y0) / (d.x1 - d.x0),
                        d1: d.x2 == d.x1 ? 0 : (d.y2 - d.y1) / (d.x2 - d.x1)
                    }
                })
                .map(function(d) {
                    return d.d0 / Math.max(d.d1, 0.00000000000001)
                });
            var m = t[0];
            if (t.length == 2) {
                //y2
                m = (t[0] + t[0] + t[1]) / 3;
            }
            previousYearTrend = (m == 0 ? 0 : (((y1 - y0) / (x1 - x0)) / m) * (x2 - x1)) + y1;
        }
    }

    if (selYear.length > 0) {
        yearDistChartData = yearDistChartData.filter(function(d) {
            return selYear.indexOf(+d.key) > -1
        });
    }

    if (yearDistChartData.length == 0) {
        d3.selectAll('.nv-group').remove();
    }
}

function projectedRangeChanged() {
    if (hasLocalStorage) {
        localStorage['projectedRangeValue'] = this.value;
    }
    var s1 = d3.scale.linear().range([previousYearTrend == -1 ? 0 : 1, 0, 0]).domain([0, 50, 100]);
    var s2 = d3.scale.linear().range([30, 30, 7]).domain([0, 50, 100]);
    prs1p = s1(this.value);
    prs2p = 1 - prs1p;
    prs2d = Math.round(s2(this.value));
    d3.select('#projectedRangeS1').style('opacity', previousYearTrend == -1 ? 0.3 : null).text('Previous years\' trends (' + fInt(100 * prs1p) + '%)');
    d3.select('#projectedRangeS2').text('Last ' + fInt(prs2d) + ' days (' + fInt(100 * prs2p) + '%)');

    if (yearDistChartData.length > 0 && !zoomYearChart && showYDProjection && +yearDistChartData[0].key == new Date().getFullYear()) {
        var pye1 = 0;
        if (previousYearTrend > -1) {
            pye1 = previousYearTrend; // * ((projectedYearLastAct.y - projectedYearFirstAct.y) / (new Date().setFullYear(2000) - projectedYearFirstAct.x)) * (new Date(2001, 0, 1) - new Date().setFullYear(2000)) + projectedYearLastAct.y;
        }
        var td = new Date(new Date().setFullYear(2000));
        td.setDate(td.getDate() - prs2d);

        if (td.getFullYear() == 1999 && yearDistChartData.length > 1) {
            td.setFullYear(2000);
            var fa = yearDistChartData[1].values[d3.bisect(yearDistChartData[1].values.map(function(e) {
                return e.x
            }), td)];
            var lyv = 0;
            if (typeof(fa) !== 'undefined') {
                lyv = yearDistChartData[1].values.last().y - fa.y;
            }
            var pye2 = daysLeftInYear * (lyv + projectedYearLastAct.y) / prs2d + projectedYearLastAct.y;
        } else {
            var fa = yearDistChartData[0].values[d3.bisect(yearDistChartData[0].values.map(function(e) {
                return e.x
            }), td)];
            var pye2 = daysLeftInYear * (projectedYearLastAct.y - fa.y) / prs2d + projectedYearLastAct.y;
        }

        /*if (viewBy == 'allTimeExplorer') {
            projectedYearDistVal = (yearDistChartData[0].values.last().y + pye1) * prs1p + pye2 * prs2p;
        } else {*/
        projectedYearDistVal = pye1 * prs1p + pye2 * prs2p;
        //}

        chart.forceX([new Date(2000, 0, 1).getTime(), new Date(2000, 11, 31, 23, 59, 59).getTime()]);
        chart.forceY([0, Math.max(d3.max(yearDistChartData, function(d) {
            return d.values[d.values.length - 1].y
        }), projectedYearDistVal)]);
        chart.update();
        d3.select('#projectedValue').html(ydyf(projectedYearDistVal));
        d3.select('#projectedValueWrapper').attr('title', 'Based on your previous history this is your projected total for the year end. That would be an average of ' + ydyf(7 * (projectedYearDistVal - projectedYearLastAct.y) / daysLeftInYear) + ' per week for the rest of year.');
    } else {
        d3.select('#projectedValue').html('n/a');
        d3.select('#projectedValueWrapper').attr('title', 'Projected value not available for current selection.');
    }

    d3.select('.projectionLine')
        .attr('x1', chart.xScale()(projectedYearLastAct.x))
        .attr('x2', chart.xScale()(new Date(2000, 11, 31, 23, 59, 59).getTime()))
        .attr('y1', chart.yScale()(projectedYearLastAct.y))
        .attr('y2', chart.yScale()(projectedYearDistVal));

    d3.selectAll('#yearDistChart .nv-legend .nv-series circle').each(function(d, i) {
        d.color = this.style.fill;
        d3.selectAll('#yearDistChart .nv-groups .nv-group.nv-series-' + i).style({
            stroke: this.style.fill,
            fill: this.style.fill
        })
    })

    window.setTimeout(chart.update, 10);
}

var ydyf, prs1p, prs2p, prs2d;

function displayYearDistances() {
    if (hasLocalStorage) {
        localStorage['SummaryYearDistChartZoom'] = zoomYearChart;
        localStorage['SummaryYearDistChart'] = viewBy;
    }

    setYearDistanceData();

    window.setTimeout(function() {
        d3.select('#yearDistChart svg').style('height', Math.max(parseInt(d3.select("#calContainer").style('height')), 300) + 'px');

        nv.addGraph(function() {
            if (typeof(chart) !== 'undefined' && typeof(chart.tooltip) !== 'undefined') {
                d3.select('#' + chart.tooltip.id()).remove();
            }
            chart = nv.models.lineChart()
                //.useInteractiveGuideline(true)
                .interpolate('step-after');
            chart.duration(0);

            chart.tooltip.contentGenerator(function(data) {
                var l = -1;
                var labels = [];
                var h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + d3.time.format('%d %B')(new Date(data.value)) + ' ' + data.series[0].key + '</strong></div>';

                h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px;font-size:0.8em">' + (typeof(data.point.d) !== 'undefined' ? data.point.d.an : 'n/a') + '</div>';
                h += '<table><tbody>';

                var cdo = d3.selectAll('#yearDistChart .nv-legend circle');
                var cd = cdo[0].map(function(d) {
                    return d.__data__
                });
                var cdc = [];
                cdo.each(function(d) {
                    cdc.push(d3.select(this).style('fill'))
                });

                cd.forEach(function(c){
                    c.y = 0;
                    c.isProjected = false;
                    if (+c.key == new Date().getFullYear() && d3.selectAll('.projectionLine').node() != null && d3.selectAll('.projectionLine').node() && projectedYearLastAct.x < data.value) {
                        c.isProjected = true;
                        c.y = projectedYearLastAct.y + (data.value - projectedYearLastAct.x) * (projectedYearDistVal - projectedYearLastAct.y) / (new Date(2000, 11, 31, 23, 59, 59).getTime() - projectedYearLastAct.x);
                    } else {
                        c.y = c.values[d3.bisectRight(c.values.map(function(d) {
                            return d.x
                        }), data.value) - 1].y;
                    }
                });
                var ySort = cd.map(function(c){ return c.y}).sort(function(a,b){return b - a});
                cd.forEach(function(c){
                    c.rank = ySort.indexOf(c.y) + 1
                });
                for (var i = 0; i < cd.length; i++) {
                    h += '<tr' + (data.seriesIndex == i && cd.length > 1 ? ' style="background-color:#ddd"' : '') + '><td class="legend-color-guide"><div style="background-color:' + cdc[i] + '"></div></td>';
                    h += '<td class="key">' + cd[i].key + '</td>';
                    h += cd[i].isProjected ? '<td class="value">(' + ydyf(cd[i].y) + ')</td>' : '<td class="value">' + ydyf(cd[i].y) + '</td>';
                    h += '<td class="value">' + suf(cd[i].rank) + '</td>';
                    h += '<td class="value">' + (data.seriesIndex == i ? '&nbsp;</td><td class="value">' : (cd[i].y - data.point.y >= 0 ? '+' : '') + ydyf(cd[i].y - data.point.y) + '</td><td class="value">' + (data.point.y == 0 ? '&nbsp;' : (cd[i].y - data.point.y >= 0 ? '+' : '') + fPercent((cd[i].y - data.point.y) / data.point.y))) + '</td></tr>';
                }
                h += '</tbody></table></div>';
                return h;
            });

            chart.xAxis
                .tickFormat(function(d) {
                    return d3.time.format('%d %b')(new Date(d))
                });

            switch (viewBy) {
                case 'yrCumDist':
                case 'yrCumDeq':
                    ydyf = function(d) {
                        return d3.format(',.0f')(d * lrgLenMult) + ' ' + lrgLenUnit
                    };
                    break;
                case 'yrCumElev':
                    ydyf = function(d) {
                        return d3.format(',.0f')(d * smlLenMult) + ' ' + smlLenUnit
                    };
                    break;
                case 'yrCumTime':
                case 'yrCumTimeElapsed':
                    ydyf = function(d) {
                        return d.toHrMinSec();
                    };
                    break;
                case 'yrCumCount':
                case 'yrCumCount1':
                    ydyf = function(d) {
                        return d3.format(',.0f')(d)
                    };
                    break;
                case 'yrCumSuffer':
                case 'yrCumRed':
                case 'yrCumTL':
                case 'yrCumKC':
                case 'yrCumCal':
                case 'yrCumPrs':
                case 'yrCumPrs2':
                case 'yrEddington':
                case 'yrEddingtonKm':
                case 'yrExplorer':
                case 'yrExplorerAll':
                case 'allTimeExplorer':
                case 'allTimeByYearExplorer':
                case 'maxsquare':
                case 'maxcluster':
                    ydyf = fInt;
                    break;
            }
            chart.yAxis.tickFormat(ydyf);

            d3.select('#yearDistChart svg')
                .datum(yearDistChartData)
                .transition().duration(0)
                .call(chart);

            d3.selectAll('.projectionLine').remove();
            if (yearDistChartData.length > 0 && !zoomYearChart && showYDProjection && +yearDistChartData[0].key == new Date().getFullYear()) {
                var lastAct = yearDistChartData[0].values[yearDistChartData[0].values.length - 1];
                d3.selectAll('#yearDistChart svg .nv-linesWrap > g > g > .nv-groups')
                    .append('line')
                    .classed('projectionLine', true)
                    .attr({
                        x1: chart.xScale()(lastAct.x),
                        x2: chart.xScale()(new Date(2000, 11, 31, 23, 59, 59)),
                        y1: chart.yScale()(lastAct.y),
                        y2: chart.yScale()(lastAct.y)
                    })
                    .style({
                        stroke: 'rgb(31, 119, 180)',
                        'stroke-width': '1.5px',
                        'stroke-dasharray': '2,2'
                    });
            }
            projectedRangeChanged.call(document.getElementById('projectedRangeValue'));

            nv.utils.windowResize(function() {
                chart.update();
                if (yearDistChartData.length > 0) {
                    var lastAct = yearDistChartData[0].values[yearDistChartData[0].values.length - 1];
                    d3.select('.projectionLine').style('display', null)
                        .attr({
                            x1: chart.xScale()(lastAct.x),
                            x2: chart.xScale()(new Date(2000, 11, 31, 23, 59, 59)),
                            y1: chart.yScale()(lastAct.y),
                            y2: chart.yScale()(projectedYearDistVal)
                        });
                }
            });

            chart.dispatch.on('stateChange', function(e) {
                if (e.disabled[0]) {
                    d3.select('.projectionLine').style('display', 'none');
                } else {
                    d3.select('.projectionLine').style('display', null)
                    window.setTimeout(function() {
                        if (yearDistChartData.length > 0) {
                            var lastAct = yearDistChartData[0].values[yearDistChartData[0].values.length - 1];
                            d3.select('.projectionLine')
                                .attr({
                                    x1: chart.xScale()(lastAct.x),
                                    x2: chart.xScale()(new Date(2000, 11, 31, 23, 59, 59)),
                                    y1: chart.yScale()(lastAct.y),
                                    y2: chart.yScale()(projectedYearDistVal)
                                });
                        }
                    }, 10);
                }
            });

            d3.selectAll('#yearDistChart .nv-point-paths')
                .style('cursor', 'pointer');

            chart.lines.dispatch.on("elementClick", function(e) {
                window.open('/athletes/' + contextAthleteId + '/' + actWrk + '/' + e.point.d.ai);
            });

            return chart;
        });
    }, 10);
}

function drawOutline() {
    calWidth = parseInt(d3.select("#calContainerInner").style('width')); // - 25;
    calCellSize = isRetinaDisplay() ? (calWidth - 6) / 55 : parseInt((calWidth - 6) / 55); // cell size
    calHeight = (calCellSize + 1) * 7;

    d3.select("#calContainer").selectAll("svg").remove();

    var yearExt = d3.extent(a.map(function(d) {
        return d.date.getFullYear()
    }));

    var distYears = {};
    a.map(function(d) {
        return d.date.getFullYear()
    }).forEach(function(d) {
        distYears[d] = d
    });
    distYears = d3.values(distYears).sort(d3.descending);

    var svg = d3.select("#calContainerInner").selectAll("svg:not(.calKey)")
        .data(distYears)
        .enter().append("svg")
        .attr('id', function(d) {
            return 'cal' + d
        })
        .attr("width", calWidth)
        .attr("height", calHeight)
        .attr("class", "RdYlGn")
        //.style('shape-rendering','geometricprecision')
        .append("g")
        .attr("transform", "translate(" + 15 + "," + (calHeight - calCellSize * 7 - 1) + ")");

    svg.append("text")
        .attr("transform", "translate(-6," + calCellSize * 3.5 + ")rotate(-90)")
        .style("text-anchor", "middle")
        .text(function(d) {
            return d;
        });

    rect = svg.selectAll(".day")
        .data(function(d) {
            return d3.time.days(new Date(d, 0, 1), new Date(d + 1, 0, 1));
        })
        .enter().append("rect")
        .attr("class", "day")
        .attr("width", calCellSize)
        .attr("height", calCellSize)
        .attr("x", function(d) {
            return fWeek(d) * calCellSize;
        })
        .attr("y", function(d) {
            return (fDay(d) - 1 < 0 ? 6 : fDay(d) - 1) * calCellSize;
        })
        .datum(fDate2);

    calYear = svg.selectAll(".year")
        .data(function(d) {
            return [new Date(d, 0, 1)];
        })
        .enter().append("path")
        .attr("class", "year")
        .style('display', 'none')
        .attr("d", yearPath)
        .datum(fYr);

    calYearDOW = svg.selectAll(".yearDOW")
        .data(function(d) {
            return d3.range(1, 8).map(function(f) {
                return new Date(d, 0, f)
            });
        })
        .enter().append("rect")
        .attr("class", "yearDOW")
        .style('display', 'none')
        .attr("x", function(d) {
            return fWeek(d) * calCellSize;
        })
        .attr("y", function(d) {
            return (fDay(d) - 1 < 0 ? 6 : fDay(d) - 1) * calCellSize;
        })
        .attr("height", calCellSize)
        .attr("width", function(d) {
            return (new Date(d.getFullYear(), 0, d.getDate() + 52 * 7).getFullYear() == d.getFullYear() ? 53 : 52) * calCellSize;
        })
        .datum(fYrDOW);

    month = svg.selectAll(".month")
        .data(function(d) {
            return d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1));
        })
        .enter().append("path")
        .attr("class", "month")
        .attr("d", monthPath)
        .datum(fYrMonth);

    calMonthDOW = svg.selectAll(".monthDOW")
        .data(function(d) {
            return d3.merge(d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1)).map(function(e) {
                return d3.range(1, 8).map(function(f) {
                    return new Date(e.getFullYear(), e.getMonth(), f)
                })
            }));
        })
        .enter().append("rect")
        .attr("class", "monthDOW")
        .style('display', 'none')
        .attr("x", function(d) {
            return fWeek(d) * calCellSize;
        })
        .attr("y", function(d) {
            return (fDay(d) - 1 < 0 ? 6 : fDay(d) - 1) * calCellSize;
        })
        .attr("height", calCellSize)
        .attr("width", function(d) {
            return (new Date(d.getFullYear(), d.getMonth(), d.getDate() + 4 * 7).getMonth() == d.getMonth() ? 5 : 4) * calCellSize;
        })
        .datum(fYrMonthDOW);

    week = svg.selectAll(".week")
        .data(function(d) {
            return d3.range(0, parseInt(fWeek(new Date(d + 1, 0, 0))) + 1).map(function(e) {
                return d + '-' + e
            });
        })
        .enter().append("rect")
        .attr("class", "week")
        .style('display', 'none')
        .attr("x", function(d) {
            return parseInt(d.split('-')[1]) * calCellSize
        })
        .attr("y", function(d) {
            var wk = d.split('-')[1];
            if (wk == '0') {
                var day = parseInt(d3.time.format('%w')(new Date(d.split('-')[0], 0, 1))) - 1;
                day = (day < 0 ? 6 : day);
                return day * calCellSize;
            } else {
                return 0;
            }
        })
        .attr("width", calCellSize)
        .attr("height", function(d) {
            var wk = d.split('-')[1];
            if (wk == '0') {
                var day = parseInt(d3.time.format('%w')(new Date(d.split('-')[0], 0, 1))) - 1;
                day = (day < 0 ? 6 : day);
                return (7 - day) * calCellSize;
            } else {
                if (wk == fWeek(new Date(parseInt(d.split('-')[0]) + 1, 0, 0))) {
                    var day = parseInt(d3.time.format('%w')(new Date(parseInt(d.split('-')[0]) + 1, 0, 0)));
                    day = day == 0 ? 7 : day;
                    return calCellSize * day;
                } else {
                    return calCellSize * 7;
                }
            }
        })
        .datum(fYrWeek);

    //var calHeight = Math.min(parseInt(d3.select("#calContainer").style('height')), 400);
    var keyPartWidth = (calWidth - 4) / 100;

    var key = d3.select("#calContainer").append('svg')
        .classed('calKey', true)
        .style('margin-top', '4px')
        .style('height', '20px')
        .style('width', calWidth + 'px');

    keyRects = key.selectAll('rect')
        .data(d3.range(0, 100, 1))
        .enter()
        .append('rect')
        .attr('x', function(d) {
            return 4 + (100 - d) * keyPartWidth - keyPartWidth
        })
        .attr('width', keyPartWidth)
        .attr('height', 10);

    keyMax = key.append("text")
        .attr("transform", "translate(" + calWidth + ",20)")
        .style("text-anchor", "end")
        .style('font-size', '0.95em');

    keyMin = key.append("text")
        .attr("transform", "translate(4,20)")
        .style("text-anchor", "start")
        .style('font-size', '0.95em');
}

function yearPath(t0) {
    var t1 = new Date(t0.getFullYear(), t0.getMonth() + 12, 0),
        d0 = (+fDay(t0) - 1 < 0 ? 6 : +fDay(t0) - 1),
        w0 = +fWeek(t0),
        d1 = (+fDay(t1) - 1 < 0 ? 6 : +fDay(t1) - 1),
        w1 = +fWeek(t1);
    return "M" + (w0 + 1) * calCellSize + "," + d0 * calCellSize + "H" + w0 * calCellSize + "V" + 7 * calCellSize + "H" + w1 * calCellSize + "V" + (d1 + 1) * calCellSize + "H" + (w1 + 1) * calCellSize + "V" + 0 + "H" + (w0 + 1) * calCellSize + "Z";
}

function monthPath(t0) {
    var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
        d0 = (+fDay(t0) - 1 < 0 ? 6 : +fDay(t0) - 1),
        w0 = +fWeek(t0),
        d1 = (+fDay(t1) - 1 < 0 ? 6 : +fDay(t1) - 1),
        w1 = +fWeek(t1);
    return "M" + (w0 + 1) * calCellSize + "," + d0 * calCellSize + "H" + w0 * calCellSize + "V" + 7 * calCellSize + "H" + w1 * calCellSize + "V" + (d1 + 1) * calCellSize + "H" + (w1 + 1) * calCellSize + "V" + 0 + "H" + (w0 + 1) * calCellSize + "Z";
}

function setCalData(istc, lCalGroup, lCalVal, filterYears, lfaData, lCalDOW) {
    var pmr = false;
    if (hasPowerMeter) {
        pmr = (lCalVal.substr(0, 2) == 'pz' || lCalVal == 'aw') && ((istc && !document.getElementById('tcPowerMeterOnlyCB').checked) || (!istc && !document.getElementById('ccPowerMeterOnlyCB').checked));
    }
    lfaData = a.filter(function(d) {
        return ((selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1) && (!filterYears || selYear.length == 0 || selYear.indexOf(d.y) > -1) && (!pmr || d.hp == 1))
    });

    var val = lCalVal;

    if (val == 'waw' || val == 'tl' || val == 'in' || val == 'scr' || val == 'sc' || val == 'r' || val.substr(0, 2) == 'hz' || val.substr(0, 2) == 'pz' || val.substr(0, 3) == 'pcz') {
        lfaData = lfaData.filter(function(d) {
            return typeof(d[val]) !== 'undefined' && d[val] != 0 || (val == 'scr' && d.sc + d.r > 0) ||
                (val.substr(0, 2) == 'hz' && d.hz1 + d.hz2 + d.hz3 + d.hz4 + d.hz5 > 0) ||
                (val.substr(0, 3) == 'pcz' && d.pcz1 + d.pcz2 + d.pcz3 + d.pcz4 + d.pcz5 + d.pcz6 > 0) ||
                (val.substr(0, 2) == 'pz' && d.pz1 + d.pz2 + d.pz3 + d.pz4 + d.pz5 + d.pz6 + d.pz7 + d.pz8 + d.pz9 + d.pz10 > 0)
        });
    }
    if (val == 'as') {
        lfaData = lfaData.filter(function(d) {
            return d.mt > 0 && d.d > 0;
        });
    }

    var valArr = [];

    if (val == 'rpb' || val == 'rpbp') {
        lfaData.sort(function(a, b) {
            return a.timestamp - b.timestamp
        });
        var bh = {
            b1k: [],
            b1m: [],
            b2m: [],
            b4: [],
            b5k: [],
            b10k: [],
            b10m: [],
            b15k: [],
            b20k: [],
            b30k: [],
            b50k: [],
            bh: [],
            bhm: [],
            bmar: []
        };
        lfaData.filter(function(d) {
            return d.t == 'Run'
        }).forEach(function(d) {
            for (var v in bh) {
                if (d[v] != null && (bh[v].length == 0 || bh[v][bh[v].length - 1][v] > d[v]))
                    bh[v].push(d);
            }
        });

        return bh;
    }

    if (val == 'streak') {
        var pd;
        var pda = [];
        lfaData.forEach(function(d) {
            d.di = Math.ceil(((new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate())).getTime() / 1000) / (60 * 60 * 24))
        })
        lfaData.sort(function(a, b) {
            return a.di - b.di
        });
        lfaData.forEach(function(d, i) {
            if (pda.length == 0) {
                pda.push(d);
                pd = d.di;
            } else {
                if (pd == d.di) {
                    //pda.push(d);
                } else {
                    if (d.di == pd + 1) {
                        pda.push(d);
                        pd = d.di;
                    } else {
                        //streaks.push(pda);
                        pda.forEach(function(e) {
                            e.streak = pda.length;
                        })
                        pda = [d];
                        pd = d.di;
                    }
                }
            }
        })
        pda.forEach(function(e) {
            e.streak = pda.length;
        })
    }

    var lcalDataFormat;
    switch (val) {
        case 'd':
        case 'deq':
            lcalDataFormat = function(dataVal) {
                return fDist(dataVal, 2) //d3.format(',.2f')(dataVal * lrgLenMult) + ' ' + lrgLenUnit
            }
            lcalTickFormat = function(dataVal) {
                return fDist(dataVal, 0)
            }
            break;
        case 'length':
        case 'length1':
        case 'scr':
        case 'sc':
        case 'kc':
        case 'tl':
            lcalDataFormat = function(dataVal) {
                return fInt(dataVal)
            }
            lcalTickFormat = function(dataVal) {
                return fInt(dataVal)
            }
            break;
        case 'r':
            lcalDataFormat = function(dataVal) {
                return dataVal + ' Points in red'
            }
            lcalTickFormat = function(dataVal) {
                return dataVal
            }
            break;
        case 'eg':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.0f')(dataVal * smlLenMult) + ' ' + smlLenUnit
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.0f')(dataVal * smlLenMult) + ' ' + smlLenUnit
            }
            break;
        case 'egd':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal * smlLenMult / lrgLenMult) + ' ' + smlLenUnit + '/' + lrgLenUnit
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal * smlLenMult / lrgLenMult) + ' ' + smlLenUnit + '/' + lrgLenUnit
            }
            break;
        case 'ph':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.2f')(dataVal) + ' W/bpm'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.2f')(dataVal) //+ ' W/bpm'
            }
            break;
        case 'sh':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.3f')(dataVal) + ' ' + speedUnit + '/bpm'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.3f')(dataVal) // + ' '+speedUnit+'/bpm'
            }
            break;
        case 'hd':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.0f')(dataVal) + ' beats/' + lrgLenUnit
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.0f')(dataVal) + ' /' + lrgLenUnit
            }
            break;
        case 'ah':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' bpm'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' bpm'
            }
            break;
        case 'ac':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' rpm'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' rpm'
            }
            break;
        case 'in':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' %'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' %'
            }
            break;
        case 'aw':
        case 'waw':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' W'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal) + ' W'
            }
            break;
        case 'tm':
            lcalDataFormat = function(dataVal) {
                return d3.format('.1f')(dataVal) + ' °' + tempUnit
            }
            lcalTickFormat = function(dataVal) {
                return d3.format('.1f')(dataVal) + ' °' + tempUnit
            }
            break;
        case 'prsc':
        case 'prsc2':
            lcalDataFormat = function(dataVal) {
                return d3.format('.2f')(dataVal)
            }
            lcalTickFormat = function(dataVal) {
                return d3.format('.2f')(dataVal)
            }
            break;
        case 'mt':
        case 'et':
        case 'hz1':
        case 'hz2':
        case 'hz3':
        case 'hz4':
        case 'hz5':
        case 'pz1':
        case 'pz2':
        case 'pz3':
        case 'pz4':
        case 'pz5':
        case 'pz6':
        case 'pz7':
        case 'pz8':
        case 'pz9':
        case 'pz10':
        case 'pz11':
        case 'pcz1':
        case 'pcz2':
        case 'pcz3':
        case 'pcz4':
        case 'pcz5':
        case 'pcz6':
        case 'rpb':
            lcalDataFormat = function(dataVal) {
                return dataVal.toHrMinSec();
            }
            lcalTickFormat = function(dataVal) {
                return dataVal.toHrMinSec();
            }
            break;
        case 'rpbp':
            lcalDataFormat = function(dataVal) {
                return (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit
            }
            lcalTickFormat = function(dataVal) {
                return (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit
            }
            break;
        case 'as':
            lcalDataFormat = function(dataVal) {
                return d3.format(',.2f')(dataVal * speedMult) + ' ' + speedUnit + ' (' + (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit + ')'
            }
            lcalTickFormat = function(dataVal) {
                return d3.format(',.1f')(dataVal * speedMult) + ' ' + speedUnit
            }
            break;
        case 'prs':
        case 'prs2':
            lcalDataFormat = function(dataVal) {
                return dataVal + ' PRs'
            }
            lcalTickFormat = function(dataVal) {
                return dataVal
            }
            break;
        case 'ca':
            lcalDataFormat = function(dataVal) {
                return fInt(dataVal) + ' kcal'
            }
            lcalTickFormat = function(dataVal) {
                return fInt(dataVal) + ' kcal'
            }
            break;
        case 'streak':
            lcalDataFormat = function(dataVal) {
                return fInt(dataVal) + ' consecutive days'
            }
            lcalTickFormat = function(dataVal) {
                return fInt(dataVal) + ' days'
            }
            break;
        case 'explorer':
        case 'explorer2':
        case 'tileLength':
            lcalDataFormat = function(dataVal) {
                return fInt(dataVal) + (val == 'explorer' ? ' new' : '') + ' map tiles covered'
            }
            lcalTickFormat = function(dataVal) {
                return fInt(dataVal) + ' tiles'
            }
            break;
    }

    lfaData.sort(function(a, b) {
        return a.timestamp - b.timestamp
    });
    var lTiles = {};

    var lCalData = d3.nest()
        .key(function(d) {
            switch (lCalGroup) {
                case 'day':
                    return fDate2(d.date);
                case 'week':
                    return fYrWeek(d.date);
                case 'month':
                    if (typeof(lCalDOW) !== 'undefined' && lCalDOW) {
                        return fYrMonthDOW(d.date);
                    }
                    return fYrMonth(d.date);
                case 'year':
                    if (typeof(lCalDOW) !== 'undefined' && lCalDOW) {
                        return fYrDOW(d.date);
                    }
                    return fYr(d.date);
            }
        })
        .rollup(function(d) {
            var date = new Date(d[0].date.getFullYear(), d[0].date.getMonth(), d[0].date.getDate())
            var obj = {
                'length': d.length,
                'date': date,
                'monday': getMonday(date),
                'firstId': d[0].i,
                data: d,
                id: lCalGroup + d[0].i
            };
            if (lCalGroup == 'week') {
                obj.date = obj.monday;
            }
            obj[val] = calGetObjVal(d, val, lTiles);
            if (d.length != 0) {
                valArr.push(obj[val]);
            }
            return obj;
        })
        .map(lfaData.filter(function(d) {
            return (val.substr(0, 4) == 'prsc' || val == 'egd' || val == 'length' || val == 'length1' || val.substr(0, 8) == 'explorer' || val == 'hd' || d[val] != null) && (val != 'ph' || (d.ah > 0 && d.aw > 0)) && (val != 'sh' || (d.ah > 0 && d.as > 0)) && (val != 'ah' || d.ah > 0) && (val != 'ac' || d.ac > 0) && (val != 'hd' || (d.ah > 0 && d.d > 0)) && (val != 'aw' || d.aw > 0)
        }));

    var lTiles = {};

    if (!istc && calRollRequired) {
        var ld = new Date();
        var data = lfaData.filter(function(d) {
            return (val.substr(0, 4) == 'prsc' || val == 'egd' || val == 'length' || val == 'length1' || val.substr(0, 8) == 'explorer' || val == 'hd' || d[val] != null) && (val != 'ph' || (d.ah > 0 && d.aw > 0)) && (val != 'sh' || (d.ah > 0 && d.as > 0)) && (val != 'ah' || d.ah > 0) && (val != 'ac' || d.ac > 0) && (val != 'hd' || (d.ah > 0 && d.d > 0)) && (val != 'aw' || d.aw > 0)
        }).filter(function(d) {
            return d.date >= new Date(ld.getFullYear(), ld.getMonth(), ld.getDate() + 1 - (calGroup == 'week' ? 7 : (calGroup == 'month' ? 30 : 365)));
        })
        d3.select('#calRoll').html(('Last ' + (calGroup == 'week' ? 7 : (calGroup == 'month' ? 30 : 365)) + ' days: ' + lcalTickFormat(calGetObjVal(data, val, lTiles))).replaceAll(' ', '&nbsp;'));
    }

    d3.keys(lCalData).forEach(function(d) {
        lCalData[d].key = d
    });

    var calMin = valArr.length == 0 ? 0 : d3.min(valArr);
    var calMax = valArr.length == 0 ? 1 : d3.max(valArr);

    return {
        data: lCalData,
        min: calMin,
        max: calMax,
        dataFormat: lcalDataFormat,
        tickFormat: lcalTickFormat
    };
}

function calGetObjVal(d, val, lTiles) {
    switch (val) {
        case 'length':
            return d.length;
            break;
        case 'length1':
            return arrayUnique(d.map(function(e) {
                return e.s.substr(0, 10)
            })).length;
            break;
        case 'd':
        case 'deq':
        case 'scr':
        case 'sc':
        case 'tl':
        case 'kc':
        case 'r':
        case 'mt':
        case 'et':
        case 'prs':
        case 'prs2':
        case 'ca':
        case 'hz1':
        case 'hz2':
        case 'hz3':
        case 'hz4':
        case 'hz5':
        case 'pz1':
        case 'pz2':
        case 'pz3':
        case 'pz4':
        case 'pz5':
        case 'pz6':
        case 'pz7':
        case 'pz8':
        case 'pz9':
        case 'pz10':
        case 'pz11':
        case 'pcz1':
        case 'pcz2':
        case 'pcz3':
        case 'pcz4':
        case 'pcz5':
        case 'pcz6':
            return d3.sum(d, function(e) {
                return e[val];
            });
            break;
        case 'eg':
            return d3.sum(d, function(e) {
                return ['AlpineSki','Snowboard'].indexOf(e.t) == -1 ? e[val] : 0;
            });
            break;
        case 'streak':
            return d.filter(function(e) {
                return typeof(e.streak) !== 'undefined'
            })[0].streak;
            break;
        case 'explorer':
        case 'explorer2':
            var oldC = Object.keys(lTiles).length;
            if (val == 'explorer2') {
                lTiles = {};
            }
            d.forEach(function(e) {
                if (typeof(e.tiles) !== 'undefined') {
                    for (var j = 0; j < e.tiles.length; j++) {
                        if (!e.tiles[j].deleted) {
                            lTiles[e.tiles[j].x + '-' + e.tiles[j].y] = 1;
                        }
                    }
                }
            });
            return Object.keys(lTiles).length - (val == 'explorer' ? oldC : 0);
            break;
        case 'as':
            return d3.sum(d, function(e) {
                return e['d'];
            }) / d3.sum(d, function(e) {
                return e['mt'];
            });
            break;
        case 'egd':
            return d3.sum(d, function(e) {
                return e['eg'];
            }) / d3.sum(d, function(e) {
                return e['d'];
            });
            break;
        case 'ph':
        case 'sh':
        case 'ah':
        case 'ac':
        case 'aw':
        case 'tm':
        case 'in':
        case 'waw':
            return d3.sum(d, function(e) {
                return e[val] * e['mt'];
            }) / d3.sum(d, function(e) {
                return e['mt'];
            });
            break;
        case 'hd':
            return d3.sum(d, function(e) {
                return e['ah'] * e['mt'] / 60;
            }) / d3.sum(d, function(e) {
                return e['d'] * lrgLenMult;
            });
            break;
        case 'prsc':
            var g = d3.merge(d.map(function(e) {
                return teffs[e.i]
            })).map(function(e) {
                return e.prs
            }).sort(d3.descending);
            g = g.filter(function(e) {
                return e != 50
            });
            return d3.mean(g.slice(0, Math.min(100, Math.floor(g.length / 4))));
            break;
        case 'prsc2':
            return d3.mean(d3.merge(d.map(function(e) {
                return teffs[e.i]
            })).filter(function(e) {
                return e.prs != 50
            }), function(e) {
                return e.prs
            });
            break;
    }
}

var tcChart, tcLineChart, tcContainer, tcSvg, tcSvgG, tcXAxis, tcYAxis, tcDataFormat, tcTickFormat, calTickFormat, tcChartData, tcLineChartData, tcCustomColors, tcip;

function drawtcrpb(bh) {
    if (tcCalVal == 'rpbp') {
        tcDataFormat = function(dataVal) {
            return (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit
        }
        tcTickFormat = function(dataVal) {
            return (lrgPaceMult / dataVal).toDayHrMinSec2() + ' ' + lrgPaceUnit
        }
    } else {
        tcDataFormat = function(dataVal) {
            return dataVal.toHrMinSec();
        };
        tcTickFormat = function(dataVal) {
            return dataVal.toHrMinSec();
        };
    }

    tcLineChartData = [];

    for (var i = 0; i < runBests.length; i++) {
        d = runBests[i];
        if (typeof(bh[d.field]) !== 'undefined' && bh[d.field].length > 0) {
            tcLineChartData.push({
                values: bh[d.field].map(function(e) {
                    var val = e[d.field];
                    if (tcCalVal == 'rpbp') {
                        var rba = d.title.split(' ');
                        if (rba.length == 1) {
                            val = 42195 / val;
                        }
                        if (rba[0] == 'Half') {
                            val = 21097.5 / val;
                        }
                        if (rba.length > 1 && rba[1] == 'km') {
                            val = (+rba[0] * 1000) / val;
                        }
                        if (rba.length > 1 && rba[1] == 'mile') {
                            val = (+rba[0] * 1609.34) / val;
                        }
                    }
                    return {
                        x: e.timestamp,
                        y: val,
                        d: e
                    }
                }),
                key: d.title
            });
        }
    }
    tcLineChartData.forEach(function(d) {
        d.values.push({
            x: new Date().getTime(),
            y: d.values[d.values.length - 1].y,
            d: d.values[d.values.length - 1].d
        })
    });

    nv.addGraph(function() {
        if (typeof(tcLineChart) !== 'undefined' && typeof(tcLineChart.tooltip) !== 'undefined') {
            d3.select('#' + tcLineChart.tooltip.id()).remove();
        }
        tcLineChart = nv.models.lineChart()
            .interpolate('step-after')
            .duration(0) //how fast do you want the lines to transition?
            .showLegend(true) //Show the legend, allowing users to turn on/off line series.
            .showYAxis(true) //Show the y-axis
            .showXAxis(true);

        tcLineChart.yAxis //Chart y-axis settings
            .tickFormat(tcTickFormat);

        tcLineChart.lines.xScale(d3.time.scale());
        tcLineChart.xAxis
            .showMaxMin(false)
            .tickFormat(function(d) {
                return fDate(new Date(d))
            });

        tcLineChart.tooltip.contentGenerator(function(data) {
            var h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + fDate(new Date(data.point.x)) + '</strong></div>';

            h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px;font-size:0.8em">' + data.point.d.an + '</div>';
            h += '<table><tbody>';
            h += '<tr><td class="legend-color-guide"><div style="background-color:' + data.point.color + '"></div></td>';
            h += '<td class="key">' + data.series[0].key + '</td>';
            h += '<td class="value">' + tcDataFormat(data.point.y) + '</td>';
            h += '</tbody></table></div>';
            return h;
        });

        d3.selectAll('#tcContainer svg, #tcGroupBG').style('display', 'none');

        var svg = d3.selectAll('#tcContainer svg#tcLineChart');

        if (svg[0].length == 0) {
            svg = d3.selectAll('#tcContainer').append('svg').attr('id', 'tcLineChart').style('height', '300px');
        }

        svg.style('display', null)
            .datum(tcLineChartData)
            .transition().duration(0)
            .call(tcLineChart);

        nv.utils.windowResize(tcLineChart.update);

        d3.selectAll('#tcContainer .nv-point-paths')
            .style('cursor', 'pointer');

        tcLineChart.lines.dispatch.on("elementClick", function(e) {
            window.open('/athletes/' + contextAthleteId + '/' + actWrk + '/' + e.point.d.ai);
        });
        return tcLineChart;
    });
}

function drawtcedd() {
    lfaData = a.filter(function(d) {
        return ((selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1) && (!filterYears || selYear.length == 0 || selYear.indexOf(d.y) > -1))
    });
    lfaData.sort(function(a, b) {
        return a.timestamp - b.timestamp
    });
    var dates = {};
    var dayCounter = 1;
    for (var i = 0; i < lfaData.length; i++) {
        var dd = (typeof(lfaData[i].ls) !== 'undefined' ? lfaData[i].ls : lfaData[i].s).substr(0, 10);
        if (typeof(dates[dd]) === 'undefined') {
            dates[dd] = 0;
        }
        dates[dd] += lfaData[i].d;

        var ev = d3.values(dates);
        ev.sort(d3.descending);
        lfaData[i].eddington = ev.filter(function(d, i) {
            return d * 0.000621371192 >= i + 1
        }).length;
        lfaData[i].eddingtonKm = ev.filter(function(d, i) {
            return d / 1000 >= i + 1
        }).length;
    }

    tcLineChartData = [];

    tcLineChartData.push({
        values: lfaData.filter(function(d, i) {
            return (i == 0 ? true : lfaData[i - 1]['eddington' + (tcCalVal == 'eddingtonKm' ? 'Km' : '')] != d['eddington' + (tcCalVal == 'eddingtonKm' ? 'Km' : '')])
        }).map(function(d) {
            return {
                x: new Date((typeof(d.ls) !== 'undefined' ? d.ls : d.s).substr(0, 10)).getTime(),
                y: d[tcCalVal],
                d: d
            }
        }),
        key: 'Eddington' + (tcCalVal == 'eddingtonKm' ? ' km' : '')
    });

    tcLineChartData[0].values.push({
        x: new Date().getTime(),
        y: tcLineChartData[0].values[tcLineChartData[0].values.length - 1].y
    })

    nv.addGraph(function() {
        if (typeof(tcLineChart) !== 'undefined' && typeof(tcLineChart.tooltip) !== 'undefined') {
            d3.select('#' + tcLineChart.tooltip.id()).remove();
        }
        tcLineChart = nv.models.lineChart()
            .interpolate('step-after')
            .duration(0) //how fast do you want the lines to transition?
            .showLegend(true) //Show the legend, allowing users to turn on/off line series.
            .showYAxis(true) //Show the y-axis
            .showXAxis(true);

        tcLineChart.yAxis //Chart y-axis settings
            .tickFormat(function(d) {
                return fInt(d)
            });

        tcLineChart.lines.xScale(d3.time.scale());
        tcLineChart.xAxis
            .showMaxMin(false)
            .tickFormat(function(d) {
                return fDate(new Date(d))
            });

        tcLineChart.tooltip.contentGenerator(function(data) {
            var h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + fDate(new Date(data.point.x)) + '</strong></div>';

            h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px;font-size:0.8em">' + (typeof(data.point.d) === 'undefined' ? 'n/a' : data.point.d.an) + '</div>';
            h += '<table><tbody>';
            h += '<tr><td class="legend-color-guide"><div style="background-color:' + data.point.color + '"></div></td>';
            h += '<td class="key">' + data.series[0].key + '</td>';
            h += '<td class="value">' + fInt(data.point.y) + '</td>';
            h += '</tbody></table></div>';
            return h;
        });

        d3.selectAll('#tcContainer svg, #tcGroupBG').style('display', 'none');

        var svg = d3.selectAll('#tcContainer svg#tcLineChart');

        if (svg[0].length == 0) {
            svg = d3.selectAll('#tcContainer').append('svg').attr('id', 'tcLineChart').style('height', '300px');
        }

        svg.style('display', null)
            .datum(tcLineChartData)
            .transition().duration(0)
            .call(tcLineChart);

        nv.utils.windowResize(tcLineChart.update);

        d3.selectAll('#tcContainer .nv-point-paths')
            .style('cursor', 'pointer');

        tcLineChart.lines.dispatch.on("elementClick", function(e) {
            if (typeof(e.point.d) !== 'undefined') {
                window.open('/athletes/' + contextAthleteId + '/' + actWrk + '/' + e.point.d.ai);
            }
        });
        return tcLineChart;
    });
}

function drawtc() {
    var lfaData;
    tcCalVal = document.getElementById('timeDisplayValue').value;
    if (hasLocalStorage) {
        localStorage['SummaryTCCalChart'] = tcCalVal;
        localStorage['SummaryTCCalChartGroup'] = tcCalGroup;
    }
    if (document.getElementById('tcMonthGroupByYearCB') == null) {
        d3.select('#timeChart').insert('div', '#tcGroupBG + *')
            .attr('id', 'tcMonthGroupByYearCB').classed('checkbox', true)
            .attr('style', 'display:none;float:left;margin-left:10px;margin-top:4px;')
            .append('label').html('<input type="checkbox"> Group by month');

        d3.select('#tcMonthGroupByYearCB input').on('change', function() {
            drawtc();
        });
    }
    var allowMonthGroup = false;
    if (tcCalGroup == 'month' && displayValues.filter(function(d) {
            return typeof(d.tcOnly) === 'undefined' && typeof(d.calOnly) === 'undefined'
        }).map(function(d) {
            return d.field
        }).indexOf(tcCalVal) > -1) {
        allowMonthGroup = true;
        d3.select('#tcMonthGroupByYearCB').style('display', null);
    } else {
        d3.select('#tcMonthGroupByYearCB').style('display', 'none');
    }
    if (tcCalVal == 'eddington' || tcCalVal == 'eddingtonKm') {
        drawtcedd();
        return;
    }
    var tcip = tcCalVal == 'hz1p' || tcCalVal == 'pz1p' || tcCalVal == 'pcz1p';
    tcCalVal = tcCalVal.replace('1p', '1');
    var lDataObj = setCalData(true, tcCalGroup, tcCalVal, true);
    if (tcCalVal == 'rpb' || tcCalVal == 'rpbp') {
        drawtcrpb(lDataObj);
        return;
    }
    tcData = lDataObj.data;
    tcDataFormat = lDataObj.dataFormat;
    tcTickFormat = lDataObj.tickFormat;
    var init = false;

    var mydata = d3.values(tcData);

    mydata.forEach(function(d) {
        d.date = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
        d.monday = new Date(d.monday.getFullYear(), d.monday.getMonth(), d.monday.getDate());
        d.y = d[tcCalVal];
        switch (tcCalGroup) {
            case 'day':
                d.x = fDate3(d.date);
                break;
            case 'week':
                d.x = fYrWeek(d.monday);
                break;
            case 'month':
                d.x = fYrMonth(d.date);
                break;
            case 'year':
                d.x = fYr(d.date);
                break;
        }
    })

    mydata.sort(function(a, b) {
        return a.date.getTime() - b.date.getTime()
    });

    tcChartData = [];
    tcChartData.push({
        key: displayValues.filter(function(d) {
            return d.field == tcCalVal
        })[0].name,
        values: mydata
    });

    if (tcChartData[0].key == 'Relative Effort') tcChartData[0].key = 'Relative Effort (excluding red)';
    if (tcChartData[0].key == 'Heart Rate Zones') tcChartData[0].key = 'Zone 1';
    if (tcChartData[0].key == 'Power Brackets') tcChartData[0].key = '0W';
    if (tcChartData[0].key == 'Pace Zones') tcChartData[0].key = 'Zone 1';

    tcCustomColors = ['#1F77B4'];

    if (tcCalVal == 'scr') {
        var rd = setCalData(true, tcCalGroup, 'r', true).data;

        for (val in tcData) {
            if (typeof(rd[val]) === 'undefined') {
                rd[val] = clone(tcData[val]);
                rd[val].r = 0;
            }
        }

        var myrdata = d3.values(rd);

        myrdata.forEach(function(d) {
            if (typeof(d.date) !== 'number') {
                d.date = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
                d.monday = new Date(d.monday.getFullYear(), d.monday.getMonth(), d.monday.getDate());
                switch (tcCalGroup) {
                    case 'day':
                        d.x = fDate3(d.date);
                        break;
                    case 'week':
                        d.x = fYrWeek(d.monday);
                        break;
                    case 'month':
                        d.x = fYrMonth(d.date);
                        break;
                    case 'year':
                        d.x = fYr(d.date);
                        break;
                }
            }
            d.y = d.r;
        })

        myrdata.sort(function(a, b) {
            return a.date.getTime() - b.date.getTime()
        });

        tcChartData.unshift({
            key: 'Points in red',
            values: myrdata
        });
        tcCustomColors = ['#FF032E', '#1F77B4'];
    }

    if (tcCalVal.substr(0, 2) == 'hz') {
        var pre = tcCalVal.substr(0, 2) == 'hz' ? 'hz' : 'pcz';
        tcCustomColors = ['#d7191c', '#fdae61', '#ffffbf', '#abd9e9', '#2c7bb6'].reverse();
        for (var i = 2; i < 6; i++) {
            var rd = setCalData(true, tcCalGroup, pre + i, true).data;

            for (val in tcData) {
                if (typeof(rd[val]) === 'undefined') {
                    rd[val] = clone(tcData[val]);
                    rd[val][pre + i] = 0;
                }
            }

            var myzdata = d3.values(rd);

            myzdata.forEach(function(d) {
                if (typeof(d.date) !== 'number') {
                    d.date = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
                    d.monday = new Date(d.monday.getFullYear(), d.monday.getMonth(), d.monday.getDate());
                    d.x = fYrWeek(new Date(d.monday));
                }
                d.y = d[pre + i];
                switch (tcCalGroup) {
                    case 'day':
                        d.x = fDate3(d.date);
                        break;
                    case 'week':
                        d.x = fYrWeek(d.monday);
                        break;
                    case 'month':
                        d.x = fYrMonth(d.date);
                        break;
                    case 'year':
                        d.x = fYr(d.date);
                        break;
                }
            })

            myzdata.sort(function(a, b) {
                return a.date.getTime() - b.date.getTime()
            });

            tcChartData.push({
                key: 'Zone ' + i,
                values: myzdata
            });
        }
    }
    if (tcCalVal.substr(0, 3) == 'pcz') {
        var pre = tcCalVal.substr(0, 2) == 'hz' ? 'hz' : 'pcz';
        tcCustomColors = ['#d73027', '#fc8d59', '#fee090', '#e0f3f8', '#91bfdb', '#4575b4'].reverse();
        for (var i = 2; i < 7; i++) {
            var rd = setCalData(true, tcCalGroup, pre + i, true).data;

            for (val in tcData) {
                if (typeof(rd[val]) === 'undefined') {
                    rd[val] = clone(tcData[val]);
                    rd[val][pre + i] = 0;
                }
            }

            var myzdata = d3.values(rd);

            myzdata.forEach(function(d) {
                if (typeof(d.date) !== 'number') {
                    d.date = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
                    d.monday = new Date(d.monday.getFullYear(), d.monday.getMonth(), d.monday.getDate());
                    d.x = fYrWeek(new Date(d.monday));
                }
                d.y = d[pre + i];
                switch (tcCalGroup) {
                    case 'day':
                        d.x = fDate3(d.date);
                        break;
                    case 'week':
                        d.x = fYrWeek(d.monday);
                        break;
                    case 'month':
                        d.x = fYrMonth(d.date);
                        break;
                    case 'year':
                        d.x = fYr(d.date);
                        break;
                }
            })

            myzdata.sort(function(a, b) {
                return a.date.getTime() - b.date.getTime()
            });

            tcChartData.push({
                key: 'Zone ' + i,
                values: myzdata
            });
        }
    }
    if (tcCalVal.substr(0, 2) == 'pz') {
        tcCustomColors = ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'].reverse();
        for (var i = 2; i < 12; i++) {
            var rd = setCalData(true, tcCalGroup, 'pz' + i, true).data;

            for (val in tcData) {
                if (typeof(rd[val]) === 'undefined') {
                    rd[val] = clone(tcData[val]);
                    rd[val]['pz' + i] = 0;
                }
            }

            var mypzdata = d3.values(rd);

            mypzdata.forEach(function(d) {
                if (typeof(d.date) !== 'number') {
                    d.date = new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
                    d.monday = new Date(d.monday.getFullYear(), d.monday.getMonth(), d.monday.getDate());
                    switch (tcCalGroup) {
                        case 'day':
                            d.x = fDate3(d.date);
                            break;
                        case 'week':
                            d.x = fYrWeek(d.monday);
                            break;
                        case 'month':
                            d.x = fYrMonth(d.date);
                            break;
                        case 'year':
                            d.x = fYr(d.date);
                            break;
                    }
                }
                d.y = d['pz' + i];
            })

            mypzdata.sort(function(a, b) {
                return a.date.getTime() - b.date.getTime()
            });

            tcChartData.push({
                key: powerBrackets[i - 1],
                values: mypzdata
            });
        }
    }
    if (tcip) {
        for (var i = 0; i < tcChartData[0].values.length; i++) {
            var t = d3.sum(tcChartData, function(d) {
                return d.values[i].y
            });
            for (var j = 0; j < tcChartData.length; j++) {
                tcChartData[j].values[i].yold = tcChartData[j].values[i].y;
                tcChartData[j].values[i].y = tcChartData[j].values[i].y / t;
            }
        }
    }
    var tcChartIsMonthGrouped = false;
    if (allowMonthGroup && d3.select('#tcMonthGroupByYearCB input').node().checked) {
        tcChartIsMonthGrouped = true;
        tcCustomColors = vv_category20.slice(0, (selYear.length == 0 ? years : selYear).length).reverse();
        tcChartData = (selYear.length == 0 ? years.map(function(d) {
            return +d
        }).sort() : selYear).map(function(y) {
            return {
                key: y,
                values: tcChartData[0].values.filter(function(d) {
                    return d.date.getFullYear() == y
                }).map(function(d) {
                    var o = clone(d);
                    o.x = o.key.replace(' ' + y, '');
                    o.key = o.key.replace(' ' + y, '')
                    return o;
                })
            };
        });

        tcChartData.forEach(function(d) {
            d3.range(0, 12).map(function(e) {
                return d3.time.format('%B')(new Date(2000, e, 10))
            }).forEach(function(m, i) {
                if (!d.values[i] || d.values[i].key != m) {
                    d.values.splice(i, 0, {
                        d: 0,
                        data: [],
                        date: new Date(d.key, i, 5),
                        key: m,
                        length: 0,
                        x: m,
                        y: 0
                    })
                }
            })
        });
    }
    nv.addGraph(function() {
        if (typeof(tcChart) !== 'undefined' && typeof(tcChart.tooltip) !== 'undefined') {
            d3.select('#' + tcChart.tooltip.id()).remove();
        }
        tcChart = nv.models.multiBarChart()
            .showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
            .groupSpacing(tcChartIsMonthGrouped ? 0.1 : 0) //Distance between each group of bars.
            .reduceXTicks(false)
            .stacked(!tcChartIsMonthGrouped)
            .x(function(d) {
                return d.x
            });

        tcChart.tooltip.contentGenerator(function(data) {
            var l = -1;
            var labels = [];
            for (var i = 0; i < tcChartData.length; i++) {
                if (data.series[0].key == tcChartData[i].key) {
                    l = i;
                }
            }
            var h = '<div><div style="margin:6px 6px 0px 6px"><strong class="x-value">' + data.value + '</strong></div>';

            var a = data.data.data;
            a.sort(function(a, b) {
                return b.d - a.d
            });
            h += '<div style="margin:6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px;font-size:0.8em">' + a[0].an + (a.length > 1 ? ' & ' + (a.length - 1) + ' other' + (a.length > 2 ? 's' : '') : '') + '</div>';
            h += '<table><tbody>';
            if (tcChartData.length > 1) {
                var tot = d3.sum(tcChartData, function(d) {
                    return d.values[data.index][(tcip ? 'yold' : 'y')]
                });
                h += '<tr><td class="legend-color-guide"><div style="background-color:white;display:none"></div></td>';
                h += '<td class="key"><strong>' + (tcCalVal == 'scr' ? 'Relative Effort' : 'Total') + '</strong></td>';
                h += '<td class="value">' + tcDataFormat(tot) + '</td><td></td></tr>';
            }
            for (var i = tcChartData.length - 1; i > -1; i--) {
                h += '<tr' + (l == i && tcChartData.length > 1 ? ' style="background-color:#ddd"' : '') + '><td class="legend-color-guide"><div style="background-color:' + tcCustomColors[i] + '"></div></td>';
                h += '<td class="key">' + (tcChartData[i].key == 'Relative Effort (excluding red)' ? 'Points not in red' : tcChartData[i].key) + '</td>';
                h += '<td class="value">' + tcDataFormat(tcChartData[i].values[data.index][(tcip ? 'yold' : 'y')]) + '</td>';
                h += '<td class="value">' + ((tcChartData.length > 1) ? '(' + fPercent(tcChartData[i].values[data.index][(tcip ? 'yold' : 'y')] / tot) + ')' : '') + '</td></tr>';
            }
            h += '</tbody></table></div>';
            return h;
        });

        tcChart.color(tcCustomColors);

        var xe = d3.extent(tcChartData[0].values.map(function(d) {
            return (tcCalGroup == 'week' ? d.monday : d.date).getTime()
        }));
        switch (tcCalGroup) {
            case 'day':
                var r = d3.range(xe[0] + 2 * 60 * 60 * 1000, xe[1] + 38 * 60 * 60 * 1000, 24 * 60 * 60 * 1000).map(function(d) {
                    return fDate3(new Date(d))
                });
                tcChart.xDomain(r);

                /*var ts = d3.time.scale().domain(r);
                tcChart.xAxis.tickValues(ts.ticks(parseInt(d3.select('#tcContainer').style('width')) / 150).map(function(d) {
                  return fDate3(d.getFullYear(), d.getMonth(), d.getDate())
                }));

                tcChart.xAxis
                  .tickFormat(function(d) {
                    return fDate3(new Date(d))
                  });*/
                tcChart.xAxis.tickValues(r.filter(function(d, i) {
                    return i % (Math.floor(r.length / (parseInt(d3.select('#tcContainer').style('width')) / 150))) == 0
                }));
                break;
            case 'week':
                var r = d3.range(new Date(xe[0]).setDate(new Date(xe[0]).getDate() + 3), new Date(xe[1]).setDate(new Date(xe[1]).getDate() + 4), 7 * 24 * 60 * 60 * 1000).map(function(d) {
                    return fYrWeek(new Date(d))
                });

                tcChart.xDomain(r);

                tcChart.xAxis.tickValues(r.filter(function(d, i) {
                    return i % (Math.floor(r.length / (parseInt(d3.select('#tcContainer').style('width')) / 250))) == 0
                }));
                break;
            case 'month':
                var r = [];
                if (tcChartIsMonthGrouped) {
                    r = d3.range(0, 12).map(function(d) {
                        return d3.time.format('%B')(new Date(2000, d, 10))
                    });
                } else {
                    r = d3.range(new Date(xe[0]).getFullYear() * 12 + new Date(xe[0]).getMonth(), new Date(xe[1]).getFullYear() * 12 + new Date(xe[1]).getMonth() + 1).map(function(d) {
                        return fYrMonth(new Date(Math.floor(d / 12), d % 12, 1))
                    });
                }
                tcChart.xDomain(r);

                if (tcChartIsMonthGrouped) {
                    tcChart.xAxis.tickValues(r);
                } else {
                    tcChart.xAxis.tickValues(r.filter(function(d, i) {
                        return i % (Math.floor(r.length / (parseInt(d3.select('#tcContainer').style('width')) / 150))) == 0
                    }));
                }
                break;
            case 'year':
                var r = d3.range(new Date(xe[0]).getFullYear(), new Date(xe[1]).getFullYear() + 1).map(function(d) {
                    return fYr(new Date(d, 0, 1))
                });
                tcChart.xDomain(r);

                tcChart.xAxis.tickValues(r.filter(function(d, i) {
                    return i % Math.max(1, (Math.floor(r.length / (parseInt(d3.select('#tcContainer').style('width')) / 150)))) == 0
                }));
                break;
        }
        if (tcip) {
            tcChart.yAxis
                .tickFormat(fPercent);
        } else {
            tcChart.yAxis
                .tickFormat(tcTickFormat);
        }
        tcChart.duration(0);

        d3.selectAll('#tcContainer svg').style('display', 'none');

        var svg = d3.selectAll('#tcContainer svg#tcMBChart');

        if (svg[0].length == 0) {
            svg = d3.selectAll('#tcContainer').append('svg').attr('id', 'tcMBChart').style('height', '300px');
        }

        if (tcCalVal == 'ah' || tcCalVal == 'ac' || tcCalVal == 'as' || tcCalVal == 'aw' || tcCalVal == 'waw') {
            var e = d3.extent(d3.merge(tcChartData.map(function(d) {
                return d.values
            })), function(d) {
                return d[tcCalVal]
            });
            e[0] = Math.max(Math.min(0, e[0]), e[0] - ((e[1] - e[0]) / 10));
            tcChart.forceY(e[0]);
        }

        d3.select('#tcGroupBG').style('display', null);
        svg.style('display', null)
            .datum(tcChartData)
            .transition().duration(0)
            .call(tcChart);

        if (d3.selectAll('#tcMBChart .nv-multibar > defs > clipPath').node() != null) {
            d3.selectAll('#tcMBChart .nv-multibar > g').attr('clip-path', 'url(#' + d3.selectAll('#tcMBChart .nv-multibar > defs > clipPath').attr('id') + ')');
        }

        nv.utils.windowResize(function() {
            tcChart.update();
            if (d3.selectAll('#tcMBChart .nv-multibar > defs > clipPath').node() != null) {
                d3.selectAll('#tcMBChart .nv-multibar > g').attr('clip-path', 'url(#' + d3.selectAll('#tcMBChart .nv-multibar > defs > clipPath').attr('id') + ')');
            }
        });

        d3.select('#tcContainer .nv-controlsWrap').style('display', (tcChartData.length > 1 ? 'block' : 'none'));
        d3.selectAll('#tcContainer .nv-bar')
            .style({
                'display': function(d) {
                    return (d.y == 0 ? 'none' : null)
                },
                'cursor': 'pointer'
            });

        tcChart.multibar.dispatch.on("elementClick", function(e) {
            var lData = e.data;
            if (lData.length > 0) {
                if (lData.length == 1) {
                    window.open('/athletes/' + contextAthleteId + '/' + actWrk + '/' + lData.firstId);
                } else {
                    var tDate = new Date(lData.date);
                    var tMon = new Date(lData.monday);
                    var timeSpan = '';
                    switch (tcCalGroup) {
                        case 'day':
                            timeSpan = tDate.getTime() + '|' + (tDate.getTime() + (24 * 60 * 60 * 1000 - 1));
                            break;
                        case 'week':
                            timeSpan = tMon.getTime() + '|' + (tMon.getTime() + (7 * 24 * 60 * 60 * 1000 - 1));
                            break;
                        case 'month':
                            timeSpan = tDate.setDate(1) + '|' + (new Date(tDate.setDate(1)).setMonth(tDate.getMonth() + 1) - 1000);
                            break;
                        default:
                            var fj = new Date(tDate.getFullYear(), 0, 1).getTime();
                            var fj1 = new Date(tDate.getFullYear() + 1, 0, 1).getTime() - 1000;
                            timeSpan = fj + '|' + fj1;
                    }
                    window.open('/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(-1, 1, 43) + ',').replace(/^,/, '') + '0:' + timeSpan);
                }
            }
        });

        d3.selectAll('#tcContainer .nv-legend').on('click', function() {
            d3.selectAll('#tcContainer rect.nv-bar').style('display', function(d) {
                return d.y == 0 ? 'none' : null;
            })
        });

        return tcChart;
    });
}

function drawCal() {
    if (hasLocalStorage) {
        localStorage['SummaryCalChart'] = calVal;
        localStorage['SummaryCalChartGroup'] = calGroup;
    }
    if (calGroup == 'week' || calGroup == 'month' || calGroup == 'year') {
        d3.select('#calRoll')
            .style('display', null);
        calRollRequired = true;
    } else {
        d3.select('#calRoll').style('display', 'none');
        calRollRequired = false;
    }
    if (calGroup == 'month' || calGroup == 'year') {
        d3.select('#calDOW').style('display', null);
    } else {
        d3.select('#calDOW').style('display', 'none');
    }

    if (selYear.length == 0) {
        d3.selectAll('#calContainer svg').style('display', null);
    } else {
        d3.selectAll('#calContainer svg').style('display', 'none');
        d3.selectAll('#calContainer .calKey').style('display', null);
        selYear.forEach(function(d) {
            d3.select('#calContainer #cal' + d).style('display', null);
        });
    }

    var lDataObj = setCalData(false, calGroup, calVal, true, faData, calDOW);
    calData = lDataObj.data;
    calDataFormat = lDataObj.dataFormat;
    calTickFormat = lDataObj.tickFormat;
    var val = calVal;

    calYearDOW.style('display', 'none');
    calMonthDOW.style('display', 'none');
    month.style('display', 'none');
    week.style('display', 'none');
    calYear.style('display', 'none');

    if (calGroup == 'week') {
        week.style('display', null);
    }
    if (calGroup == 'year') {
        if (calDOW) {
            calYearDOW.style('display', null);
        } else {
            calYear.style('display', null);
        }
    } else {
        if (calDOW && calGroup == 'month') {
            calMonthDOW.style('display', null);
        } else {
            month.style('display', null);
        }
    }

    var colDom = d3.scale.linear()
        .range([lDataObj.min, lDataObj.max])
        .domain([0, 4]);
    color.domain([colDom(0), colDom(1), colDom(2), colDom(3), colDom(4)]);

    keyRects.style('fill', function(d) {
        return color(colDom((100 - d) / 25))
    });

    keyMin.text(calTickFormat(lDataObj.min));
    keyMax.text(calTickFormat(lDataObj.max));

    rect.style('cursor', 'default')
        .style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    week.style('cursor', 'default')
        .style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    month.style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    calYear.style('cursor', 'default')
        .style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    calMonthDOW.style('cursor', 'default')
        .style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    calYearDOW.style('cursor', 'default')
        .style('fill', null)
        .on('mouseover', null)
        .on('mouseout', null)
        .on('mousemove', null);

    (calGroup == 'day' ? rect : (calGroup == 'week' ? week : (calGroup == 'month' ? (calDOW ? calMonthDOW : month) : (calDOW ? calYearDOW : calYear)))).filter(function(d) {
            return d in calData;
        })
        .style("fill", function(d) {
            return color(calData[d][val]);
        })
        .style('cursor', 'pointer')
        .on('click', function(d) {
            if (typeof(calData[d]) !== 'undefined' && calData[d].length > 0) {
                if (calData[d].length == 1) {
                    window.open('/athletes/' + contextAthleteId + '/' + actWrk + '/' + calData[d].firstId);
                } else {
                    var tDate = new Date(calData[d].date.getFullYear(), calData[d].date.getMonth(), calData[d].date.getDate());
                    var tMon = new Date(calData[d].monday.getFullYear(), calData[d].monday.getMonth(), calData[d].monday.getDate());
                    var timeSpan = '';
                    switch (calGroup) {
                        case 'day':
                            timeSpan = tDate.getTime() + '|' + (tDate.getTime() + (24 * 60 * 60 * 1000 - 1));
                            break;
                        case 'week':
                            timeSpan = tMon.getTime() + '|' + (tMon.getTime() + (7 * 24 * 60 * 60 * 1000 - 1));
                            break;
                        case 'month':
                            timeSpan = tDate.setDate(1) + '|' + (new Date(tDate.setDate(1)).setMonth(tDate.getMonth() + 1) - 1000);
                            break;
                        default:
                            var fj = new Date(tDate.getFullYear(), 0, 1).getTime();
                            var fj1 = new Date(tDate.getFullYear() + 1, 0, 1).getTime() - 1000;
                            timeSpan = fj + '|' + fj1;
                    }
                    window.open('/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(-1, 1, 43) + ',').replace(/^,/, '') + '0:' + timeSpan + (calDOW ? ',98:' + d3.time.format('%A')(tDate) : ''));
                }
            }
        })
        .on('mouseover', function(d) {
            if (typeof(calTooltip) === 'undefined') {
                calTooltip = d3.select('body').append('div')
                    .classed('nvtooltip xy-tooltip nv-pointer-events-none', true)
                    .attr('id', 'nvtooltip-calTooltip')
                    .attr('style', 'top: 0px; left: 0px; opacity: 0');
            }
            calTooltip.selectAll('*').remove();

            calTooltip.append('div')
                .style('margin', '6px 6px 0px 6px')
                .append('strong')
                .classed('x-value', true)
                .text(calGroup == 'day' ? fDate3(fDate.parse(d)) : d);

            if (typeof(calData[d]) !== 'undefined') {
                calData[d].data.sort(function(a, b) {
                    return b.d - a.d
                });
                var actDesc = calData[d].data[0].an;
                if (calData[d].data.length > 1) {
                    actDesc += ' and ' + (calData[d].data.length - 1) + ' other ' + (calData[d].data.length == 2 ? actWrkSng : actWrk);
                }

                calTooltip.append('div')
                    .attr('style', 'margin: 6px 6px 0px 6px;white-space:normal;font-weight:bold;width:200px;font-size:0.8em')
                    .html(actDesc);

                var cd = years.map(function(e) {
                    var g = d.replace(/ \([^\)]*\)/, '');
                    var yl = datePreference.indexOf('Y') > -1 ? 4 : 2;
                    var calData2 = {};
                    for (key in calData) {
                        calData2[key.replace(/ \([^\)]*\)/, '')] = calData[key]
                    }

                    var f = calData2[datePreference.indexOf('Y') == 1 ? (calGroup == 'day' ? e + g.substr(yl) : g.substr(0, g.length - yl) + e) : g.substr(0, g.length - yl) + e.substr(-yl)];
                    if (typeof(f) !== 'undefined') {
                        //f.key = d.substr(0, d.length - yl) + e.substr(-yl);
                        f.year = e;
                        f.itemValue = f[val];
                    }
                    return f;
                }).filter(function(e) {
                    return typeof(e) !== 'undefined'
                });
                var valSort = cd.map(function(c){ return c.itemValue}).sort(function(a,b){return b - a});
                cd.forEach(function(c){
                    c.rank = valSort.indexOf(c.itemValue) + 1
                });

                var tb = calTooltip.append('table').style('width', '90%').append('tbody')
                    .selectAll('tr')
                    .data(cd)
                    .enter()
                    .append('tr').each(function(e) {
                        var tr = d3.select(this);
                        if (d == e.key) {
                            tr.style('background-color', '#ddd');
                        }
                        tr.append('td')
                            .classed('key', true)
                            .text(e.year);
                        tr.append('td')
                            .classed('value', true)
                            .text(calDataFormat(e[val]));
                        tr.append('td')
                            .classed('value', true)
                            .text(suf(e.rank));
                        if (d == e.key) {
                            tr.append('td')
                                .classed('value', true)
                                .html('&nbsp;');
                            tr.append('td')
                                .classed('value', true)
                                .html('&nbsp;');
                        } else {
                            tr.append('td')
                                .classed('value', true)
                                .text((e[val] - calData[d][val] >= 0 ? '+' : '') + calDataFormat(e[val] - calData[d][val]));
                            tr.append('td')
                                .classed('value', true)
                                .html((calData[d][val] == 0 ? '&nbsp;' : (e[val] - calData[d][val] >= 0 ? '+' : '') + fPercent((e[val] - calData[d][val]) / calData[d][val])));
                        }
                    });
            }
            calTooltip.style('opacity', null);
        })
        .on('mouseout', function() {
            calTooltip.style('opacity', 0)
        })
        .on('mousemove', function() {
            var x = d3.event.pageX + 10;
            var w = parseFloat(calTooltip.style('width'));
            if (x + w > window.innerWidth) {
                x = d3.event.pageX - w - 10;
            }
            var y = d3.event.pageY - parseFloat(calTooltip.style('height')) / 2;
            calTooltip.style('transform', 'translate(' + x + 'px, ' + y + 'px)')
        });
}

function filtersUpdated() {
    if (athleteId == contextAthleteId) {
        setCookie('summarySelYear', selYear.join('###'), 365);
        setCookie('summarySelType', selType.join('###'), 365);
        setCookie('summarySelGear', selGear.join('###'), 365);
    }

    calcStats(false);
    redraw();
    displayYearDistances();
    drawCal();
    drawtc();
    drawGearSummary();
    drawDeviceSummary();
    explorerKMLDisplayLink();

    if (selYear.length == 0 && selType.length == 0 && selGear.length == 0) {
        d3.select('#scores .well svg')
            .style('display', 'block');
        scoreHistoryUpdateLayout();
    } else {
        d3.select('#scores .well svg')
            .style('display', 'none');
    }
}

function drawMain() {
    if (typeof(types) !== 'undefined') {
        return;
    }
    types = d3.keys(d3.nest().key(function(d) {
            return d.t;
        }).map(a))
        .sort();
    years = d3.keys(d3.nest().key(function(d) {
            return d.y;
        }).map(a))
        //.concat('0')
        .sort();
    gears = getGear(a).sort(function(a, b) {
        if (a.type == b.type) {
            return a.name < b.name ? -1 : 1;
        }
        return a.type < b.type ? -1 : 1;
    });

    //years[0] = 'All';

    /*selYear = [];
    selType = [];
    selGear = [];*/

    if (years.length > 4) {
        d3.select('#yearsSelect')
            .append('select')
            .attr('data-placeholder', 'All')
            .attr('multiple', true)
            .selectAll('option')
            .data(years.sort(d3.descending), function(d) {
                return d;
            })
            .enter()
            .append('option')
            .attr('value', function(d) {
                return d;
            })
            .attr('selected', function(d) {
                return selYear.indexOf(+d) > -1 ? 'selected' : null
            })
            .text(function(d) {
                return d;
            });
    } else {
        var bg = d3.select('#yearsSelect')
            .classed('btn-group', true)
            .attr('data-toggle', 'buttons-checkbox');
        allYearBtn = bg.append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary', true)
            .classed('active', function(d) {
                return selYear.length == 0
            })
            .text('All')
            .on('click', function() {
                selYear = [];
                yearBtns.classed('active', false);
                filtersUpdated();
            });
        yearBtns = bg.selectAll('button.items')
            .data(years.sort(d3.descending), function(d) {
                return d;
            })
            .enter()
            .append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary items', true)
            .attr('data-val', function(d) {
                return d;
            })
            .classed('active', function(d) {
                return selYear.indexOf(+d) > -1
            })
            .text(function(d) {
                return d;
            })
            .on('click', function(d) {
                if (d3.select(this).classed('active')) {
                    selYear.splice(selYear.indexOf(+d), 1);
                } else {
                    selYear.push(+d);
                }
                allYearBtn.classed('active', selYear.length == 0);
                filtersUpdated();
            });
    }

    if (types.length > 3) {
        d3.select('#typesSelect')
            .append('select')
            .attr('data-placeholder', 'All')
            .attr('multiple', true)
            .selectAll('option')
            .data(types, function(d) {
                return d;
            })
            .enter()
            .append('option')
            .attr('value', function(d) {
                return d;
            })
            .attr('selected', function(d) {
                return selType.indexOf(d) > -1 ? 'selected' : null
            })
            .text(function(d) {
                return d.replace(/(.)([A-Z])/g, '$1 $2');
            });
    } else {
        var bg = d3.select('#typesSelect')
            .classed('btn-group', true)
            .attr('data-toggle', 'buttons-checkbox');
        allTypeBtn = bg.append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary', true)
            .classed('active', function(d) {
                return selType.length == 0
            })
            .text('All')
            .on('click', function() {
                selType = [];
                typeBtns.classed('active', false);
                filtersUpdated();
            });
        typeBtns = bg.selectAll('button.items')
            .data(types.sort(), function(d) {
                return d;
            })
            .enter()
            .append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary items', true)
            .attr('data-val', function(d) {
                return d;
            })
            .classed('active', function(d) {
                return selType.indexOf(d) > -1
            })
            .text(function(d) {
                return d.replace(/(.)([A-Z])/g, '$1 $2');
            })
            .on('click', function(d) {
                if (d3.select(this).classed('active')) {
                    selType.splice(selType.indexOf(d), 1);
                } else {
                    selType.push(d);
                }
                allTypeBtn.classed('active', selType.length == 0);
                filtersUpdated();
            });
    }

      if (gears.length > 3) {
        d3.select('#gearSelect')
            .append('select')
            .attr('data-placeholder', 'All')
            .attr('multiple', true)
            .selectAll('optgroup')
            .data(gearTypes, function(d) {
                return d;
            })
            .enter()
            .append('optgroup')
            .attr('label', function(d) {
                return d.replaceAll(' (trainer)','')
            })
            .each(function(c) {
                d3.select(this)
                    .selectAll('option')
                    .data(gears.filter(function(d) {
                        return d.type == c.replaceAll(' (trainer)','')
                    }), function(d) {
                        return d.type + ' ' + d.name;
                    })
                    .enter()
                    .append('option')
                    .attr('value', function(d) {
                        return d.name;
                    })
                    .attr('selected', function(d) {
                        return selGear.indexOf(d.name) > -1 ? 'selected' : null
                    })
                    .text(function(d) {
                        return d.name;
                    });
            })
    } else {
        var bg = d3.select('#gearSelect')
            .classed('btn-group', true)
            .attr('data-toggle', 'buttons-checkbox');
        allGearBtn = bg.append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary', true)
            .classed('active', function(d) {
                return selGear.length == 0
            })
            .text('All')
            .on('click', function() {
                selGear = [];
                gearBtns.classed('active', false);
                filtersUpdated();
            });
        gearBtns = bg.selectAll('button.items')
            .data(gears.sort(), function(d) {
                return d.name;
            })
            .enter()
            .append('button')
            .attr('type', 'button')
            .classed('btn btn-mini btn-primary items', true)
            .attr('data-val', function(d) {
                return d.name;
            })
            .classed('active', function(d) {
                return selGear.indexOf(d.name) > -1
            })
            .text(function(d) {
                return d.name;
            })
            .on('click', function(d) {
                if (d3.select(this).classed('active')) {
                    selGear.splice(selGear.indexOf(d.name), 1);
                } else {
                    selGear.push(d.name);
                }
                allGearBtn.classed('active', selGear.length == 0);
                filtersUpdated();
            });
    }

    $('#yearsSelect select, #typesSelect select, #gearSelect select')
        .css('width', '190px')
        .chosen({
            width: '190px'
        })
        .on('change', function(evt, params) {
            var lArr, lSelArr;
            switch (this.parentElement.id) {
                case 'yearsSelect':
                    lArr = years;
                    lSelArr = selYear;
                    break;
                case 'typesSelect':
                    lArr = types;
                    lSelArr = selType;
                    break;
                default:
                    lArr = gears.map(function(d) {
                        return d.name
                    });
                    lSelArr = selGear;
                    selGear = [];
                    break;
            }

            if (typeof(params) !== 'undefined') {
                if (event.altKey && params.selected) {
                    lSelArr = lArr.filter(function(d) {
                        return d != params.selected
                    });
                    $("#" + this.parentElement.id + " select").val(lSelArr).trigger("chosen:updated")
                } else {
                    if (event.altKey && params.deselected) {
                        lSelArr = lArr.filter(function(d) {
                            return d == params.deselected
                        });
                        $("#" + this.parentElement.id + " select").val(lSelArr).trigger("chosen:updated")
                    } else {
                        if (params.selected) {
                            lSelArr.push(params.selected);
                        } else {
                            lSelArr = lSelArr.filter(function(d) {
                                return d != params.deselected
                            });
                        }
                    }
                }
            } else {
                lSelArr = $(evt.target).val();
                if (lSelArr == null) {
                    lSelArr = [];
                }
            }
            switch (this.parentElement.id) {
                case 'yearsSelect':
                    selYear = lSelArr.map(function(d) {
                        return +d
                    });
                    break;
                case 'typesSelect':
                    selType = lSelArr;
                    break;
                default:
                    selGear = lSelArr;
            }

            filtersUpdated();
        });

    if (d3.selectAll('.chosen-container')[0].length == 0) {
        d3.selectAll('#yearsSelect select, #typesSelect select, #gearSelect select').insert("option", ":first-child").attr('disabled', true);
    }

    var aszb = d3.select('#actSummaryZoomBG').attr('data-toggle', 'buttons-radio').html('');
    aszb.append('button').attr('type', 'button').attr('id', 'zoomOff').classed('btn btn-primary btn-mini active', true).html('Full');
    aszb.append('button').attr('type', 'button').attr('id', 'zoomOn').classed('btn btn-primary btn-mini', true).html('Zoom');
    aszb.append('button').attr('type', 'button').attr('id', 'zoom30').classed('btn btn-primary btn-mini', true).html('30 Day');

    d3.selectAll('#actSummaryZoomBG button')
        .classed('active', function(d, i) {
            return i == zoomYearChart;
        })
        .on('click', function(d, i) {
            zoomYearChart = i; //!d3.select(this).classed('active');
            displayYearDistances();
        });

    d3.selectAll('#actSummaryYBG')
        .on('change', function(d) {
            viewBy = document.getElementById('actSummaryYBG').value;
            if (viewBy == 'allTimeExplorer') {
                d3.select('#actSummaryZoomBG').style('display', 'none');
                d3.selectAll('#actSummaryZoomBG button').classed('active', null);
                d3.select('#zoomOff').classed('active', true);
                zoomYearChart = 0;
            } else {
                d3.select('#actSummaryZoomBG').style('display', null);
            }
            var targetType = 'd';
            var targetMult = 1;
            switch (viewBy) {
                case 'yrCumDist':
                    targetMult = lrgLenMult;
                    d3.select('#targetLabel').text('Annual distance target:');
                    //document.getElementById('targetType').value = 'd';
                    d3.select('#targetUnit').text(lrgLenUnit);
                    d3.select('#targetForm').style('display', 'block');
                    break;
                case 'yrCumElev':
                    targetType = 'e';
                    targetMult = smlLenMult;
                    d3.select('#targetLabel').text('Annual elevation target:');
                    //document.getElementById('targetType').value = 'e';
                    d3.select('#targetUnit').text(smlLenUnit);
                    d3.select('#targetForm').style('display', 'block');
                    break;
                default:
                    d3.select('#targetForm').style('display', 'none');
            }
            displayYearDistances();
        })
        .selectAll('option')
        .data([{
            n: 'Distance',
            v: 'yrCumDist',
            d: 'd'
        }, {
            n: 'Elevation',
            v: 'yrCumElev',
            d: 'eg'
        }, {
            n: 'Time (moving)',
            v: 'yrCumTime',
            d: 'mt'
        }, {
            n: 'Time (elapsed)',
            v: 'yrCumTimeElapsed',
            d: 'et'
        }, {
            n: 'Relative Effort',
            v: 'yrCumSuffer',
            d: 'sc'
        }, {
            n: 'Points in red',
            v: 'yrCumRed',
            d: 'r'
        }, {
            n: 'Training Load',
            v: 'yrCumTL',
            d: 'tl'
        }, {
            n: 'Distance + Elevation (m) / 100',
            v: 'yrCumDeq',
            d: 'deq'
        }, {
            n: 'Kudos',
            v: 'yrCumKC',
            d: 'tl'
        }, {
            n: 'Calories',
            v: 'yrCumCal',
            d: 'ca'
        }, {
            n: 'PRs',
            v: 'yrCumPrs',
            d: 'prs'
        }, {
            n: 'PRs (tries > 1)',
            v: 'yrCumPrs2',
            d: 'prs2'
        }, {
            n: 'Activity count',
            v: 'yrCumCount',
            d: 'prs2'
        }, {
            n: 'Day count',
            v: 'yrCumCount1',
            d: 'prs2'
        }, {
            n: 'Explorer (new tiles by year)',
            v: 'yrExplorer'
        }, {
            n: 'Explorer (new tiles all time)',
            v: 'allTimeExplorer'
        }, {
            n: 'Explorer (new tiles all time by year)',
            v: 'allTimeByYearExplorer'
        }, {
            n: 'Explorer (all tiles)',
            v: 'yrExplorerAll'
        }, {
            n: 'Max Square',
            v: 'maxsquare'
        }, {
            n: 'Max Cluster',
            v: 'maxcluster'
        }, {
            n: 'Eddington',
            v: 'yrEddington'
        }, {
            n: 'Eddington (km)',
            v: 'yrEddingtonKm'
        }])
        .enter().append('option')
        .attr('value', function(d) {
            return d.v
        })
        .attr('selected', function(d) {
            return d.v == viewBy ? 'selected' : null
        })
        .text(function(d) {
            return d.n
        });

    if (viewBy == 'allTimeExplorer') {
        d3.select('#actSummaryZoomBG').style('display', 'none');
        d3.selectAll('#actSummaryZoomBG button').classed('active', null);
        d3.select('#zoomOff').classed('active', true);
        zoomYearChart = 0;
    }

    d3.selectAll('#calBG button')
        .on('click', function(d) {
            calVal = d3.select(this).attr('data-val');
            drawCal();
        });
    d3.selectAll('#calGroupBG button').remove();
    d3.select('#calGroupBG')
        .append('button')
        .attr('type', 'button')
        .attr('data-val', 'day')
        .classed('btn btn-primary btn-mini', true)
        .classed('active', calGroup == 'day')
        .text('Day');
    d3.select('#calGroupBG')
        .append('button')
        .attr('type', 'button')
        .attr('data-val', 'week')
        .classed('btn btn-primary btn-mini', true)
        .classed('active', calGroup == 'week')
        .text('Week');
    d3.select('#calGroupBG')
        .append('button')
        .attr('type', 'button')
        .attr('data-val', 'month')
        .classed('btn btn-primary btn-mini', true)
        .classed('active', calGroup == 'month')
        .text('Month');
    d3.select('#calGroupBG')
        .append('button')
        .attr('type', 'button')
        .attr('data-val', 'year')
        .classed('btn btn-primary btn-mini', true)
        .classed('active', calGroup == 'year')
        .text('Year');
    d3.selectAll('#calGroupBG button')
        .on('click', function(d) {
            calGroup = d3.select(this).attr('data-val');
            drawCal();
        });
    d3.selectAll('#tcGroupBG button')
        .on('click', function(d) {
            tcCalGroup = d3.select(this).attr('data-val');
            //d3.selectAll('#tcContainer .nv-bar').remove();
            drawtc();
        });

    d3.selectAll("#tcGroupBG button[data-val='" + tcCalGroup + "']").classed('active', true);

    var goalRow;

    if (types.indexOf('Run') > -1 && d3.selectAll('.runBestsH2')[0].length == 0) {
        d3.select('#segStatsContainer')
        .insert('div', ':first-child')
        .attr('id', 'runBests')
        .classed('span4', true)
        .append('div')
        .classed('well', true)
        .each(function() {
            var item = d3.select(this);
            item.append('h2')
                .classed('runBestsH2', true)
                .text('Run Bests');
            item.append('dl')
                .classed('dl-horizontal', true)
                .each(function() {
                    var dl = d3.select(this);
                    runBests.forEach(function(d) {
                        dl.append('dt')
                            .classed('runBest' + d.field, true)
                            .text(d.title + ':');
                        dl.append('dd')
                            .classed('nowrap runBest' + d.field, true)
                            .attr('id', 'runBestDD' + d.field);
                    });
                });
            });
    } else {
        d3.selectAll('#segStats')
            .classed('span4', false)
            .classed('span5', true);
        d3.selectAll('#tableWrapper')
            .classed('span4', false)
            .classed('span7', true);
    }

    d3.select('#rightWell')
        .append('div')
        .attr('id', 'expBests')
        .classed('well', true)
        .append('h2')
        .classed('runBestsH2', true)
        .html('Exploration <a target="_blank" href="https://blog.veloviewer.com/veloviewer-explorer-overview/"><span style="font-size: 0.6em" class="label label-info">?</span></a>');

    let dl = d3.select('#expBests')
        .append('dl')
        .attr('id', 'expBestsList')
        .classed('dl-horizontal', true);

    if (contextAthleteId == loggedInAthleteId) {
        d3.select('#expBests')
            .append('div')
            .html('Strava route builder code: <button class="js-copy-vvext-btn btn btn-mini btn-primary">Copy code</button> <button class="js-refresh-vvext-btn btn btn-mini btn-primary"><i class="icon-refresh icon-white"></i></button>');

        document.querySelector('.js-copy-vvext-btn').addEventListener('click', function(event) {
            d3.select('.js-copy-vvext-btn').html('Copied');
            copyTextToClipboard(vvext);
            window.setTimeout(function(){
                d3.select('.js-copy-vvext-btn').html('Copy code');
            }, 2000);
        });
        document.querySelector('.js-refresh-vvext-btn').addEventListener('click', function(event) {
            if (confirm("This will invalidate your previous Strava route builder code and provide a new one via the \"Copy code\" button. You will need to copy/paste the new code into the Strava Route Builder. Proceed?")) {
                $.ajax({
                    url: '/api/resetExternalCode.php',
                    success: function(a) {
                        if (typeof(a.vvext) !== 'undefined') {
                            vvext = a.vvext;
                        } else {
                            console.info(a);
                        }
                    }
                });
            }
        });
    }

    goalRow = d3.selectAll('#mainContent')
        .insert('div', '.row-fluid:nth-child(3)')
        .classed('row-fluid awards', true);

    var dGoalDiv = goalRow.append('div')
        .classed('span4', true)
        .append('div')
        .classed('well', true);

    dGoalDiv.append('h2')
        .text('Distance Awards');

    dGoalDiv.append('div')
        .attr('id', 'distAwardsContainer');

    var eGoalDiv = goalRow.append('div')
        .classed('span4', true)
        .append('div')
        .classed('well', true);

    eGoalDiv.append('h2')
        .text('Elevation Awards');

    eGoalDiv.append('div')
        .attr('id', 'elevAwardsContainer');

    var tGoalDiv = goalRow.append('div')
        .classed('span4', true)
        .append('div')
        .classed('well', true);

    tGoalDiv.append('h2')
        .text('Time Awards');

    tGoalDiv.append('div')
        .attr('id', 'timeAwardsContainer');

    a.sort(function(a, b) {
        return (a.date > b.date ? -1 : 1)
    });

    var recent = d3.selectAll('#mainContent')
        .append('div')
        .classed('row-fluid', true)
        .append('div')
        .classed('span12', true)
        .append('div')
        .classed('recent', true);
    recent.append('h2').text('Recent Activities');
    var recTab = recent.append('div').classed('tableWrapper', true)
        .append('table').classed('table table-striped table-condensed table-hover table-bordered', true);
    var recTHead = recTab.append('thead').append('tr');
    recTHead.append('th').text('Name');
    recTHead.append('th').text('When');
    recTHead.append('th').text('Dist');
    recTHead.append('th').text('Elev');
    recTHead.append('th').text('Time');
    recTHead.append('th').text('Type');
    recTHead.append('th').text('Gear');
    recTHead.append('th').html('&nbsp;');
    recTab.append('tbody');

    data = {};

    calcStats(false);

    redraw();

    if (!updatingStats) {
        d3.select('#mainContent')
            .style('display', 'block');
        d3.select('#loadingMsg')
            .style('display', 'none');
    }
}

function regionStats(entries){
    return;
  let e = entries.sort(function(a, b){
      return (b.visitedCount / b.tileCount) - (a.visitedCount / a.tileCount);
  }).slice(0, 3);

  let explorerDT = document.createElement('dt');
  explorerDT.title = "Regional bests are the areas you have explored the most. The percentage shown represents the amount of tiles you have ticked out of the number of tiles available in that region.";
  explorerDT.style = 'width: 93px';
  explorerDT.innerText = 'Regional Bests:';

  let explorerDD = document.createElement('dd');
  let textnode = document.createTextNode('');
  explorerDD.id = 'expRegionBests';
  explorerDD.style = 'margin-left: 99px';
  explorerDD.appendChild(textnode);


  let dl = document.getElementById('expBestsList');
  dl.appendChild(explorerDT);
  dl.appendChild(explorerDD);

  let dd = d3.select('#expRegionBests')
    .selectAll('div')
    .data(e)
    .enter()
    .append('div');

  dd.append('div')
    .attr('style', 'float: left; clear: left')
    .html(function(d) { return '<a href="/explorerRegions?r='+ d.countryCode + '">'+ d.name + '</a>&nbsp;'; });

  dd.append('div')
    .attr('style', 'float: right; clear: right')
    .html(function(d) { return ((d.visitedCount / d.tileCount) * 100).toFixed(2) + ' %'; });

  dd.node().childNodes[0].setAttribute('style', 'float: left');
}

function segDet(seg, val) {
    return (typeof(seg) === 'undefined' ? null : seg.i + '|' + seg[val] + '|' + seg.r + '|' + seg.tac + '|' + seg.sn);
}

function actDet(act, val) {
    if (Object.prototype.toString.call(act) === '[object Array]') {
        return act.map(function(d) {
            return (typeof(d) === 'undefined' ? null : d.i + '|' + (val != null ? d[val] : '') + '|' + d.an)
        });
    } else {
        return (typeof(act) === 'undefined' ? null : act.i + '|' + (val != null ? act[val] : '') + '|' + act.an);
    }
}

function calcStatsPrep() {
    seg = seg.filter(function(d) {
        return typeof(d.r) !== 'undefined' && d.r != null
    });
    seg.forEach(function(d) {
        d.y = new Date(d.sd).getFullYear();
        d.s = d.r == 0 ? 0 : (d.tac + 1 - d.r) * (100 / (d.tac + 1));
        d.ps = (d.tac + 1 - 1) * (100 / (d.tac + 1));
    });
    a.forEach(function(d) {
        d.y = d.date.getFullYear();
    });

    s = {
        athleteId: contextAthleteId,
        scores: [],
        summaries: []
    };
}

function saveExplorerKML() {
    var maxClumpArr = d3.keys(maxClump).map(function(d) {
        return d.split('-').map(function(e) {
            return +e
        })
    });
    var maxClumpArrXExt = d3.extent(maxClumpArr, function(d) {
        return d[0]
    });
    var maxClumpArrYExt = d3.extent(maxClumpArr, function(d) {
        return d[1]
    });

    var kmlPadding = hasLocalStorage && localStorage.getItem('kmlExplorerPadding') != null ? +localStorage.getItem('kmlExplorerPadding') : 10;

    if (getParameterByName('kmlPadding') != '') {
        kmlPadding = +getParameterByName('kmlPadding');
        if (hasLocalStorage) localStorage.setItem('kmlExplorerPadding', kmlPadding);
    }

    var explorerKML = [];
    for (var i = maxClumpArrYExt[0] - kmlPadding; i <= maxClumpArrYExt[1] + kmlPadding; i++) {
        for (var j = maxClumpArrXExt[0] - kmlPadding; j <= maxClumpArrXExt[1] + kmlPadding; j++) {
            if (!explorerTiles[j + '-' + i]) {
                explorerKML.push([j, i]);
            }
        }
    }
    explorerKML = explorerKML.filter(function(d) {
        for (i = d[0] - kmlPadding - 1; i <= d[0] + kmlPadding - 1; i++) {
            if (maxClump[i + '-' + (d[1] - kmlPadding)] || maxClump[i + '-' + (d[1] + kmlPadding)]) {
                return true;
            }
        }
        for (j = d[1] - kmlPadding - 1; j <= d[1] + kmlPadding - 1; j++) {
            if (maxClump[(d[0] - kmlPadding) + '-' + j] || maxClump[(d[0] + kmlPadding) + '-' + j]) {
                return true;
            }
        }
        if (maxClumpArr[0][0] > d[0] - kmlPadding && maxClumpArr[0][0] < d[0] + kmlPadding && maxClumpArr[0][1] > d[1] - kmlPadding && maxClumpArr[0][1] < d[1] + kmlPadding) {
            return true;
        }
        return false;
    });
    $.ajax({
        type: "POST",
        url: '/api/saveExplorerKML.php',
        data: {
            t: compress(explorerKML, 0, 2)
        },
        success: function(a) {
            if (a.status && a.status == 'Success') {
                explorerKMLSrc = a.filename;
                explorerKMLDisplayLink();
            }
        }
    });
}

function explorerKMLDisplayLink() {
    if (typeof(explorerKMLSrc) !== 'undefined' && explorerKMLSrc != '' && selYear.length == 0 && selType.length == 0 && selGear.length == 0) {
        d3.select('#explorerKMLLink').html('<a target="_blank" title="Download a KML file of unexplored tiles around your current Max Cluster (10 square padding)" href="' + explorerKMLSrc + '">KML</a>');
    } else {
        d3.select('#explorerKMLLink').html('');
    }
}

function calcStats(justScores) {
    //var y = (selYear == 'All' ? '0' : selYear);
    //var t = selType;

    if (typeof(s.summaries) === 'undefined') {
        s.summaries = [];
    }

    if (typeof(s.scores) === 'undefined') {
        s.scores = [];
    }

    if (s.summaries.filter(function(d) {
            return selType.join() == d.type.join() &&
                selYear.join() == d.year.join() &&
                selGear.join() == d.gear.join() &&
                d.justScores == justScores
        }).length == 0) {
        /*var ig = false;
        if (t.indexOf('#GEAR#') == 0) {
          ig = true;
        }*/
        var sc = {
            type: clone(selType),
            year: clone(selYear),
            gear: clone(selGear)
        };
        var su = {
            type: clone(selType),
            year: clone(selYear),
            gear: clone(selGear),
            justScores: justScores
        };
        //t = t.replace('#GEAR#', '');
        var fAct = a.filter(function(d) {
            return (d.f == 0 || (d.f == 1 && d.p == 1)) &&
                (selYear.length == 0 || selYear.indexOf(d.y) > -1) &&
                (selType.length == 0 || selType.indexOf(d.t) > -1) &&
                (selGear.length == 0 || selGear.indexOf(d.g) > -1) &&
                (!justScores || ((d.t != 'Ride' || d.as < 19.444) &&
                (d.t != 'Run' || d.as < 10) &&
                d.t != 'Workout' &&
                d.t != 'Crossfit' && // hidden for athlete 111864
                ((d.map != null && d.map != '') || typeof(texplorer[d.i]) !== 'undefined'))) // hiding manual from leaderboards
            //return d.f == 0 && (y == '0' || y == d.y) && (t == 'All' || (!ig && t == d.t) || (ig && t == d.g)) && (incTrainer || d.tr == 0 || d.t == 'Swim')
        });
        var fSeg = seg.filter(function(d) {
            //d.f != 2 && d.r > 0 &&
            return (selYear.length == 0 || selYear.indexOf(d.y) > -1) &&
                (selType.length == 0 || selType.indexOf(d.t) > -1) &&
                (selGear.length == 0 || selGear.indexOf(d.gn) > -1)
            //return (y == '0' || y == d.y) && (t == 'All' || (!ig && t == d.t) || (ig && t == d.gn))
        });
        var npSeg = fSeg.filter(function(d) {
            return d.tac > 1 && d.r > 0
        });
        var sfSeg = npSeg.filter(function(d) {
            return d.gr >= -0.25
        });
        sc.segTotal = sfSeg.length;
        sfSeg.sort(function(a, b) {
            return (a.r == b.r ? (b.tac == a.tac ? b.i - a.i : b.tac - a.tac) : a.r - b.r)
        });
        sfSeg.sort(function(a, b) {
            return (b.s == a.s ? b.i - a.i : b.s - a.s)
        });
        var sfSeg = sfSeg.slice(0, (sfSeg.length > 400 ? 100 : Math.max(4, Math.round(sfSeg.length / 4))));
        var rfSeg = npSeg.slice(0, Math.max(4, Math.round(npSeg.length / 4)));;
        sc.avgTotal = d3.mean(rfSeg.map(function(d) {
            return d.tac
        }));
        rfSeg = rfSeg.filter(function(d) { return d.r > 0 });
        sc.avgPos = d3.mean(rfSeg.map(function(d) {
            return d.r
        }));
        sc.avgPosMedian = d3.median(rfSeg.map(function(d) {
            return d.r
        }));
        var pc = {};
        rfSeg.forEach(function(d) {
            if (!pc[d.r]) pc[d.r] = { i: d.r, c: 0 };
            pc[d.r].c++;
        });
        var pcm = d3.values(pc).sort(function(a, b) { return b.c - a.c });
        sc.avgPosMode = pcm.length > 0 ? pcm[0].i : 0;
        sc.possibleScore = d3.mean(sfSeg.map(function(d) {
            return d.ps
        }));
        sc.minScore = d3.min(sfSeg.map(function(d) {
            return d.s
        }));
        sc.score = d3.mean(sfSeg.map(function(d) {
            return d.s
        }));
        sc.segFrom = sfSeg.length;
        ['&nbsp;HC', '1st', '2nd', '3rd', '4th'].forEach(function(c) {
            var c1 = (c == '&nbsp;HC' ? 'Hc' : parseInt(c));
            cfSeg = fSeg.filter(function(d) {
                return d.cc.replace(' ', '&nbsp;') == c
            });
            su['cat' + c1 + 'AvgRp'] = d3.mean(cfSeg.filter(function(d) {
                return d.rp > 0
            }).map(function(d) {
                return d.rp
            }));
            su['cat' + c1 + 'AvgVam'] = d3.mean(cfSeg.filter(function(d) {
                return d.v > 0
            }).map(function(d) {
                return d.v
            }));
            su['cat' + c1 + 'Count'] = cfSeg.length;
            su['cat' + c1 + 'MaxRp'] = segDet(cfSeg.sort(function(a, b) {
                return b.rp - a.rp
            })[0], 'rp');
            su['cat' + c1 + 'MaxVam'] = segDet(cfSeg.sort(function(a, b) {
                return b.v - a.v
            })[0], 'v');
            su['cat' + c1 + 'TopScore'] = segDet(cfSeg.sort(function(a, b) {
                return b.s - a.s
            })[0], 's');
            su['cat' + c1 + 'TopPlacing'] = segDet(cfSeg.sort(function(a, b) {
                return (a.r == b.r ? b.tac - a.tac : a.r - b.r)
            })[0], 'r');
        });

        su.topScore = segDet(fSeg.sort(function(a, b) {
            return b.s - a.s
        })[0], 's');
        su.totalSegments = fSeg.length;
        var fEfforts = d3.merge(fAct.map(function(d) {
            return teffs[d.i]
        }).filter(function(d) {
            return typeof(d) !== 'undefined'
        })).filter(function(d) {
            return typeof(tsegs[d.si]) !== 'undefined' && typeof(tsegs[d.si].f) === 'undefined'
        });
        su.totalEfforts = fEfforts.length;
        su.totalUniqueSegents = d3.values(d3.nest()
            .key(function(d) {
                return d.si
            })
            .map(fEfforts)).length;
        su.rank1 = npSeg.filter(function(d) {
            return d.r == 1
        }).length;
        su.rank2 = npSeg.filter(function(d) {
            return d.r == 2
        }).length;
        su.rank3 = npSeg.filter(function(d) {
            return d.r == 3
        }).length;
        su.rankTop5 = npSeg.filter(function(d) {
            return d.r <= 5 && d.r > 0
        }).length;
        su.rankTop10 = npSeg.filter(function(d) {
            return d.r <= 10 && d.r > 0
        }).length;
        su.rankTop25 = npSeg.filter(function(d) {
            return d.r <= 25 && d.r > 0
        }).length;
        su.rankTop50 = npSeg.filter(function(d) {
            return d.r <= 50 && d.r > 0
        }).length;
        su.rankTop100 = npSeg.filter(function(d) {
            return d.r <= 100 && d.r > 0
        }).length;
        su.totalSegsMultiTries = fSeg.filter(function(d) {
            return d.tr > 1
        }).length; //fsSum.length;

        su.recent = fAct.sort(function(a, b) {
            return (a.date > b.date ? -1 : 1)
        }).slice(0, 10);
        su.maxDist = actDet(fAct.sort(function(a, b) {
            return b.d - a.d
        }).slice(0, 5), 'd');
        su.maxElev = actDet(fAct.filter(function(d) { return ['AlpineSki','Snowboard'].indexOf(d.t) == -1 }).sort(function(a, b) {
            return b.eg - a.eg
        }).slice(0, 5), 'eg');
        su.maxTime = actDet(fAct.sort(function(a, b) {
            return b.mt - a.mt
        }).slice(0, 5), 'mt');
        su.totalActivities = fAct.length;
        su.totalDist = d3.sum(fAct.map(function(d) {
            return d.d
        }));
        su.totalDist2 = d3.sum(fAct.filter(function(d) {
            return d.m == 0 && d.trn == 0 && d.map && d.t != 'VirtualRide' && d.t != 'VirtualRun'
        }).map(function(d) {
            return d.d
        }));
        su.totalElev = d3.sum(fAct.filter(function(d) { return ['AlpineSki','Snowboard'].indexOf(d.t) == -1 }).map(function(d) {
            return d.eg
        }));
        su.totalTime = d3.sum(fAct.map(function(d) {
            return d.mt
        }));
        su.totalCal = d3.sum(fAct.map(function(d) {
            return d.ca
        }));
        var dayDists = d3.values(d3.nest().key(function(d) {
                return d.y + '-' + d.date.dayOfYear()
            })
            .rollup(function(d) {
                return {
                    d: d3.sum(d, function(e) {
                        return e.d
                    }),
                    date: d[0].date
                }
            })
            .map(fAct)).sort(function(a, b) {
            return b.d - a.d
        });
        su.eddingtonMl = dayDists.filter(function(d, i) {
            return d.d * 0.000621371192 >= i + 1
        }).length;
        su.eddingtonMl1 = su.eddingtonMl + 1 - dayDists.filter(function(d, i) {
            return d.d * 0.000621371192 >= su.eddingtonMl + 1
        }).length;
        su.eddingtonMl2 = su.eddingtonMl + 2 - dayDists.filter(function(d, i) {
            return d.d * 0.000621371192 >= su.eddingtonMl + 2
        }).length;
        su.eddingtonKm = dayDists.filter(function(d, i) {
            return d.d * 0.001 >= i + 1
        }).length;
        su.eddingtonKm1 = su.eddingtonKm + 1 - dayDists.filter(function(d, i) {
            return d.d * 0.001 >= su.eddingtonKm + 1
        }).length;
        su.eddingtonKm2 = su.eddingtonKm + 2 - dayDists.filter(function(d, i) {
            return d.d * 0.001 >= su.eddingtonKm + 2
        }).length;
        var dayTimes = d3.values(d3.nest().key(function(d) {
                return d.y + '-' + d.date.dayOfYear()
            })
            .rollup(function(d) {
                return d3.sum(d, function(e) {
                    return e.mt
                })
            })
            .map(fAct)).sort(d3.descending);
        su.eddingtonTime = dayTimes.filter(function(d, i) {
            return d / 60 >= i + 1
        }).length;
        su.eddingtonTime1 = su.eddingtonTime + 1 - dayTimes.filter(function(d, i) {
            return d / 60 >= su.eddingtonTime + 1
        }).length;
        su.eddingtonTime2 = su.eddingtonTime + 2 - dayTimes.filter(function(d, i) {
            return d / 60 >= su.eddingtonTime + 2
        }).length;
        var dayClimbs = d3.values(d3.nest().key(function(d) {
                return d.y + '-' + d.date.dayOfYear()
            })
            .rollup(function(d) {
                return d3.sum(d, function(e) {
                    return e.eg
                })
            })
            .map(fAct)).sort(d3.descending);

        su.eddingtonClimb = dayClimbs.filter(function(d, i) {
            return d / edClimbMult >= i + 1
        }).length;
        su.eddingtonClimb1 = su.eddingtonClimb + 1 - dayClimbs.filter(function(d, i) {
            return d / edClimbMult >= su.eddingtonClimb + 1
        }).length;
        su.eddingtonClimb2 = su.eddingtonClimb + 2 - dayClimbs.filter(function(d, i) {
            return d / edClimbMult >= su.eddingtonClimb + 2
        }).length;
        su.centuryCount = fAct.filter(function(d) { return d.d >= 160934.4 }).length;
        su.kIndex = fAct.sort(function(a, b) {
            return b.kc - a.kc
        }).filter(function(d, i) {
            return d.kc > i
        }).length;
        su.kCount = d3.sum(fAct.map(function(d) {
            return d.kc
        }));

        su.dGoal = getGoals('d', dGoals, fAct, function(dataVal) {
            return d3.format(',')((dataVal * lrgLenMult).round(2)) + ' ' + lrgLenUnit
        }, function(dataVal) {
            return f2dp(dataVal * lrgLenMult) + ' ' + lrgLenUnit
        }).reverse();
        su.eGoal = getGoals('eg', eGoals, fAct, function(dataVal) {
            return d3.format(',')((dataVal * smlLenMult).round(0)) + ' ' + smlLenUnit
        }, function(dataVal) {
            return fInt(dataVal * smlLenMult) + ' ' + smlLenUnit
        }).reverse();
        su.tGoal = getGoals('mt', tGoals, fAct, function(dataVal) {
            return (dataVal % 900 == 0 ? (dataVal < 3600 ? (dataVal / 60) + ' mins' : (dataVal / 3600) + ' hour' + (dataVal == 3600 ? '' : 's')) : dataVal.toHrMinSec())
        }).reverse();

        su.hzt = 0;
        for (var i = 1; i <= 5; i++) {
            su['hz' + i] = d3.sum(a, function(d) {
                return d['hz' + i]
            });
            su.hzt += su['hz' + i];
        }
        su.pzt = 0;
        for (var i = 1; i <= 11; i++) {
            su['pz' + i] = d3.sum(a, function(d) {
                return d['pz' + i]
            });
            su.pzt += su['pz' + i];
        }
        su.pczt = 0;
        for (var i = 1; i <= 6; i++) {
            su['pcz' + i] = d3.sum(a, function(d) {
                return d['pcz' + i]
            });
            su.pczt += su['pcz' + i];
        }

        /*su.dEdd = 0;
        fAct.sort(function(a, b) {
          return b.d - a.d
        });
        for (var i = 0; i < fAct.length; i++) {
          if (i + 1 >= fAct[i].d * lrgLenMult) {
            su.dEdd = i;
            break;
          }
        }*/

        su.runBests = [];
        runBests.forEach(function(e, i) {
            su.runBests[i] = actDet(fAct.filter(function(d) {
                    return d.t == 'Run' && d[e.field] != null
                })
                .sort(function(a, b) {
                    return a[e.field] - b[e.field]
                })[0], e.field);
        });

        /*su.pftp = d3.merge(cacheAct.filter(function(d) {
            return d.hp == 1
          }).map(function(d) {
            return d.ai
          }).map(function(d) {
            return teffs[d].filter(function(e) {
              return e.et > 60 * 20 && e.aw > 0
            })
          })).map(function(d) {
            d.pftp = d.aw * Math.min(1, (0.05 / 4) * ((d.et / 60) - 20) / 10 + 0.95);
            return d
          })
          .sort(function(a, b) {
            return b.pftp - a.pftp
          });
        if (su.pftp.length > 0) {
          su.pftp = su.pftp[0];
        } else {
          su.pftp = null;
        }
        su.hrftp = d3.merge(cacheAct.filter(function(d) {
            return typeof(d.ah) !== 'undefined'
          }).map(function(d) {
            return d.ai
          }).map(function(d) {
            return teffs[d].filter(function(e) {
              return e.et > 60 * 20 && e.ah > 0
            })
          })).map(function(d) {
            d.hrftp = d.ah * Math.min(1, (0.05 / 4) * ((d.et / 60) - 20) / 10 + 0.95);
            return d
          })
          .sort(function(a, b) {
            return b.hrftp - a.hrftp
          });
        if (su.hrftp.length > 0) {
          su.hrftp = su.hrftp[0];
        } else {
          su.hrftp = null;
        }*/

        var tiles = setExplorer(su, fAct, true);
        if (typeof(actsPSrc) !== 'undefined' && selType.length == 0 && selGear.length == 0 && selYear.length == 0) {
            var actsSrcTS = actsSrc.match(/\.([0-9]*)\.js$/);
            var actsPSrcTS = actsPSrc.match(/\.([0-9]*)\.js$/);
            var explorerTilesSrcTS = explorerTilesSrc.match(/\.([0-9]*)\.js$/);
            var explorerSrcTS = explorerSrc.match(/\.([0-9]*)\.js$/);
            if (actsSrcTS != null && actsPSrcTS != null && (explorerTilesSrcTS == null || (explorerSrcTS != null && explorerTilesSrcTS != null && +explorerSrcTS[1] > +explorerTilesSrcTS[1]) || +actsSrcTS[1] > +explorerTilesSrcTS[1] || +actsPSrcTS[1] > +explorerTilesSrcTS[1])) {
                // save new
                $.ajax({
                    type: "POST",
                    url: '/api/saveExplorerTiles.php',
                    data: {
                        t: compress(setExplorer(su, fAct, true).filter(function(d) {
                            return !d.deleted
                        }).sort(function(a, b) {
                            return (a.x * 1000000 + a.y) - (b.x * 1000000 + b.y)
                        }).map(function(d) {
                            return [d.x, d.y]
                        }), 0, 2)
                    }
                });
                saveExplorerKML();
            } else {
                var explorerKMLSrcTS = explorerKMLSrc.match(/\.([0-9]*)\.kml$/);
                if (typeof(explorerKMLSrc) !== 'undefined' && explorerKMLSrcTS == null) {
                    saveExplorerKML();
                }
            }

            expSyncTiles(fAct);
            //expSyncCountries(fAct, regionStats);
        }

        s.summaries.push(su);
        s.scores.push(sc);
    } else {
        if (typeof(s.summaries.filter(function(d) {
                return selType.join() == d.type.join() &&
                    selYear.join() == d.year.join() &&
                    selGear.join() == d.gear.join()
            })[0].explorer) === 'undefined') {

            //t = t.replace('#GEAR#', '');
            var fAct = a.filter(function(d) {
                return (d.f == 0 || (d.f == 1 && d.p == 1)) &&
                    (selYear.length == 0 || selYear.indexOf(d.y) > -1) &&
                    (selType.length == 0 || selType.indexOf(d.t) > -1) &&
                    (selGear.length == 0 || selGear.indexOf(d.g) > -1)
                //return d.f == 0 && (y == '0' || y == d.y) && (t == 'All' || (!ig && t == d.t) || (ig && t == d.g)) && (incTrainer || d.tr == 0 || d.t == 'Swim')
            });

            setExplorer(s.summaries.filter(function(d) {
                return selType.join() == d.type.join() &&
                    selYear.join() == d.year.join() &&
                    selGear.join() == d.gear.join()
            })[0], fAct);
        }
    }
}

function drawScorehistory() {
    if (d3.selectAll('#scores svg')[0].length > 0) {
        return;
    }
    scores = scores.filter(function(d) { return d.s > 0 });
    var shHeight = 75;

    var shMargin = [3, 3, 25, 3];

    var shs = d3.select('#scores .well').append('svg')
        .style('margin-top', '10px')
        .style('width', '100%')
        .style('height', '70px')
        .append('g')
        .attr('transform', 'translate(' + shMargin[3] + ',' + shMargin[0] + ')');


    if (selYear.length == 0 && selType.length == 0 && selGear.length == 0) {
        d3.select('#scores .well svg')
            .style('display', 'block');
    } else {
        d3.select('#scores .well svg')
            .style('display', 'none');
    }

    shs.append('g')
        .attr('class', 'x axis')
        .attr("transform", "translate(0," + (shHeight - shMargin[0] - shMargin[2]) + ")");

    var shca = shs.append('g')
        .classed('mainContent', true);

    shca.append('path')
        .attr('fill', 'none')
        .attr('stroke', '#FF032E')
        .attr('stroke-width', '2');

    shca.selectAll('circle')
        .data(scores)
        .enter()
        .append('circle')
        .attr('r', 5)
        .attr('fill', '#FF032E')
        .attr('fill-opacity', '0')
        .attr('stroke', '#FF032E')
        .attr('stroke-thickness', '2px')
        .attr('stroke-opacity', '0')
        .on('mouseover', function(d) {
            d3.select(this).transition()
                .duration(0.5)
                .attr('fill-opacity', '0.5')
                .attr('stroke-opacity', '0.7');
        })
        .on('mouseout', function(d) {
            d3.select(this).transition()
                .duration(0.75)
                .attr('fill-opacity', '0')
                .attr('stroke-opacity', '0');
        })
        .append('title')
        .text(function(d) {
            return fDate(d.date) + ' - ' + d.s
        });

    scoreHistoryUpdateLayout();
}

var scoreExpMult = 25;

function scoreHistoryUpdateLayout() {
    var shs = d3.select('#scores .well svg');
    if (!s_s) {
        shs.style('display', 'none');
        return;
    }
    var shWidth = parseInt(shs.style('width'));
    var shHeight = 75;

    var shMargin = [3, 3, 25, 3];

    var shxs = d3.time.scale()
        .range([0, shWidth - shMargin[1] - shMargin[3]])
        .domain(d3.extent(scores, function(d) {
            return d.date
        }));

    var se = d3.extent(scores, function(d) {
        return d.s
    });

    var shys = d3.scale.linear()
        .range([shHeight - shMargin[0] - shMargin[2], 0])
        .domain([0, Math.exp(100 / scoreExpMult)]);

    var shLine = d3.svg.line()
        .x(function(e) {
            return shxs(e.date);
        })
        .y(function(e) {
            return shys(Math.exp(((e.s - se[0]) * 100 / (se[1] - se[0])) / scoreExpMult));
        });

    var shxa = d3.svg.axis().scale(shxs).ticks(3).tickFormat(fDate);

    shs.select('g.axis')
        .call(shxa);

    var shca = shs.select('g.mainContent');

    shca.select('path')
        .attr('fill', 'none')
        .attr('stroke', '#FF032E')
        .attr('stroke-thickness', '2px')
        .attr("d", shLine(scores));

    shca.selectAll('circle')
        .attr('cx', function(d) {
            return shxs(d.date)
        })
        .attr('cy', function(d) {
            return shys(Math.exp(((d.s - se[0]) * 100 / (se[1] - se[0])) / scoreExpMult))
        });
}

function drawGearSummary() {
    d3.select('#gearSummary').remove();
    var gs = d3.select('#mainContent')
        .append('div')
        .style('margin-bottom', '10px')
        .attr('id', 'gearSummary')
        .classed('row-fluid', true)
        .append('div')
        .classed('span12', true);
    gs.append('h2')
        .text('Gear Summary');
    var gt = gs.append('div')
        .classed('tableWrapper', true)
        .append('table')
        .classed('table table-striped table-condensed table-hover table-bordered', true);
    var thr = gt.append('thead')
        .append('tr');
    thr.append('th')
        .text('Type');
    thr.append('th')
        .text('Name');
    thr.append('th')
        .classed('ar', true)
        .text('Count');
    thr.append('th')
        .classed('ar', true)
        .text('Total Distance ' + lrgLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Average Distance ' + lrgLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Total Elevation ' + smlLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Average Elevation ' + smlLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Total Time');
    thr.append('th')
        .classed('ar', true)
        .text('Average Time');
    thr.append('th')
        .classed('ar', true)
        .text('Average speed/pace');
    thr.append('th')
        .classed('ar', true)
        .text('PRs');
    thr.append('th')
        .classed('ar', true)
        .text((gender == 'F' ? 'Q' : 'K') + 'OMs');
    thr.append('th')
        .classed('ar', true)
        .text('Top 3s');
    thr.append('th')
        .classed('ar', true)
        .text('Top 10s');

    var data = d3.nest()
        .key(function(d) {
            return d.g + '-' + d.t/*.replace('Virtual', '')*/.replace(' (commute)', '').replace(' (trainer)', '').replace(/(.)([A-Z])/g, '$1 $2');
        })
        .rollup(function(d) {
            var type = d[0].t/*.replace('Virtual', '')*/.replace(' (commute)', '').replace(' (trainer)', '');
            return {
                name: d[0].g,
                type: type.replace(/(.)([A-Z])/g, '$1 $2'),
                t: d[0].t,
                count: d.length,
                distance: d3.sum(d, function(e) {
                    return e.d
                }),
                elevation: d3.sum(d, function(e) {
                    return e.eg
                }),
                time: d3.sum(d, function(e) {
                    return e.mt
                }),
                aveSpeed: d3.sum(d, function(e) {
                    return e.d
                }) / d3.sum(d, function(e) {
                    return e.mt
                }),
                avgDist: d3.mean(d, function(e) {
                    return e.d
                }),
                avgElev: d3.mean(d, function(e) {
                    return e.eg
                }),
                avgTime: d3.mean(d, function(e) {
                    return e.mt
                }),
                prs: seg.filter(function(f) {
                    return type == f.t.replace(' (commute)', '').replace(' (trainer)', '') && f.gn == d[0].g && (selYear.length == 0 || selYear.indexOf(f.y) > -1)
                }).length,
                koms: seg.filter(function(f) {
                    return type == f.t.replace(' (commute)', '').replace(' (trainer)', '') && f.gn == d[0].g && f.r == 1 && (selYear.length == 0 || selYear.indexOf(f.y) > -1)
                }).length,
                top3: seg.filter(function(f) {
                    return type == f.t.replace(' (commute)', '').replace(' (trainer)', '') && f.gn == d[0].g && f.r > 0 && f.r <= 3 && (selYear.length == 0 || selYear.indexOf(f.y) > -1)
                }).length,
                top10: seg.filter(function(f) {
                    return type == f.t.replace(' (commute)', '').replace(' (trainer)', '') && f.gn == d[0].g && f.r > 0 && f.r <= 10 && (selYear.length == 0 || selYear.indexOf(f.y) > -1)
                }).length
            };
        })
        .map(a.filter(function(d) {
            return (selYear.length == 0 || selYear.indexOf(d.y) > -1) && (selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1);
            //return (selYear == 'All' || d.y == selYear) && (incTrainer || d.tr == 0);
        }));

    data = d3.values(data).sort(function(a, b) {
        if (a.type < b.type) {
            return -1;
        } else {
            if (a.type > b.type) {
                return 1;
            } else {
                if (a.name < b.name) {
                    return -1;
                } else {
                    return 1;
                }
            }
        }
    });

    /*var yearFilter = ''
    if (selYear.length > 0) {
        selYear.sort();
        yearFilter = ',0:'+(new Date(selYear[0],0,1).getTime())+'|'+(new Date(selYear[selYear.length-1],11,31).getTime());
    }*/

    gt.append('tbody')
        .selectAll('tr')
        .data(data)
        .enter()
        .append('tr')
        .each(function(d) {
            var item = d3.select(this);
            item.append('td')
                .html(d.type);
            item.append('td')
                .html(d.name);
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(0, 1, -1) + ',').replace(/^,/, '') + '1:' + encodeURIComponent(d.t.replace(' (trainer)', '')) + ',43:' + encodeURIComponent(d.name) + '">' + d.count + '</a>');
            item.append('td')
                .classed('ar', true)
                .html(f1dp(d.distance * lrgLenMult));
            item.append('td')
                .classed('ar', true)
                .html(f1dp(d.avgDist * lrgLenMult));
            item.append('td')
                .classed('ar', true)
                .html(fInt(d.elevation * smlLenMult));
            item.append('td')
                .classed('ar', true)
                .html(fInt(d.avgElev * smlLenMult));
            item.append('td')
                .classed('ar', true)
                .html(d.time.toHrMinSec());
            item.append('td')
                .classed('ar', true)
                .html(d.avgTime.toHrMinSec());
            item.append('td')
                .classed('ar', true)
                .html(d.type == 'Run' ? (lrgPaceMult / d.aveSpeed).toDayHrMinSec2() + ' ' + lrgPaceUnit : (d.type == 'Swim' ? (smlPaceMult / d.aveSpeed).toDayHrMinSec2() + ' ' + smlPaceUnit : f1dp(d.aveSpeed * speedMult) + ' ' + speedUnit));
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, -1) + ',').replace(/^,/, '') + '34:' + encodeURIComponent(d.name) + '">' + fInt(d.prs) + '</a>');
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, -1) + ',').replace(/^,/, '') + '0:1|1,34:' + encodeURIComponent(d.name) + '">' + fInt(d.koms) + '</a>');
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, -1) + ',').replace(/^,/, '') + '0:1|3,34:' + encodeURIComponent(d.name) + '">' + fInt(d.top3) + '</a>');
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/segments?f=' + (getQSFilter(28, 5, -1) + ',').replace(/^,/, '') + '0:1|10,34:' + encodeURIComponent(d.name) + '">' + fInt(d.top10) + '</a>');
        });
}

function drawDeviceSummary() {

    d3.select('#deviceSummary').remove();
    var gs = d3.select('#mainContent')
        .append('div')
        .style('margin-bottom', '10px')
        .attr('id', 'deviceSummary')
        .classed('row-fluid', true)
        .append('div')
        .classed('span12', true);
    gs.append('h2')
        .text('Device/App Summary');
    var gt = gs.append('div')
        .classed('tableWrapper', true)
        .append('table')
        .classed('table table-striped table-condensed table-hover table-bordered', true);
    var thr = gt.append('thead')
        .append('tr');
    thr.append('th')
        .text('Name');
    thr.append('th')
        .classed('ar', true)
        .text('Count');
    thr.append('th')
        .classed('ar', true)
        .text('Total Distance ' + lrgLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Average Distance ' + lrgLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Total Elevation ' + smlLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Average Elevation ' + smlLenUnit);
    thr.append('th')
        .classed('ar', true)
        .text('Total Time');
    thr.append('th')
        .classed('ar', true)
        .text('Average Time');
    thr.append('th')
        .classed('ar', true)
        .text('Average speed/pace');
    
    var data = d3.nest()
        .key(function (d) {
            return d.dv.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase();
        })
        .rollup(function (d) {
            var device = d[0].dv;
            return {
                name: d[0].dv,
                count: d.length,
                distance: d3.sum(d, function (e) {
                    return e.d
                }),
                elevation: d3.sum(d, function (e) {
                    return e.eg
                }),
                time: d3.sum(d, function (e) {
                    return e.mt
                }),
                aveSpeed: d3.sum(d, function (e) {
                    return e.d
                }) / d3.sum(d, function (e) {
                    return e.mt
                }),
                avgDist: d3.mean(d, function (e) {
                    return e.d
                }),
                avgElev: d3.mean(d, function (e) {
                    return e.eg
                }),
                avgTime: d3.mean(d, function (e) {
                    return e.mt
                })
            };
        })
        .map(a.filter(function (d) {
            return (selYear.length == 0 || selYear.indexOf(d.y) > -1) && (selType.length == 0 || selType.indexOf(d.t) > -1) && (selGear.length == 0 || selGear.indexOf(d.g) > -1);
            //return (selYear == 'All' || d.y == selYear) && (incTrainer || d.tr == 0);
        }));

    data = d3.values(data).sort(function (a, b) {
        if (a.name.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase() < b.name.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase()) {
            return -1;
        } else {
            return 1;
        }
    });

    /*var yearFilter = ''
    if (selYear.length > 0) {
        selYear.sort();
        yearFilter = ',0:'+(new Date(selYear[0],0,1).getTime())+'|'+(new Date(selYear[selYear.length-1],11,31).getTime());
    }*/

    gt.append('tbody')
        .selectAll('tr')
        .data(data)
        .enter()
        .append('tr')
        .each(function (d) {
            var item = d3.select(this);
            item.append('td')
                .html(d.name);
            item.append('td')
                .classed('ar', true)
                .html('<a target="_blank" href="/athlete/' + contextAthleteId + '/activities?f=' + (getQSFilter(0, 1, -1) + ',').replace(/^,/, '') + '108:' + encodeURIComponent(d.name) + '">' + d.count + '</a>');
            item.append('td')
                .classed('ar', true)
                .html(f1dp(d.distance * lrgLenMult));
            item.append('td')
                .classed('ar', true)
                .html(f1dp(d.avgDist * lrgLenMult));
            item.append('td')
                .classed('ar', true)
                .html(fInt(d.elevation * smlLenMult));
            item.append('td')
                .classed('ar', true)
                .html(fInt(d.avgElev * smlLenMult));
            item.append('td')
                .classed('ar', true)
                .html(d.time.toHrMinSec());
            item.append('td')
                .classed('ar', true)
                .html(d.avgTime.toHrMinSec());
            item.append('td')
                .classed('ar', true)
                .html(d.type == 'Run' ? (lrgPaceMult / d.aveSpeed).toDayHrMinSec2() + ' ' + lrgPaceUnit : (d.type == 'Swim' ? (smlPaceMult / d.aveSpeed).toDayHrMinSec2() + ' ' + smlPaceUnit : f1dp(d.aveSpeed * speedMult) + ' ' + speedUnit));
        });
}

function localInit() {
    if (!initialised && segCacheReady && actCacheReady && segAsyncCount + actAsyncCount + asyncCount <= 0) {
        if ((typeof(cacheSeg) !== 'undefined') && typeof(cacheAct) !== 'undefined') {
            if ( /*cacheSeg.length > 0 &&*/ cacheAct.length > 0) {
                initialised = true;
                seg = cacheSeg;
                delete cacheSeg;
                a = cacheAct;
                delete cacheAct;

                for (var i = 0; i < a.length; i++) {
                    a[i].an = a[i].an.replaceAll('|', '&#124;');
                    a[i].date = parseDateTime(a[i].s, a[i].tz);
                    a[i].dow = a[i].date.getDay();
                    a[i].timestamp = a[i].date.getTime();
                    if (a[i].trn && a[i].t != 'Swim') {
                        a[i].t += ' (trainer)';
                    }
                    if (a[i].co) {
                        a[i].t += ' (commute)';
                    }
                }

                for (var i = 0; i < seg.length; i++) {
                    if (typeof(tacts[seg[i].ai]) !== 'undefined' && tacts[seg[i].ai].trn && tacts[seg[i].ai].t != 'Swim') {
                        seg[i].t += ' (trainer)';
                    }
                    if (typeof(tacts[seg[i].ai]) !== 'undefined' && tacts[seg[i].ai].co) {
                        seg[i].t += ' (commute)';
                    }
                }

                d3.select('#charts').insert('div', ':first-child')
                    .attr('title', 'Based on your previous history this is your projected total for the year end.')
                    .attr('id', 'projectedValueWrapper')
                    .style({
                        'float': 'right',
                        'text-align': 'right',
                        'margin-top': '-5px',
                        'font-weight': 'bold',
                        'line-height': 'normal'
                    }).html('<input title="Enable/disable projected year end values/line" type="checkbox" id="projectedYDProjectionCB" ' + (showYDProjection ? 'checked="checked"' : '') + '/> <span class="hidden-tablet">Projected y</span><span class="visible-tablet">Y</span>ear end:<br/><span id="projectedValue">n/a</span>');

                d3.select('#projectedYDProjectionCB').on('change', function() {
                    showYDProjection = this.checked;
                    localStorage['SummaryShowYDProjection'] = showYDProjection ? 1 : 0;
                    displayYearDistances();
                });

                d3.select('#charts').append('div').each(function() {
                    var t = d3.select(this).attr('id', 'projectedRangeContainer').style({
                        'margin-top': '-20px',
                        'text-align': 'center',
                        'white-space': 'nowrap'
                    });
                    t.append('span').style({
                        'width': '110px',
                        'display': 'inline-block',
                        'white-space': 'normal'
                    }).attr({
                        'id': 'projectedRangeS1',
                        'title': 'The previous years\' trends are based on the last two *selected* years\' data.'
                    });
                    t.append('input').attr({
                        id: 'projectedRangeValue',
                        style: 'vertical-align:top;margin-top:10px;',
                        type: 'range',
                        min: 0,
                        max: 100,
                        step: 1,
                        value: hasLocalStorage && typeof(localStorage['projectedRangeValue']) !== 'undefined' ? +localStorage['projectedRangeValue'] : 25
                    }).on('input', projectedRangeChanged).on('change', projectedRangeChanged);
                    t.append('span').style({
                        'width': '80px',
                        'white-space': 'normal',
                        'display': 'inline-block'
                    }).attr('id', 'projectedRangeS2');
                });

                d3.selectAll('button.prs, button.egd, button.ph, button.ah, button.aw, button.ca, button.streak').remove();

                d3.selectAll('#calBG button')
                    .on('click', function(d) {
                        calVal = d3.select(this).attr('data-val');
                        drawCal();
                    });


                d3.select('#calDisplayValue')
                    .on('change', function() {
                        calVal = document.getElementById('calDisplayValue').value;
                        if (hasPowerMeter) d3.select('#ccPowerMeterOnly').style('display', (calVal.substr(0, 2) == 'pz' || calVal == 'aw' ? null : 'none'));
                        drawCal();
                    })
                    .selectAll('option')
                    .data(displayValues.filter(function(d) {
                        return typeof(d.tcOnly) === 'undefined'
                    }), function(d) {
                        return d.field
                    })
                    .enter().append('option')
                    .attr('value', function(d) {
                        return d.field
                    })
                    .attr('selected', function(d) {
                        return d.field == calVal ? 'selected' : null
                    })
                    .text(function(d) {
                        return d.name
                    });

                d3.select('#timeDisplayValue')
                    .on('change', function() {
                        tcCalVal = document.getElementById('timeDisplayValue').value;
                        if (hasPowerMeter) d3.select('#tcPowerMeterOnly').style('display', (tcCalVal.substr(0, 2) == 'pz' || tcCalVal == 'aw' ? null : 'none'));
                        d3.selectAll('#tcContainer .nv-bar').remove();
                        drawtc();
                    })
                    .selectAll('option')
                    .data(displayValues.filter(function(d) {
                        return typeof(d.calOnly) === 'undefined'
                    }), function(d) {
                        return d.field
                    })
                    .enter().append('option')
                    .attr('value', function(d) {
                        return d.field
                    })
                    .attr('selected', function(d) {
                        return d.field == tcCalVal ? 'selected' : null
                    })
                    .text(function(d) {
                        return d.name
                    });

                if (a.filter(function(d) {
                        return d.hp
                    }).length > 0) {
                    hasPowerMeter = true;

                    d3.select('#timeChart').insert('div', 'div:nth-child(2)')
                        .style({
                            'margin-left': '10px',
                            'display': 'none'
                        })
                        .attr('id', 'tcPowerMeterOnly')
                        .style('float', 'left')
                        .append('label').each(function() {
                            var item = d3.select(this);
                            item.append('input').attr({
                                    type: 'checkbox',
                                    id: 'tcPowerMeterOnlyCB'
                                })
                                .on('change', function() {
                                    tcCalVal = document.getElementById('timeDisplayValue').value;
                                    d3.selectAll('#tcContainer .nv-bar').remove();
                                    drawtc();
                                });
                            item.append('span').style('vertical-align', 'bottom').text(' include estimated');
                        });

                    d3.select('#calendar').insert('div', 'div:nth-child(2)')
                        .style({
                            'margin-left': '10px',
                            'display': 'none'
                        })
                        .attr('id', 'ccPowerMeterOnly')
                        .style('float', 'left')
                        .append('label').each(function() {
                            var item = d3.select(this);
                            item.append('input').attr({
                                    type: 'checkbox',
                                    id: 'ccPowerMeterOnlyCB'
                                })
                                .on('change', function() {
                                    drawCal();
                                });
                            item.append('span').style('vertical-align', 'bottom').text(' include estimated');
                        });

                    d3.select('#ccPowerMeterOnly').style('display', (calVal.substr(0, 2) == 'pz' || calVal == 'aw' ? null : 'none'));
                    d3.select('#tcPowerMeterOnly').style('display', (tcCalVal.substr(0, 2) == 'pz' || tcCalVal == 'aw' ? null : 'none'));
                }

                d3.select('#calContainer')
                    .append('div')
                    .attr('id', 'calContainerInner')
                    .style({
                        'max-height': '400px',
                        'overflow-y': 'auto'
                    });

                d3.selectAll('#calDOW').remove();
                d3.select('#calGroupBG').style('float', 'left');
                d3.select('#calendar').insert('div', '#calContainer')
                    .attr('id', 'calRoll')
                    .style({
                        float: 'left',
                        display: 'none',
                        margin: '5px 10px 0px 0px'
                    });
                d3.select('#calendar').insert('label', '#calContainer')
                    .attr({
                        id: 'calDOW',
                        title: 'Group by the Day of Week (for month and year groupings only)'
                    })
                    .style({
                        float: 'left',
                        display: 'none',
                        margin: '5px 10px 0px 0px',
                        'font-size': '1em'
                    }).each(function() {
                        var item = d3.select(this);
                        item.append('input').attr({
                                type: 'checkbox',
                                id: 'calDOWCB'
                            })
                            .style('margin', '0px')
                            .on('change', function() {
                                calDOW = document.getElementById('calDOWCB').checked;
                                drawCal()
                            });
                        item.append('span').style('vertical-align', 'bottom').text(' DoW');
                    });

                d3.selectAll('#climbsTable th:not(:first-child)').classed('ar', true);

                for (var i = 0; i < a.length; i++) {
                    if (((typeof(a[i].map) !== 'undefined' && a[i].map != null) || typeof(texplorer[a[i].i]) !== 'undefined') && a[i].t.substring(0, 7) != 'Virtual') {
                        getActMapTiles(a[i], explorerZoom, 100);
                    }
                }
                tilesCalculated = true;

                calcStatsPrep();

                var scoreLastSaved = d3.max((actsSrc + bestsSrc + effsSrc + segsSrc + segDetsSrc + mapsSrc + (typeof(actsPSrc) !== 'undefined' ? actsPSrc + bestsPSrc + effsPSrc + mapsPSrc + explorerSrc : '')).match(/\.[0-9]*\./g).map(function(d) {
                    return parseInt(d.replace(/\./g, ''))
                }));

                benchmarkScores = [];
                if (hasLocalStorage && typeof(localStorage['scoreLastSaved' + contextAthleteId]) !== 'undefined') {
                    benchmarkScores = JSON.parse(localStorage['scores' + contextAthleteId])
                }
                if (!hasLocalStorage || lastScoreValue == -1 || (typeof(explorerTilesSrc) !== 'undefined' && explorerTilesSrc.match(/\.([0-9]*)\.js$/) == null) || typeof(localStorage['scoreLastSaved' + contextAthleteId]) === 'undefined' || scoreLastSaved != localStorage['scoreLastSaved' + contextAthleteId] || benchmarkScores.filter(function(d) {
                        return d.actType == 'Swim'
                    }).length == 0 || benchmarkScores.filter(function(d) {
                        return typeof(d.edc) !== 'undefined'
                    }).length == 0) {
                    var lYear = clone(selYear);

                    var lType = clone(selType);
                    var lGear = clone(selGear);

                    var savedCombos = [{
                        y: [],
                        t: []
                    }, {
                        y: [new Date().getFullYear()],
                        t: []
					}, {
                        y: [],
                        t: ["Ride", "Ride (commute)"]
                    }, {
                        y: [new Date().getFullYear()],
                        t: ["Ride", "Ride (commute)"]
					}, {
                        y: [],
                        t: ["VirtualRide", "VirtualRide (trainer)", "VirtualRide (trainer) (commute)"]
                    }, {
                        y: [new Date().getFullYear()],
                        t: ["VirtualRide", "VirtualRide (trainer)", "VirtualRide (trainer) (commute)"]
                    }, {
                        y: [],
                        t: ['Run']
                    }, {
                        y: [new Date().getFullYear()],
                        t: ['Run']
                    }, {
                        y: [],
                        t: ['Swim']
                    }, {
                        y: [new Date().getFullYear()],
                        t: ['Swim']
                    }];

                    /*if (!hasLocalStorage || typeof(localStorage['score2015LastSaved' + contextAthleteId]) === 'undefined') {
                        if (hasLocalStorage) {
                            localStorage['score2015LastSaved' + contextAthleteId] = scoreLastSaved;
                        }*/
                    savedCombos = d3.merge([savedCombos, [{
                        y: [new Date().getFullYear() - 1],
                        t: []
                    }, {
                        y: [new Date().getFullYear() - 1],
                        t: ["Ride", "Ride (commute)"]
                    }, {
                        y: [new Date().getFullYear() - 1],
                        t: ["VirtualRide", "VirtualRide (trainer)", "VirtualRide (trainer) (commute)"]
                    }, {
                        y: [new Date().getFullYear() - 1],
                        t: ["Run"]
                    }, {
                        y: [new Date().getFullYear() - 1],
                        t: ["Swim"]
                    }]]);
                    //}

                    selGear = [];
                    savedCombos.forEach(function(d) {
                        selYear = d.y;
                        selType = d.t;
                        calcStats(true);
                        if (selYear.length == 0 && selType.length == 0) {
                            getExplorerScore(true);
                        }
                    });

                    if (loggedInAthleteId == contextAthleteId) {
                        $.ajax({
                            type: "POST",
                            url: '/api/saveScoreNew.php',
                            data: {
                                id: contextAthleteId,
                                scores: s.summaries.map(function(d, i) {
                                    return {
                                        actType: d.type.filter(function(d) {
                                            return d.indexOf('commute') == -1 && d.indexOf('trainer') == -1
                                        }).join(),
                                        year: d.year.join(),
                                        dist: d.totalDist,
                                        elev: d.totalElev,
                                        time: d.totalTime,
                                        expl: d.explorer,
                                        explm: d.explorerMax,
                                        explc: d.explorerClumpMax,
                                        explcs: d.explorerClumpMaxSpan.span,
                                        expltc: d.tilesMaxConnected,
                                        expltcs: d.explorerMaxConnectedSpan.span,
                                        explxc: d.tilesMaxConnectedX.length > 0 ? d.tilesMaxConnectedX[0].max : 0,
                                        explyc: d.tilesMaxConnectedY.length > 0 ? d.tilesMaxConnectedY[0].max : 0,
                                        explxt: d.tilesTotalX.length > 0 ? d.tilesTotalX[0].ys.length : 0,
                                        explyt: d.tilesTotalY.length > 0 ? d.tilesTotalY[0].xs.length : 0,
                                        edm: d.eddingtonMl,
                                        edk: d.eddingtonKm,
                                        edt: d.eddingtonTime,
                                        edc: d.eddingtonClimb,
                                        k: d.kIndex,
                                        vvs: s.scores[i].score
                                    }
                                })
                            }
                        });
                    }
                    benchmarkScores = s.summaries.map(function(d, i) {
                        return {
                            actType: d.type.filter(function(d) {
                                return d.indexOf('commute') == -1
                            }).join(),
                            year: d.year.join(),
                            dist: d.totalDist,
                            elev: d.totalElev,
                            time: d.totalTime,
                            expl: d.explorer,
                            explm: d.explorerMax,
                            explc: d.explorerClumpMax,
                            explcs: d.explorerClumpMaxSpan.span,
                            expltc: d.tilesMaxConnected,
                            expltcs: d.explorerMaxConnectedSpan.span,
                            explxc: d.tilesMaxConnectedX.length > 0 ? d.tilesMaxConnectedX[0].max : 0,
                            explyc: d.tilesMaxConnectedY.length > 0 ? d.tilesMaxConnectedY[0].max : 0,
                            explxt: d.tilesTotalX.length > 0 ? d.tilesTotalX[0].ys.length : 0,
                            explyt: d.tilesTotalY.length > 0 ? d.tilesTotalY[0].xs.length : 0,
                            edm: d.eddingtonMl,
                            edk: d.eddingtonKm,
                            edt: d.eddingtonTime,
                            edc: d.eddingtonClimb,
                            k: d.kIndex,
                            vvs: s.scores[i].score
                        }
                    });

                    if (hasLocalStorage) {
                        localStorage['scoreLastSaved' + contextAthleteId] = scoreLastSaved;
                        localStorage['scores' + contextAthleteId] = JSON.stringify(benchmarkScores);
                    }

                    selYear = lYear;
                    selType = lType;
                    selGear = lGear;
                } else {
                    getExplorerScore(false);
                }

                drawMain();

                drawOutline();
                drawCal();
                drawtc();
                drawGearSummary();
                drawDeviceSummary();

                displayYearDistances();

                if (contextAthleteId == loggedInAthleteId && isPRO) {
                    d3.select('#flaggedActs').remove();
                    var flaggedActs = a.filter(function(d) {
                        return d.f == 1 && d.p == 0
                    });
                    if (flaggedActs.length > 0) {
                        d3.select('#mainContent').insert('div', ':first-child').attr('id', 'flaggedActs').classed('alert alert-info', true)
                            .html('You have ' + flaggedActs.map(function(d, i) {
                                return '<a target="_blank" href="https://www.strava.com/activities/' + d.i + '">' + (i + 1) + '</a>'
                            }).join(', ') + ' flagged activit' + (flaggedActs.length > 1 ? 'ies' : 'y') + '! Flagged activities will not be counted towards your overall/leaderboard stats. <a href="/athlete/' + contextAthleteId + '/activities?f=39:1" target="_blank">View all your flagged activities</a>.');
                    }
                }

                d3.select('#resetAll').remove();
                d3.select('#mainContent div div').append('button')
                    .attr('id', 'resetAll')
                    .style('display', 'inline-block')
                    .classed('btn btn-mini btn-primary', true)
                    .attr('title', 'Reset all filters and views to their defaults.')
                    .text('Reset')
                    .on('click', function() {
                        showYDProjection = true;
                        localStorage['SummaryShowYDProjection'] = 1;
                        document.getElementById('projectedYDProjectionCB').checked = true;

                        selYear = [];
                        selType = [];
                        selGear = [];
                        calVal = 'd', calGroup = 'day', tcCalVal = 'd', tcCalGroup = 'day', viewBy = 'yrCumDist';
                        document.getElementById('calDisplayValue').selectedIndex = 0;
                        document.getElementById('actSummaryYBG').selectedIndex = 0;
                        document.getElementById('timeDisplayValue').selectedIndex = 0;
                        d3.selectAll('#actSummaryZoomBG button').classed('active', null);
                        d3.select('#zoomOff').classed('active', true);
                        zoomYearChart = 0;
                        d3.selectAll("#tcGroupBG button.active, #calGroupBG button.active").classed('active', false);
                        d3.selectAll("#tcGroupBG button[data-val='day'], #calGroupBG button[data-val='day']").classed('active', true);
                        $("#yearsSelect select,#typesSelect select,#gearSelect select").val('').trigger("chosen:updated");
                        if (years.length <= 4) {
                          d3.selectAll("#yearsSelect button").classed('active', false);
                          d3.selectAll("#yearsSelect button:first-child").classed('active', true);
                        }
                        if (types.length <= 3) {
                          d3.selectAll("#typesSelect button").classed('active', false);
                          d3.selectAll("#typesSelect button:first-child").classed('active', true);
                        }
                        if (gears.length <= 3) {
                          d3.selectAll("#gearSelect button").classed('active', false);
                          d3.selectAll("#gearSelect button:first-child").classed('active', true);
                        }
                        filtersUpdated();
                    });

                var simpleDateFormat = d3.time.format("%Y-%m-%d");
                scores.forEach(function(d) {
                    d.date = simpleDateFormat.parse(d.d);
                });

                drawScorehistory();
                setDlWidth();

                window.onresize = sizeChanged;
                window.setTimeout(sizeChanged, 500);
            } else {
                if (hasLocalStorage) {
                    localStorage.clear();
                }
                alert('Your data doesn\'t appear to be complete! Make sure you\'ve completed at least one, full sync on the update page. If you think all your data is already synced then try refreshing the page. Also make sure that your activities in Strava contain at least one segment.');
            }
        } else {
            if (hasLocalStorage) {
                localStorage.clear();
            }
            alert('Oh dear, something\'s not gone right. Try refreshing the page (Ctrl+F5 to clear your cache might help) and see if it helps. If you keep seeing this message then let me know.');
        }
    }
}

function sizeChanged() {
    drawOutline();
    drawCal();
    //drawtc();
    //displayYearDistances();
    scoreHistoryUpdateLayout();
}

$(document).ready(function() {
    var calDT = document.createElement('dt');
    var textnode = document.createTextNode("Total kcal:");
    calDT.appendChild(textnode);
    var calDD = document.createElement('dd');
    var textnode = document.createTextNode(" ");
    calDD.appendChild(textnode);
    calDD.id = 'totKcal';
    d3.select('#actStats dl').node().insertBefore(calDT, d3.select('#actStats dl').node().children[8]);
    d3.select('#actStats dl').node().insertBefore(calDD, d3.select('#actStats dl').node().children[9]);

    var edDT = document.createElement('dt');
    edDT.id = 'eddingtonTitle';
    var edDD = document.createElement('dd');
    var textnode = document.createTextNode(" ");
    edDD.appendChild(textnode);
    edDD.id = 'eddington';
    d3.select('#actStats dl').node().insertBefore(edDT, d3.select('#actStats dl').node().children[10]);
    d3.select('#actStats dl').node().insertBefore(edDD, d3.select('#actStats dl').node().children[11]);

    d3.select('#eddingtonTitle').append('a')
        .attr({
            'href': '#eddingtonModal',
            'role': 'button',
            'data-toggle': 'modal',
            'class': 'btn btn-mini btn-primary',
            'id': 'eddingtonDetailsBtn'
        }).html('Eddington <span class="icon-signal icon-white"></span>');
    d3.select('#eddingtonTitle').append('span').text(' :');

    d3.select('#eddington').style({
        'margin-top': '2px',
        'margin-bottom': '2px'
    });

    var kIndDT = document.createElement('dt');
    kIndDT.title = "The K-index is based on h-index used in measuring productivity and citation impact for academic papers. The K-index is comparing the amount of Kudos you receive on how many activities. e.g. if you've had at least 10 Kudos on at least 10 activities then your K-index is 10.";
    var textnode = document.createTextNode("(Kudos) K-index:");
    kIndDT.appendChild(textnode);
    var kIndDD = document.createElement('dd');
    kIndDD.title = "The K-index is based on h-index used in measuring productivity and citation impact for academic papers. The K-index is comparing the amount of Kudos you receive on how many activities. e.g. if you've had at least 10 Kudos on at least 10 activities then your K-index is 10.";
    var textnode = document.createTextNode(" ");
    kIndDD.appendChild(textnode);
    kIndDD.id = 'kIndex';
    d3.select('#actStats dl').node().insertBefore(kIndDT, d3.select('#actStats dl').node().children[12]);
    d3.select('#actStats dl').node().insertBefore(kIndDD, d3.select('#actStats dl').node().children[13]);

    var ssdl = d3.select('#segStats dl');
    ssdl.append('dt').text((gender == 'F' ? 'Q' : 'K') + 'OMs:');
    ssdl.append('dd').attr('id', 'rank1').html('&nbsp;');
    ssdl.append('dt').text('2nd places:');
    ssdl.append('dd').attr('id', 'rank2').html('&nbsp;');
    ssdl.append('dt').text('3rd places:');
    ssdl.append('dd').attr('id', 'rank3').html('&nbsp;');
    ssdl.append('dt').text('Top 5s:');
    ssdl.append('dd').attr('id', 'rank5').html('&nbsp;');
    ssdl.append('dt').text('Top 10s:');
    ssdl.append('dd').attr('id', 'rank10').html('&nbsp;');
    ssdl.append('dt').text('Top 25s:');
    ssdl.append('dd').attr('id', 'rank25').html('&nbsp;');
    ssdl.append('dt').text('Top 50s:');
    ssdl.append('dd').attr('id', 'rank50').html('&nbsp;');
    ssdl.append('dt').text('Top 100s:');
    ssdl.append('dd').attr('id', 'rank100').html('&nbsp;');

    d3.select('#actStats > div')
    .insert('a', 'h2')
    .attr({
        href: '/leaderboard',
        role: 'button'
    })
    .style('float', 'right')
    .classed('btn btn-mini btn-primary', true)
    .html('<span class="icon-signal icon-white"></span> Overall/Club<br/>Yearly Leaderboards');

    asyncAdd2('asyncCount');
    progressTotal += asyncCount;

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

            if (typeof(obj.error) === "undefined") {
                if (obj.scores.length > 0) {
                    scores = obj.scores.filter(function(d) {
                        return d.s != null
                    });
                    /*var lastScore = 0;
                    scores = scores.filter(function(d) {
                        if (d.s < lastScore * 0.99) {
                        return false;
                        }
                        lastScore = d.s;
                        return true
                    })*/
                }
                asyncComplete2(localInit, 'asyncCount');
            } else {
                alert('Error GSS1: 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, ajaxObj) {
            this.tryCount++;
            if (this.tryCount <= this.retryLimit) {
                $.ajax(this);
            } else {
                alert('Error GSS2: Oh dear, some of your data hasn\'t loaded correctly. Try refreshing the page. If you keep seeing this message then let me know.')
            }
        }
    });
});

var dGoals = d3.merge([
    [0.5 / lrgLenMult,
        1 / lrgLenMult
    ],
    d3.range(2.5 / lrgLenMult, 10 / lrgLenMult, 2.5 / lrgLenMult),
    d3.range(10 / lrgLenMult, 50 / lrgLenMult, 10 / lrgLenMult),
    d3.range(50 / lrgLenMult, 200 / lrgLenMult, 25 / lrgLenMult),
    d3.range(200 / lrgLenMult, 500 / lrgLenMult - 1, 50 / lrgLenMult),
    d3.range(500 / lrgLenMult, 1000 / lrgLenMult - 1, 100 / lrgLenMult),
    d3.range(1000 / lrgLenMult, 2500 / lrgLenMult - 1, 250 / lrgLenMult),
    d3.range(2500 / lrgLenMult, 5000 / lrgLenMult - 1, 500 / lrgLenMult),
    d3.range(5000 / lrgLenMult, 1 + 10000 / lrgLenMult, 1000 / lrgLenMult)
]);
/*if (smlLenUnit == 'm') {
  var dGoals = d3.merge([[0.5 / lrgLenMult, 1 / lrgLenMult], d3.range(2.5 / lrgLenMult, 10 / lrgLenMult, 2.5 / lrgLenMult), d3.range(10 / lrgLenMult, 25 / lrgLenMult, 5 / lrgLenMult), d3.range(25 / lrgLenMult, 1 + 1000 / lrgLenMult, 25 / lrgLenMult)]);
} else {
  var dGoals = d3.range(10 / lrgLenMult, 1 + 1000 / lrgLenMult, 10 / lrgLenMult);
}*/
if (smlLenUnit == 'm') {
    var eGoals = d3.merge([d3.range(50, 250, 50), d3.range(250, 1000, 250), d3.range(1000, 5000, 500), d3.range(5000, 10000, 1000), d3.range(10000, 25001, 2500)]);
} else {
    var eGoals = d3.merge([d3.range(100 / smlLenMult, 500 / smlLenMult, 100 / smlLenMult), d3.range(500 / smlLenMult, 1000 / smlLenMult, 250 / smlLenMult), d3.range(1000 / smlLenMult, 2000 / smlLenMult, 500 / smlLenMult), d3.range(2000 / smlLenMult, 5000 / smlLenMult, 1000 / smlLenMult), d3.range(5000 / smlLenMult, 10000 / smlLenMult, 2500 / smlLenMult), d3.range(10000 / smlLenMult, 1 + 55000 / smlLenMult, 5000 / smlLenMult)]);
}
var tGoals = [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 6, 9, 12, 18, 24, 30, 36, 48, 60, 72, 96, 120, 144, 168].map(function(d) {
    return d * 3600
});
//d3.merge([d3.range(900, 3600, 900), d3.range(3600, 7200, 1800), d3.range(7200, 43200, 3600), d3.range(43200, 86400, 7200), d3.range(86400, 172800, 10800), d3.range(172800, 259201, 21600)]);

function getGoals(param, valArr, data, format1, format2) {
    if (format2 == null) {
        format2 = format1;
    }
    var dAchs = [];
    var lastFound = -1;
    for (var i = 1; i < valArr.length; i++) {
        var dAch = {
            param: param,
            goal: valArr[i - 1],
            format1: format1,
            format2: format2
        };
        dAch.a = data.filter(function(e) {
            return e[param] >= valArr[i - 1] && e[param] < valArr[i]
        });
        if (dAch.a.length > 0) {
            lastFound = i;
        }
        dAchs.push(dAch);
    }
    return dAchs.slice(0, lastFound + 1).reverse().slice(0, 8);
}
var cpref = 'sum';

var s_sNotice = (!s_s ? '<p style="' + (isFree ? 'margin-top:8px;' : '') + '"><b>Important note:</b> Stats related to 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) {
    d3.select('#tourInfo')
        .style('display', null)
        .classed('alert alert-info', true)
        .html('<h4>VeloViewer Tour Notes</h4><a class="btn btn-primary" href="http://cf.veloviewer.com/img/veloviewer-strava-summary-homepage.png" target="_blank" style="float:right"><img style="height:30px" src="https://cf.veloviewer.com/img/veloviewer-strava-summary-homepage-small.png"></a>' + s_sNotice + '<p style="' + (s_s ? 'margin-top:8px;' : '') + 'font-weight:bold">Restricted to 25 activities and 250 segments for free users. <a href="/update">Upgrade to PRO</a> to see your entire Strava history.</p><p>Your Summary page provides a great overview of your entire Strava history showing year on year comparisons of distance/elevation/time/speed/etc along with your top activities and segments.  Easily filterable by year, activity type and gear (bikes and shoes) gives you an easy view of how your are progressing along with summaries by bike/shoe right at the bottom. The VeloViewer Score provides a simple value to compare yourself with your peers. To see how this page looks with a full set of data then click the button over there &#10137;</p><p>After trying out all the filter buttons on this page then head to one of the other tabs just below maybe starting at <b>"Activities"</b>.</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>');
} else {
    if (!s_s) {
        d3.select('#tourInfo')
            .style('display', null)
            .classed('alert alert-info', true)
            .html(s_sNotice);
        d3.select('#tourInfo p').style('margin-bottom', '0px');
    }
}

if (typeof(getCookie('hideScore')) !== 'undefined' || getParameterByName('hideScore') != '') {
    d3.select('#scores').style('display', 'none');
    setCookie('hideScore', 1, 10000);
}

var explorerScore = -1, clumpScore = -1;
function getExplorerScore(force) {
    if (loggedInAthleteId != contextAthleteId) {
        updateExplorerScore();
        return;
    }
    var sum = s.summaries.filter(function (d) { return d.type.length == 0 && d.year.length == 0 && d.gear.length == 0 });
    if (!force || sum.length > 0) {
        $.ajax({
            url: '/api/getExplorerScore.php',
            type: "POST",
            data: force ? {
                t: sum[0].explorerTilesCompressed,
                c: sum[0].explorerClumpCompressed
            } : {},
            success: function(result) {
                if (typeof(result.error) !== 'undefined') {
                    explorerScore = -2;
                    clumpScore = -2;
                } else {
                    explorerScore = result.score;
                    clumpScore = result.clumpScore;
                }
                updateExplorerScore();
            }
        });
    } else {
        explorerScore = -2;
        clumpScore = -2;
        updateExplorerScore();
    }
}

function updateExplorerScore() {
    if (d3.select('#explorerScoreValue').node() != null) {
        if (loggedInAthleteId != contextAthleteId) {
            d3.select('#explorerScoreValue').html('Only available when looking at your own data');
            d3.select('#clumpScoreValue').html('Only available when looking at your own data');
            return;
        }
        if (selYear.length == 0 && selType.length == 0 && selGear.length == 0) {
            d3.select('#explorerScoreValue').html((explorerScore == -1 ? 'Please wait...' : (explorerScore == -2 ? 'n/a' : fInt(explorerScore) + ' points')));
            d3.select('#clumpScoreValue').html((clumpScore == -1 ? 'Please wait...' : (clumpScore == -2 ? 'n/a' : fInt(clumpScore) + ' points')));
        } else {
            d3.select('#explorerScoreValue').html('<span title="Explorer score is not available for filtered views. Remove all filters (press reset button) to see your Explorer score.">n/a</span>');
            d3.select('#clumpScoreValue').html('<span title="Max cluster score is not available for filtered views. Remove all filters (press reset button) to see your Max cluster score.">n/a</span>');
        }
    } else {
        window.setTimeout(updateExplorerScore, 100);
    }
}

// chart tooltip tidy
//window.setInterval(function() {
//d3.selectAll('.nvtooltip:not(#nvtooltip-calTooltip):not(:last-child)').remove();
//}, 1000);
