/*
Copyright 2012-2026 VeloViewer. All Rights Reserved
###V83###
*/
var curAth, map, us, s, sd, sdo, sdl, lis, mapResizeTimer, sdloading = false,
    sdSort = 'Leaderboard',
    compareDetailLevel = 0.2,
    selectedSegment = null,
    am, cs, o, aall = null,
    segs = [],
    checkTime = 30,
    resizeTimer, stBatchSize = 50,
    segAsyncCount = 0,
    errorShown = false,
    genderFilter = 'All';

function refreshData() {
    d3.select('#checkPlacingsWrapper').text('Updating leaderboard...');
    $.ajax({
        url: '/api/getSegmentHunterS3New.php?id=' + shid,
        async: true,
        tryCount: 0,
        retryLimit: 3,
        success: function(result) {
            var o = result;
            if (typeof(o.error) === 'undefined') {
                s = o;
                segs = [];
                processData();
                displayData();
            } else {
                console.debug(o.error);
            }
            d3.select('#checkPlacingsWrapper').text('');
            d3.select('#checkPlacingsWrapper').style('display', 'none');
        },
        error: function(xhr, textStatus, errorThrown) {
            handleAjaxError(xhr, textStatus, errorThrown, this);
        }
    });
}

function handleSegmentData(result) {
    s = result;

    processData();
    displayData();
}

function checkPlacings() {
    /*if (!isPRO) {
        alert('PRO users only');
        return;
    }*/
    if (myClubs.indexOf(s.clubId) == -1) {
        alert('Not in club');
        return;
    }
    segAsyncCount = 0;
    d3.select('#checkPlacingsWrapper').text('Checking... 0 of ' + s.segments.length);
    s.segments.forEach(function(d) {
        segAsyncCount++;
        $.ajax({
            url: '/api/getHunterSegmentEfforts.php?id=' + d.id + (typeof(d.aid) == 'undefined' || d.aid == '' ? '' : '&aid=' + d.aid),
            async: true,
            success: function(result) {
                segAsyncCount--;
                d3.select('#checkPlacingsWrapper').text('Checking... ' + (s.segments.length - segAsyncCount) + ' of ' + s.segments.length);
                if (segAsyncCount == 0) {
                    refreshData();
                    if (hasLocalStorage) {
                        localStorage.setItem('shlc' + s.id, new Date().getTime());
                    }
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                segAsyncCount--;
                d3.select('#checkPlacingsWrapper').text('Checking... ' + (s.segments.length - segAsyncCount) + ' of ' + s.segments.length);
                if (segAsyncCount == 0) {
                    refreshData();
                    d3.select('#checkPlacingsWrapper').style('display', 'none');
                    if (hasLocalStorage) {
                        localStorage.setItem('shlc' + s.id, new Date().getTime());
                    }
                }
                console.debug(xhr);
            }
        });
    })
}

function getSegments() {
    $.ajax({
        url: 'https://s3.veloviewer.com/' + shurl,
        turl: shurl,
        async: true,
        jsonpCallback: 'handleSegmentData',
        contentType: "application/json",
        dataType: 'jsonp',
        error: function(xhr, textStatus, errorThrown) {
            if (textStatus != 'parsererror') {
                $.ajax({
                    parentCall: this,
                    parentXhr: xhr,
                    async: true,
                    url: '/api/checkName.php?n=' + this.turl,
                    success: function(d) {
                        if (d.status == 'success') {
                            this.parentCall.url = 'https://s3.veloviewer.com/' + d.name;
                            $.ajax(this.parentCall);
                        } else {
                            if (!errorShown) {
                                alert('Error getting data file, try refreshing the page.');
                                errorShown = true;
                            }
                        }
                    }
                });
            }
        }
    });
}

function checkClubs() {
    $.ajax({
        url: '/api/checkClubs.php',
        async: true,
        success: function(result) {
            if (typeof(result.error) === 'undefined') {
                myClubs = result;
                if (result.indexOf(s.clubId) > -1) {
                    document.location.reload();
                } else {
                    alert('You still don\'t seem to be a member of the "' + s.clubName + '" Strava club.');
                }
            } else {
                alert('Unable to get clubs :-(');
            }
        },
        error: function(xhr, textStatus, errorThrown) {
            alert('Unable to get clubs :-(');
        }
    });
}

var dtdol, dtdm, lineAnimationCounter, lineAnimationInterval = null,
    animationMarker = new L.circleMarker([0, 0], {
        fillColor: '#000',
        fillOpacity: 1,
        radius: 3,
        stroke: false
    }),
    animationMarkerOuter = new L.circleMarker([0, 0], {
        color: '#000',
        weight: 1,
        radius: 8,
        fillOpacity: 0
    });

function displayMapPosition(s, i) {
    var lsdo = (document.getElementById('alignScaleCB').checked ? sdo : s);
    animationMarker.addTo(map);
    animationMarkerOuter.addTo(map);
    animationMarker.setLatLng(s.latLngArr2[i]);
    animationMarkerOuter.setLatLng(s.latLngArr2[i]);
    s.posMarker.attr('cx', lsdo.xscale(s.distanceArr2[i]));
    s.posMarker.attr('cy', lsdo.yscale(s.elevationArr2[i]));
    s.posMarkerOuter.attr('cx', lsdo.xscale(s.distanceArr2[i]));
    s.posMarkerOuter.attr('cy', lsdo.yscale(s.elevationArr2[i]));
    s.svgDistText.html(fDist(s.distanceArr2[i]));
    s.svgElevText.html(fPercent1(s.gradientArr2[i]));
}

function startAnimation() {
    lineAnimationCounter = 0;
    if (selectedSegment == null) return;
    selectedSegment.posMarker.style('display', null);
    selectedSegment.posMarkerOuter.style('display', null);
    selectedSegment.svgDistText.style('display', null);
    selectedSegment.svgElevText.style('display', null);
    displayMapPosition(selectedSegment, lineAnimationCounter);
    if (lineAnimationInterval != null) {
        window.clearInterval(lineAnimationInterval);
    }
    lineAnimationInterval = window.setInterval(function() {
        if (selectedSegment == null) {
            stopAnimation();
            map.removeLayer(animationMarker);
            map.removeLayer(animationMarkerOuter);
            return;
        }
        displayMapPosition(selectedSegment, lineAnimationCounter);
        lineAnimationCounter += selectedSegment.animationIncrement;
        if (lineAnimationCounter >= selectedSegment.latLngArr2.length) {
            lineAnimationCounter = 0;
        }
    }, Math.max(10, 10000/selectedSegment.latLngArr2.length));
}

function stopAnimation() {
    window.clearInterval(lineAnimationInterval);
    lineAnimationInterval = null;
}

var smallStartIcon = L.icon({
    iconUrl: 'https://cf.veloviewer.com/img/live-start.svg',
    iconSize: [10, 10], // size of the icon
    iconAnchor: [0, 0]
});
var smallEndIcon = L.icon({
    iconUrl: 'https://cf.veloviewer.com/img/live-end.svg',
    iconSize: [10, 10], // size of the icon
    iconAnchor: [0, 0]
});
var bigStartIcon = L.icon({
    iconUrl: 'https://cf.veloviewer.com/img/live-start.svg',
    iconSize: [20, 20], // size of the icon
    iconAnchor: [0, 0]
});
var bigEndIcon = L.icon({
    iconUrl: 'https://cf.veloviewer.com/img/live-end.svg',
    iconSize: [20, 20], // size of the icon
    iconAnchor: [0, 0]
});

function clickSegmentDetail(d, scroll) {
    var li = d3.select('#segmentDetailsLi' + d.segmentId);
    stopAnimation();
    map.removeLayer(animationMarker);
    map.removeLayer(animationMarkerOuter);
    if (selectedSegment != null) {
        selectedSegment.posMarker.style('display', 'none');
        selectedSegment.posMarkerOuter.style('display', 'none');
        selectedSegment.svgDistText.style('display', 'none');
        selectedSegment.svgElevText.style('display', 'none');
    }
    if (selectedSegment != null && selectedSegment.segmentId == d.segmentId) {
        //li.style('background', 'white');
        lis.style('opacity', null);
        selectedSegment = null;
        setMapBounds();
        d3.select('#resetListBtn').style('display', 'none');
    } else {
        lis.style('opacity', 0.5);
        li.style('opacity', null);
        d3.select('#resetListBtn').style('display', null);
        //d3.select('#segmentDetailsLi' + selectedSegment).style('background', 'white');
        //li.style('background', '#f5d4d9');
        selectedSegment = d;
    }
    sdl.forEach(function(d) {
        if (selectedSegment != null) {
            if (d.segmentId == selectedSegment.segmentId) {
                d.simpleLine.setStyle(lm_mapLineBGDef);
                d.startMarker.setOpacity(1);
                d.endMarker.setOpacity(1);
                d.startMarker.setIcon(bigStartIcon);
                d.endMarker.setIcon(bigEndIcon);
            } else {
                d.simpleLine.setStyle(lm_mapLine);
                d.simpleLine.setStyle({ opacity: 0.5 });
                d.startMarker.setOpacity(0.5);
                d.endMarker.setOpacity(0.5);
                d.startMarker.setIcon(smallStartIcon);
                d.endMarker.setIcon(smallEndIcon);
                if (typeof(d.gradLines) !== 'undefined') {
                    d.gradLines.forEach(function(e) {
                        map.removeLayer(e);
                    });
                }
            }
        } else {
            d.simpleLine.setStyle(lm_mapLine);
            d.startMarker.setOpacity(1);
            d.endMarker.setOpacity(1);
            d.startMarker.setIcon(smallStartIcon);
            d.endMarker.setIcon(smallEndIcon);
            if (typeof(d.gradLines) !== 'undefined') {
                d.gradLines.forEach(function(e) {
                    map.removeLayer(e);
                });
            }
        }
    });
    if (selectedSegment != null) {
        map.removeLayer(d.simpleLine);
        map.removeLayer(d.startMarker);
        map.removeLayer(d.endMarker);
        map.addLayer(d.simpleLine);
        if (typeof(d.gradLines) === 'undefined') {
            segment = d;
            bdsmel = s.bdsmel;
            d.gradLines = setMapDetail('L', map);
            d.bdsmel = bdsmel;
        } else {
            d.gradLines.forEach(function(e) {
                map.addLayer(e);
            });
        }
        map.addLayer(d.startMarker);
        map.addLayer(d.endMarker);
        setMapBounds(d);
        startAnimation();
    }
    if (scroll) {
        li.node().scrollIntoView();
    }
}

function fillGaps(o, maxGap) {
  if (o.latLngArr2.length > 500) return;
    maxGap = typeof(maxGap) !== 'undefined' ? maxGap : Math.max(5,o.distance/500);
    for (var i = 1; i < o.distanceArr2.length; i++) {
        var dd = o.distanceArr2[i] - o.distanceArr2[i - 1];
        if (dd == 0) {
            o.distanceArr2.splice(i, 1);
            o.elevationArr2.splice(i, 1);
            o.latLngArr2.splice(i, 1);
            i--;
        } else {
            if (dd > maxGap * 1.5) {
                var num = Math.round(dd / maxGap);
                var gap = dd / num;
                var da = d3.range(o.distanceArr2[i - 1] + gap, o.distanceArr2[i] - 1, gap);
                var args = [i, 0].concat(da);
                Array.prototype.splice.apply(o.distanceArr2, args);
                args = [i, 0].concat(da.map(function(d) { return o.es(d) }));
                Array.prototype.splice.apply(o.elevationArr2, args);
                args = [i, 0].concat(da.map(function(d) { return { lat: o.lats(d), lng: o.lngs(d) } }));
                Array.prototype.splice.apply(o.latLngArr2, args);
            }
        }
    }
}

function segmentProfileMove(e, preventPropogation) {
    if (selectedSegment != null && selectedSegment.segmentId == e.segmentId) {
        preventPropogation = typeof(preventPropogation) === 'undefined' ? true : preventPropogation;
        stopAnimation();
        var i = Math.min(selectedSegment.distanceArr2.length - 1, d3.bisectLeft(selectedSegment.distanceArr2, (document.getElementById('alignScaleCB').checked ? sdo : selectedSegment).xscale.invert(d3.event.layerX)));
        displayMapPosition(selectedSegment, i);
        if (preventPropogation) {
            if (d3.event.stopPropagation) d3.event.stopPropagation();
            if (d3.event.preventDefault) d3.event.preventDefault();
            d3.event.cancelBubble = true;
            d3.event.returnValue = false;
            return false;
        }
    }
}

function populateDetailedInfo() {
    if (sd.length > 0 && typeof(sd[0].latLngArr2) === 'undefined') {
        sdo = {
            maxDistance: 0,
            minElev: 100000,
            maxElev: -100000,
            height: 100,
            marginTop: 5,
            width: document.getElementById('segmentDetailOL').getBoundingClientRect().width - 200
        };
        sd.forEach(function(d) {
            d.latExtent = [360, -360];
            d.lngExtent = [360, -360];
            d.distanceArr2 = decompress1(d.distanceArr, 1);
            d.elevationArr2 = decompress1(d.elevationArr, 1);
            d.latLngArr2 = decompressN(d.latLngArr, 5, 2);
            d.latLngArr2 = d.latLngArr2.slice(0, d.latLngArr2.length / 2).map(function(e, i) {
                var lat = d.latLngArr2[i * 2];
                var lng = d.latLngArr2[i * 2 + 1];
                if (lat < d.latExtent[0]) d.latExtent[0] = lat;
                if (lat > d.latExtent[1]) d.latExtent[1] = lat;
                if (lng < d.lngExtent[0]) d.lngExtent[0] = lng;
                if (lng > d.lngExtent[1]) d.lngExtent[1] = lng;
                return {
                    lat: +lat.toFixed(5),
                    lng: +lng.toFixed(5)
                }
            });

            // remove duplicate Points
            var lat = 0, lng = 0;
            for (var i=0; i < d.latLngArr2.length; i++) {
              while (i < d.latLngArr2.length && lat == d.latLngArr2[i].lat && lng == d.latLngArr2[i].lng) {
                d.latLngArr2.splice(i,1);
                d.distanceArr2.splice(i,1);
                d.elevationArr2.splice(i,1);
              }
              if (i < d.latLngArr2.length) {
                lat = d.latLngArr2[i].lat;
                lng = d.latLngArr2[i].lng;
              }
            }

            d.xy = filterData(d.distanceArr2.map(function(e, i) {
                return {
                    x: e,
                    y: d.elevationArr2[i],
                    i: i
                };
            })).map(function(e) {
                return { x: e.x, y: e.ySmooth, z: 0, i: e.i };
            });
            //d.xy2 = clone(simplify(d.xy, (d.distance / 20000) * compareDetailLevel / (2 * 0.03), false));
            d.bdsmel = d.xy.map(function(e) { return e.y });
            d.elevationArr2 = d.bdsmel;

            d.xy2 = d.xy;
            if (d.xy2.length >= sdo.width / 2) {
                var simpVal = 10;
                d.xy2 = simplify(d.xy, simpVal, true);
                while (d.xy2.length < sdo.width / 2 && simpVal > 0.01) {
                    simpVal = simpVal / 2;
                    d.xy2 = simplify(d.xy, simpVal, true);
                }
            }
            delete d.xy;

            d.es = d3.scale.linear().domain(d.distanceArr2).range(d.elevationArr2);
            d.lats = d3.scale.linear().domain(d.distanceArr2).range(d.latLngArr2.map(function(e) { return e.lat }));
            d.lngs = d3.scale.linear().domain(d.distanceArr2).range(d.latLngArr2.map(function(e) { return e.lng }));

            d.elevExt = d3.extent(d.elevationArr2);
            sdo.maxDistance = Math.max(sdo.maxDistance, d.distance);
            sdo.minElev = Math.min(sdo.minElev, d.elevExt[0]);
            sdo.maxElev = Math.max(sdo.maxElev, d.elevExt[1]);

            fillGaps(d);

            d.gradientArr2 = d.distanceArr2.map(function(e, i) {
                var i0 = i == 0 ? 0 : i - 1;
                var i1 = i == 0 ? 1 : i;
                return (d.elevationArr2[i1] - d.elevationArr2[i0]) / (d.distanceArr2[i1] - d.distanceArr2[i0]);
            });

            d.animationIncrement = Math.max(1, Math.round(d.latLngArr2.length / 1000));

            d.simpleLine = new L.Polyline(d.latLngArr2.map(function(e) { return [e.lat, e.lng] }), lm_mapLine);
            //d.simpleLine.setStyle({ opacity: 1, color: '#FF032E' });
            d.startMarker = L.marker([d.latLngArr2[0].lat, d.latLngArr2[0].lng], {
                icon: smallStartIcon
            });
            d.endMarker = L.marker([d.latLngArr2.last().lat, d.latLngArr2.last().lng], {
                icon: smallEndIcon
            });

            d.simpleLine.on('click', function(e) {
                if (selectedSegment == null || selectedSegment.segmentId != d.segmentId) {
                    clickSegmentDetail(d, true);
                }
            });
            d.startMarker.on('click', function(e) {
                if (selectedSegment == null || selectedSegment.segmentId != d.segmentId) {
                    clickSegmentDetail(d, true);
                }
            });
            d.endMarker.on('click', function(e) {
                if (selectedSegment == null || selectedSegment.segmentId != d.segmentId) {
                    clickSegmentDetail(d, true);
                }
            });
            d.xscale = d3.scale.linear().range([0, sdo.width]);
            d.yscale = d3.scale.linear().range([sdo.height, sdo.marginTop]);
            d.line = d3.svg.line()
                //.interpolate("basis")
                .x(function(e) {
                    return d.xscale(e.x);
                })
                .y(function(e) {
                    return d.yscale(e.y);
                });
            d.xscale.domain([0, d.distance]);
            var ymaxAdd = Math.max(0, (d.distance / 100) + d.elevExt[0] - d.elevExt[1]);
            d.yscale.domain([d.elevExt[0] - (d.elevExt[1] - d.elevExt[0]) / 10, d.elevExt[1] + ymaxAdd]);
        });
        sdo.xscale = d3.scale.linear().range([0, sdo.width]);
        sdo.yscale = d3.scale.linear().range([sdo.height, sdo.marginTop]);
        sdo.line = d3.svg.line()
            //.interpolate("basis")
            .x(function(e) {
                return sdo.xscale(e.x);
            })
            .y(function(e) {
                return sdo.yscale(e.y);
            });
        sdo.xscale.domain([0, sdo.maxDistance]);
        var ymaxAdd = Math.max(0, (sdo.maxDistance / 100) + sdo.minElev - sdo.maxElev);
        sdo.yscale.domain([sdo.minElev - (sdo.maxElev - sdo.minElev) / 10, sdo.maxElev + ymaxAdd]);
    }

    sdl = sd.sort(function(a, b) {
        switch (sdSort) {
            case 'Leaderboard':
                return a.orderBy - b.orderBy;
            case 'VVOM ↑':
                return a.vvom - b.vvom;
            case 'VVOM ↓':
                return b.vvom - a.vvom;
            case 'Distance ↑':
                return a.distance - b.distance;
            case 'Distance ↓':
                return b.distance - a.distance;
            case 'Elevation gain ↑':
                return a.elevationGain - b.elevationGain;
            case 'Elevation gain ↓':
                return b.elevationGain - a.elevationGain;
        }
    }); //sort

    lis = dtdol.selectAll('li').data(sdl, function(d) { return d.segmentId });
    lis.exit().remove().each(function(d) {
        map.removeLayer(d.simpleLine);
        map.removeLayer(d.startMarker);
        map.removeLayer(d.endMarker);
    });
    lis.enter().append('li')
        .attr('id', function(d) { return 'segmentDetailsLi' + d.segmentId }).style({
            'background': 'white',
            'border': '#bbb 1px solid',
            'padding': '10px 10px',
            'margin-bottom': '4px',
            'position': 'relative'
        }).each(function(d) {
            var li = d3.select(this).on('click', function(d) {
                clickSegmentDetail(d);
            });
            var tr = li.append('div').style({
                'float': 'right',
                'background': '#f4f4f4',
                'margin': '-10px -10px 0px 0px',
                'padding': '5px',
                'border': 'solid #bbb',
                'border-width': '0px 0px 1px 1px',
                'border-radius': '0px 0px 0px 4px'
            });
            if (typeof(curAth['seg' + d.segmentId]) === 'undefined') {
                tr.append('div').style({
                    'font-size': '2em',
                    'line-height': 'normal',
                    'text-align': 'right'
                }).html('&nbsp;-&nbsp;');
            } else {
                tr.append('div').style({
                    'font-size': '2em',
                    'line-height': 'normal',
                    'text-align': 'right'
                }).html(suf(curAth['seg' + d.segmentId].r));
                tr.append('div').style({
                    'font-size': '1.3em',
                    'line-height': 'normal',
                    'text-align': 'right'
                }).html(curAth['seg' + d.segmentId].t.toDayHrMinSec2());
            }
            tr.append('div');
            li.append('h2').html(d.name.replace(/ *OFFICIAL .*Climbs\S*/i, '').replace(' Hellingen App', '').replace(' (Zwift Insider verified)', '').replace(' (Watopia Climb Portal)', '').replace(' Greatest TDF Climbs', '').replace('The Cycling Podcast "Our Giro" 2020 - ', '').replace(' (Shortened due to virtual landslide)', '').replace('"OurGiro" ', '').replace(' - RGT', ''));
            li.append('div').html('VVOM:&nbsp;' + f2dp(d.vvom) + '<br/>Distance:&nbsp;' + fDist(d.distance).replace(/ /g, '&nbsp;') + '<br/>Elevation&nbsp;gain:&nbsp;' + fSmlDist(d.elevationGain).replace(/ /g, '&nbsp;'));
            li.append('div').append('a').attr({
                'target': '_blank',
                'href': function(d) { return '/segments/' + d.segmentId }
            }).html('View on VeloViewer');
            var div = li.append('div').style({
                'margin-bottom': '5px'
            }).each(function(d) {
                var item = d3.select(this);
                item.append('a').attr({
                    'target': '_blank',
                    'href': 'https://www.strava.com/segments/' + d.segmentId
                }).html('View on Strava');
                var aida = s.segments.filter(function(e) { return e.id == d.segmentId});
                if (aida.length > 0 && typeof(aida[0].aid) != 'undefined' && aida[0].aid != '') {
                    item.append('span').html(' (<a target="_blank" href="https://www.strava.com/segments/' + aida[0].aid + '">France</a>)');
                }
            });
            var kom = li.append('div').style({
                'float': 'left',
                'background': '#f4f4f4',
                'margin': '0px 0px -10px -10px',
                'padding': '5px',
                'border': 'solid #bbb',
                'border-width': '1px 1px 0px 0px',
                'border-radius': '0px 0px 0px 0px',
                'position': 'relative'
            });
            kom.append('div').style({
                'font-size': '1.1em',
                'line-height': 'normal',
                'position': 'absolute',
                'transform': 'rotate(-90deg) translate(-100%)',
                'transform-origin': 'left top',
                'font-weight': 'bold'
            }).html('KOM');
            var komd = kom.append('div').style({
                'margin-left': '2em'
            })
            komd.append('div').style({
                'font-size': '1.3em',
                'text-align': 'right'
            }).html(d.komElapsedTime == null ? 'n/a' : d.komElapsedTime.toDayHrMinSec2());
            komd.append('div').style({
                'text-align': 'right'
            }).html(d.komAthleteCount == null ? '&nbsp;' : fInt(d.komAthleteCount));
            var qom = li.append('div').style({
                'float': 'left',
                'background': '#f4f4f4',
                'margin': '0px 0px -10px 0px',
                'padding': '5px',
                'border': 'solid #bbb',
                'border-width': '1px 1px 0px 0px',
                'border-radius': '0px 0px 0px 0px',
                'position': 'relative'
            });
            qom.append('div').style({
                'font-size': '1.1em',
                'line-height': 'normal',
                'position': 'absolute',
                'transform': 'rotate(-90deg) translate(-100%)',
                'transform-origin': 'left top',
                'font-weight': 'bold'
            }).html('QOM');
            var qomd = qom.append('div').style({
                'margin-left': '2em'
            });
            qomd.append('div').style({
                'font-size': '1.3em',
                'text-align': 'right'
            }).html(d.qomElapsedTime == null ? 'n/a' : d.qomElapsedTime.toDayHrMinSec2());
            qomd.append('div').style({
                'text-align': 'right'
            }).html(d.qomAthleteCount == null ? '&nbsp;' : fInt(d.qomAthleteCount));
            li.append('div').style('clear', 'both');

            d.svg = li.append('svg').style({
                'height': sdo.height + 'px',
                'width': sdo.width + 'px',
                'position': 'absolute',
                'bottom': '0px',
                'right': '0px',
                'border': 'solid #bbb',
                'border-width': '1px 0px 0px 1px',
                'border-top-left-radius': '4px',
                'background': 'rgb(244, 244, 244)'
            }).on('mousemove', function(e) {
                segmentProfileMove(e);
            }).on('mouseout', function(e) {
                if (selectedSegment != null && selectedSegment.segmentId == e.segmentId) {
                    var evt = d3.event.toElement || d3.event.relatedTarget;
                    if (evt != null && (evt.parentNode == this || evt == this)) {
                        return;
                    }
                    startAnimation();
                }
            }).on('touchstart', function(e) {
                segmentProfileMove(e);
            }).on('touchmove', function(e) {
                segmentProfileMove(e);
            }).on('touchend', function(e) {
                if (selectedSegment != null && selectedSegment.segmentId == e.segmentId) {
                    var evt = d3.event.toElement || d3.event.relatedTarget;
                    if (evt != null && (evt.parentNode == this || evt == this)) {
                        return;
                    }
                    startAnimation();
                }
            });

            d.svg.selectAll('path.grad').data(d.xy2.slice(1)).enter().append('path')
                .classed('grad', true)
                .datum(function(e, i) {
                    return [
                        { x: d.xy2[i].x, y: sdo.yscale.domain()[0] },
                        { x: d.xy2[i].x, y: d.xy2[i].y },
                        { x: e.x, y: e.y },
                        { x: e.x, y: sdo.yscale.domain()[0] }
                    ]
                }).style({
                    'fill': function(e, i) {
                        return colScale2((e[2].y - e[1].y) / (e[2].x - e[1].x));
                    },
                    'stroke-width': '0'
                });

            d.svg.append('path')
                .datum(d.xy2)
                .style({
                    'fill': 'transparent',
                    'stroke': '#000',
                    'stroke-width': '0.5px'
                });

            d.posMarker = d.svg.append('circle').attr({
                'r': 2
            }).style({
                'display': 'none',
                'fill': '#000'
            });
            d.posMarkerOuter = d.svg.append('circle').attr({
                'r': 5
            }).style({
                'display': 'none',
                'stroke': '#000',
                'stroke-width': '1px',
                'fill': 'transparent'
            });

            d.svgDistText = d.svg.append('text').attr({
                'x': 50,
                'y': 15,
                'text-anchor': 'end'
            }).style({
                'display': 'none'
            })

            d.svgElevText = d.svg.append('text').attr({
                'x': 90,
                'y': 15,
                'text-anchor': 'end'
            }).style({
                'display': 'none'
            })

            map.addLayer(d.simpleLine);
            map.addLayer(d.startMarker);
            map.addLayer(d.endMarker);
        });

    lis.each(function(d) {
        if (document.getElementById('alignScaleCB').checked) {
            d.svg.selectAll('path').attr('d', sdo.line);
        } else {
            d.svg.selectAll('path').attr('d', d.line);
        }

    })

    lis.order();
}

function setMapBounds(obj) {
    var minLat, minLng, maxLat, maxLng;
    if (typeof(obj) === 'undefined') {
        minLat = d3.min(sdl, function(d) { return d.latExtent[0] });
        maxLat = d3.max(sdl, function(d) { return d.latExtent[1] });
        minLng = d3.min(sdl, function(d) { return d.lngExtent[0] });
        maxLng = d3.max(sdl, function(d) { return d.lngExtent[1] });
    } else {
        minLat = obj.latExtent[0];
        maxLat = obj.latExtent[1];
        minLng = obj.lngExtent[0];
        maxLng = obj.lngExtent[1];
    }
    map.fitBounds([
        [minLat, minLng],
        [maxLat, maxLng]
    ]);
    setMapSize();
}

function setMapSize() {
    var height = window.innerWidth < 768 ? 400 : Math.max(400, window.innerHeight - document.getElementById('mapDomObj').getBoundingClientRect().top - 62);
    d3.select('#mapDomObj')
        .style('height', height + 'px');

    height = window.innerWidth < 768 ? 400 : Math.max(400, window.innerHeight - document.getElementById('segmentDetailOL').getBoundingClientRect().top - 62);
    dtdol.style('height', height + 'px');

    if (map) {
        map.invalidateSize();
    }

    var sdoWidth = document.getElementById('segmentDetailOL').getBoundingClientRect().width - 200;
    if (typeof(sdo) !== 'undefined' && sdo.width != sdoWidth) {
        sdo.width = sdoWidth;

        dtdol.selectAll('li svg').style('width', sdo.width + 'px');
        sdo.xscale.range([0, sdo.width]);
        sd.forEach(function(d) {
            d.xscale.range([0, sdo.width]);
        });
        if (document.getElementById('alignScaleCB').checked) {
            dtdol.selectAll('li path').attr('d', sdo.line);
        } else {
            dtdol.selectAll('li path').each(function(d) {
                d3.select(this).attr('d', this.parentElement.__data__.line);
            });
        }
    }
}

function processData() {
    if (s.seriesAlias != null && s.seriesAlias.indexOf('zwift') == 0) {
        lm_ZwiftMapsMarker = true;
    }

    a = {};

    if (loggedInAthleteId > 0 && gender != '') {
        if (s.athletes[loggedInAthleteId]) {
            s.athletes[loggedInAthleteId].g = gender;
        }
    }

    if (!s_s) {
        s.segments.forEach(function(d) {
            d.result.entries.forEach(function(e) {
                e.t = 0;
            });
        });
    }

    s.segments.forEach(function(d){
        d.result.entries.sort(function(a,b){ return a.t == b.t ? +a.i - b.i : a.t - b.t })
    });
    
    var completedLength = s.segments.filter(function(d) { return d.result.entries.length }).length;

    s.segments.forEach(function(d) {
        segs.push({
            'id': d.id,
            'name': d.name,
            'kom': d.kom,
            'qom': d.qom,
            'rating': d.rating
        });
        cs = d.id;
        if (typeof(d.result) !== 'undefined') {
            if (genderFilter == 'All') {
                d.result.entriesF = d.result.entries;
            } else {
                d.result.entriesF = d.result.entries.filter(function(f) { return s.athletes[f.i].g == genderFilter || (genderFilter == 'M' && s.athletes[f.i].g == '') || (genderFilter == 'F' && s.athletes[f.i].g == 'G') });
            }
            d.result.entriesF.sort(function(a, b) { return a.t - b.t });

            d.result.entriesF.forEach(function(e, i) {
                if (typeof(a[e.i]) === 'undefined') {
                    a[e.i] = {
                        'id': e.i,
                        'name': s.athletes[e.i].n.replace(/\\/g, ''),
                        'gender': s.athletes[e.i].g == '' ? 'M' : s.athletes[e.i].g,
                        'countBonus': 0,
                        'kqomBonus': 0,
                        'timeTotal': 0,
                        'placingScore': 0,
                        'totalScore': 0,
                        'segs': 0
                    };
                    if ((typeof(us) === 'undefined' || us == null) && a[e.i].id == contextAthleteId) {
                        us = a[e.id];
                    }
                }
                if (typeof(a[e.i]['seg' + cs]) === 'undefined') {
                    a[e.i].segs++;
                }
                e.r = i > 0 && d.result.entries[i - 1].t == e.t ? d.result.entries[i - 1].r : i + 1;
                a[e.i]['seg' + cs] = e;
                //a[e.i].placingScore += Math.max(0, s.placingScore + 1 - e.rank);
                a[e.i]['seg' + cs].isKom = s_s && (typeof(d.aid) === 'undefined' || d.aid == '') ? (a[e.i].gender == 'M' && e.t <= d.kom) || (a[e.i].gender != 'M' && e.t <= d.qom) : false;
                if (a[e.i]['seg' + cs].isKom) {
                    if (a[e.i].gender == 'M') d.kom = e.t;
                    if (a[e.i].gender != 'M') d.qom = e.t;
                }
                a[e.i]['seg' + cs].kqomBonus = (a[e.i]['seg' + cs].isKom ? s.kqomBonus : 0);
                a[e.i]['seg' + cs].score = s_s ? (d.result.entriesF.length + 1 - e.r) * (100 / (d.result.entriesF.length + 1)) : 0;
                a[e.i].placingScore += s.placingScore == 0 ? 0 : a[e.i]['seg' + cs].score;
                a[e.i].kqomBonus += a[e.i]['seg' + cs].kqomBonus;
                if (a[e.i].segs >= s.segCountBonusFrom) {
                    a[e.i].countBonus = a[e.i].segs * s.segCountBonus;
                }
                a[e.i].timeTotal += e.t;
                a[e.i].score = typeof(a[e.i].r) === 'undefined' ? 0 : (d.result.entriesF.length + 1 - a[e.i].r) * (100 / (d.result.entriesF.length + 1));
            });
        }
    });

    if (s.id == 122) {
        d3.values(a).forEach(function(d) {
            d.gcTime = 0;
            if (completedLength == d.segs) {
                for (var att in d) {
                    if (att.match(/^seg[1-9]/) != null) {
                        d.gcTime += d[att].t;
                    }
                }
            }
            d.gcTime = d.gcTime == 0 ? 1000000 : d.gcTime;
        });
    }

    if (aall == null) {
        aall = a;
    }
    a = d3.values(a);
    a.forEach(function(d) {
        d.timeTotal = s.segments.length == d.segs ? d.timeTotal : -1;
        d.bonusTotal = d.countBonus + d.kqomBonus;
        d.totalScore = d.placingScore + d.countBonus + d.kqomBonus;
    });

    if (s.title == null) {
        refreshData();
        return;
    }

    d3.select('#title').html(s.clubName + ' - ' + s.title.replace('NE Derbyshire and S Yorkshire - ', ''));

    var html = s.desc != null ? s.desc : '';
    if (s.id != 122 && s.id != 131) {
        html += (s.desc != null ? ' - ' : '');
        if (s.year == null && s.month == null && s.day == null) {
            s.startDate = new Date(1980, 0, 1);
            s.endDate = new Date(3000, 0, 0);
            //html += 'From ' + fDate(s.startDate) + ' to ' + fDate(s.endDate) + ' - ';
        }
        if (s.year != null && s.month == null && s.day == null) {
            s.startDate = new Date(s.year, 0, 1);
            s.endDate = new Date(s.year + 1, 0, 0);
            html += 'From ' + fDate(s.startDate) + ' to ' + fDate(s.endDate) + ' - ';
        }
        if (s.year != null && s.month != null && s.day == null) {
            s.startDate = new Date(s.year, s.month - 1, 1);
            s.endDate = new Date(s.year, s.month, 0);
            html += 'From ' + fDate(s.startDate) + ' to ' + fDate(s.endDate) + ' - ';
        }
        if (s.year != null && s.month != null && s.day != null) {
            s.startDate = new Date(s.year, s.month - 1, s.day);
            s.endDate = new Date(s.year, s.month - 1, s.day);
            html += 'On ' + fDate(new Date(s.year, s.month - 1, s.day)) + ' - ';
        }
        s.startDate = s.startDate.getTime();
        s.endDate = s.endDate.getTime() + 1000 * 60 * 60 * 24;

        html += '<a href="' + (typeof(s.seriesAlias) !== 'undefined' && s.seriesAlias != '' && s.seriesAlias != null ? '/' + s.seriesAlias : '/segmentHunterSeries/' + (shid <= 7 && shid >= 5 ? 2 : (shid <= 4 ? 1 : s.seriesId))) + '">View the series leaderboard</a>'; //<span id="checkingMsg" title="The leaderboard will update every 3 hours while it is still active."> Last updated: ' + (shurl.match(/\.([0-9]*)\./) ? fDateTime(new Date(+shurl.match(/\.([0-9]*)\./)[1] * 1000)) : fDateTime(new Date())) + '</span>';
        if (s.seriesId != 30) {
            html += ' <span class="hidden-phone"> - Data from Apple, Wahoo, Garmin, Suunto, Coros, etc. devices.</span>';
        }
    }

    d3.select('#hunterDesc').classed('lead', false).html(html);

    /*d3.select('#stage3').remove();

    if (s.id == 122) {
        d3.select(d3.select('#hunterDesc').node().parentElement).append('p').attr('id', 'stage3').classed('alert alert-info', true).html('<b>Stage 3 - </b> shortened due to virtual landslide, <a href="https://www.facebook.com/groups/roadgrandtoursusers/permalink/2876927209039862/" target="_blank">More info here</a>.');
    }*/

    var segCheckP;
    if (!embed) {

        segCheckP = d3.selectAll('#checkPlacingsWrapper');
        segCheckP.selectAll('button.remove, span').remove();
        segCheckP.style('clear', 'both');

        if (d3.select('#detailsTab').html() == '') {
            var dtd = d3.select('#detailsTab').append('div').classed('row-fluid', true);
            var dtdl = dtd.append('div').classed('span6', true);

            if (!isPRO) {
                dtdl.append('p').classed('alert alert-info', true).html('Restricted to 3 segments for free users. <a href="/update">Upgrade to PRO</a> to see the details of all the leaderboard\'s segments and of course to import all of your Strava history into VeloViewer!');
            }

            var dtf = dtdl.append('form').classed('form-inline', true).style('margin-bottom', '0px');
            dtf.append('button').attr('id', 'resetListBtn').style({
                'display': 'none',
                'float': 'right'
            }).classed('btn btn-mini btn-primary', true).on('click', function() {
                if (selectedSegment != null) {
                    clickSegmentDetail(selectedSegment);
                }
                if (d3.event.stopPropagation) d3.event.stopPropagation();
                if (d3.event.preventDefault) d3.event.preventDefault();
                d3.event.cancelBubble = true;
                d3.event.returnValue = false;
                return false;
            }).html('<i class="icon-repeat icon-white"></i>')

            dtf.append('label').classed('checkbox', true).html('<input type="checkbox" id="alignScaleCB"> Align scale');

            d3.select('#alignScaleCB').on('click', function() {
                populateDetailedInfo();
            });

            dtf.append('label').html('Order by:').style('margin', '0px 4px 0px 15px');
            dtf.append('select').attr('id', 'sortOrderSelect').style({
                    'width': '9em',
                    'height': '22px',
                    'margin-bottom': '2px'
                }).on('change', function() {
                    sdSort = document.getElementById('sortOrderSelect').selectedOptions[0].value;
                    populateDetailedInfo();
                }).selectAll('option').data(['Leaderboard', 'Distance ↓', 'Distance ↑', 'Elevation gain ↓', 'Elevation gain ↑', 'VVOM ↓', 'VVOM ↑'])
                .enter().append('option').attr('value', function(d) { return d }).text(function(d) { return d });

            dtdol = dtdl.append('ol').style({
                'list-style-type': 'none',
                'margin-left': '0px',
                'height': '400px',
                'overflow-y': 'scroll',
                'border-top': '1px solid #bbb',
                'border-bottom': '1px solid #bbb'
            }).attr('id', 'segmentDetailOL');
            dtdm = dtd.append('div').classed('span6', true).attr('id', 'mapChartContainer')
                .append('div').attr('id', 'mapDomObj');

            setMapSize();

            $(window).resize(function() {
                window.clearTimeout(mapResizeTimer);
                mapResizeTimer = setTimeout(setMapSize, 200);
            });
            
            $('a[data-toggle="tab"][href="#leaderboardTab"]').on('shown', function(e) {
                setScrollingTableSize();
            });

            $('a[data-toggle="tab"][href="#detailsTab"]').on('shown', function(e) {
                if (typeof(sd) === 'undefined' && !sdloading) {
                    sdloading = true;
                    $.ajax({
                        url: '/api/getSegmentHunterFullDetails.php?id=' + s.id,
                        async: true,
                        success: function(result) {
                            sdloading = false;
                            if (typeof(result.error) === 'undefined') {
                                sd = result;
                                initMap('', false);
                                populateDetailedInfo();
                                setMapBounds();
                            } else {
                                alert('Trouble getting detailed info.');
                            }
                        },
                        error: function(xhr, textStatus, errorThrown) {
                            sdloading = false;
                            alert('Trouble getting detailed info.');
                        }
                    });
                } else {
                    setMapSize();
                }
            });
        }
    }
    //d3.select('#checkPlacingsWrapper').remove();
    //var segCheckP = d3.select('.container-fluid > .row-fluid').append('p').attr('id', 'checkPlacingsWrapper');

    var genderRadio = (embed ? d3.select('#titleWrapper') : segCheckP).selectAll('#genderRadio').data([0]).enter()
        .append('div').attr('id', 'genderRadio').style('float', 'right')
        .attr({
            'class': 'btn-group',
            'data-toggle': 'buttons-radio'
        }).each(function() {
            d3.select(this).selectAll('button').data(['All', 'M', 'F']).enter()
                .append('button').classed('btn btn-primary btn-mini', true).html(function(d) {
                    switch (d) {
                        case 'M':
                            return 'Male';
                        case 'F':
                            return 'Female';
                        case 'All':
                            return 'All';
                    }
                }).classed('active', function(d) { return d == 'All' });
        });

    genderRadio.selectAll('button').on('click', function(d) {
        genderFilter = d;
        segs = [];
        processData();
        displayData();
    });

    if (!embed && contextAthleteId > 0) {
        if (myClubs.indexOf(s.clubId) > -1) {
            if ((new Date().getTime() - localStorage.getItem('shlc' + s.id)) > 1000 * 60 * 30 - (s.id == 122 || s.id == 131 ? 1000 * 60 * 50 : 0)) {
                segCheckP.append('button')
                    .on('click', checkPlacings)
                    .classed('btn btn-primary btn-small remove', true)
                    .text(typeof(aall[contextAthleteId]) === 'undefined' ? 'Check your times' : 'Update your times');
                if (typeof(aall[contextAthleteId]) === 'undefined') {
                    segCheckP.append('span').style('margin-left', '5px').text('Checking your times implies you are happy for your name and data to be shown in these public leaderboards.');
                } else {
                    segCheckP.append('span').style('margin-left', '5px').text('Please only update when you have completed a new segment or improved a time :-)');
                }
            } else {
                segCheckP.append('span').text('Your placings can only be rechecked once every half hour.');
            }
        } else {
            segCheckP.classed('alert alert-info', true).html('You must be a member of the "' + s.clubName.replace(' Insider', '') + '" Strava club to appear in this leaderboard. Please <a href="https://www.strava.com/clubs/' + s.clubId + '" target="_blank">join the club</a> and then <button class="btn btn-primary btn-small" id="checkClubMembershipBtn">recheck club membership</button>');
            d3.select('#checkClubMembershipBtn').on('click', function() {
                checkClubs();
            });
        }
    }

    d3.selectAll('#dataTable tbody tr').remove();

    var cg = d3.select('#dataTable colgroup');

    cg.append('col').style('width', 'auto');
    cg.append('col').style('width', 'auto');
    cg.append('col').style('width', (100 / (segs.length + 1)) + '%');

    for (var i = 0; i < segs.length; i++) {
        cg.append('col').style('width', 'auto');
        cg.append('col').style('width', (100 / (segs.length + 1)) + '%');
    }

    cg.append('col').style('width', 'auto');

    var ths = d3.select('#dataTable thead tr')
        .selectAll('th')
        .data(segs, function(d) {
            return (typeof(d) !== 'undefined' ? d.id : 0)
        });

    ths.enter().append('th')
        .attr('colspan', '2')
        //.classed('ac', true)
        .html(function(d) {
            return '<a style="float:right" href="/segment/' + d.id + '" target="_blank">View</a>' + d.name.replace(/ *OFFICIAL .*Climbs\S*/i, '').replace(' Hellingen App', '').replace(' (Zwift Insider verified)', '').replace(' (Watopia Climb Portal)', '').replace(' Greatest TDF Climbs', '').replace('The Cycling Podcast "Our Giro" 2020 - ', '').replace(' (Shortened due to virtual landslide)', '').replace('"OurGiro" ', '').replace(' - RGT', '');
        });

    d3.selectAll('.totalTh').remove();

    d3.select('#dataTable thead tr').append('th').classed('totalTh ar', true).text('Time Total');
    d3.select('#dataTable thead tr').append('th').classed('totalTh ar', true).text('Bonus Total');
    d3.select('#dataTable thead tr').append('th').classed('totalTh ar', true).text('Placing Total');
    d3.select('#dataTable thead tr').append('th').classed('totalTh ar', true).text('Overall Total');

    if (s.id == 122) {
        d3.select('#dataTable thead tr').append('th').classed('totalTh ar', true).text('Time');
    }

    d3.selectAll('#dataTable th').style('background', function(d) {
        return typeof(s.colour) === 'undefined' || s.colour == null ? '#1b1b1b' : '#' + s.colour;
    });

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

    d3.selectAll('.tableWrapper.st th')
        .style('cursor', 'pointer')
        .on('click', function(d) {
            if (d3.event.srcElement.nodeName == 'A') return;
            d3.selectAll('.tableWrapper.st th')
                .classed('ascOrder', false)
                .classed('descOrder', false);

            d3.select(this)
                .classed('ascOrder', true);

            o = this;

            stBatchSize = 50;
            d3.selectAll('#dataTable tbody tr').remove();

            displayData();
        });
}

function getData() {
    var data = a;

    data = data.sort(function(a, b) { return b.totalScore - a.totalScore; });

    var ls = -1;
    var lsr = -1

    if (data[0] && typeof(data[0].r) === 'undefined') {
        for (var i = 0; i < data.length; i++) {
            if (data[i].totalScore == ls) {
                data[i].r = lsr;
            } else {
                data[i].r = i > 0 && data[i - 1].totalScore == data[i].totalScore ? data[i - 1].r : i + 1;
                ls = data[i].totalScore;
                lsr = i + 1;
            }
        }

        if (s.id == 122) {
            data = data.sort(function(a, b) { return a.gcTime - b.gcTime; });
            for (var i = 0; i < data.length; i++) {
                data[i].tr = i > 0 && data[i - 1].gcTime == data[i].gcTime ? data[i - 1].tr : i + 1;
            }
        }
    }

    data = data.sort(function(a, b) {
        if (o == null) {
            return b.totalScore - a.totalScore;
        }
        switch (typeof(o.innerText) === 'undefined' ? o.textContent : o.innerText) {
            case 'Rank':
            case 'Overall Total':
                return b.totalScore - a.totalScore;
                break;
            case 'Bonus Total':
                return (b.bonusTotal == a.bonusTotal ? b.totalScore - a.totalScore : b.bonusTotal - a.bonusTotal);
                break;
            case 'Placing Total':
                return b.placingScore - a.placingScore;
                break;
            case 'Time Total':
                return (a.timeTotal == -1 ? 1000000 : a.timeTotal) - (b.timeTotal == -1 ? 1000000 : b.timeTotal);
                break;
            case 'Time':
                return a.gcTime - b.gcTime;
                break;
            case 'Name':
                return (a.name < b.name ? -1 : 1);
                break;
            case '#':
            case 'Completed':
                return (b.segs == a.segs ? b.totalScore - a.totalScore : b.segs - a.segs);
                break;
            default:
                return ((typeof(a['seg' + o.__data__.id]) !== 'undefined' ? a['seg' + o.__data__.id].r : 10000) - (typeof(b['seg' + o.__data__.id]) !== 'undefined' ? b['seg' + o.__data__.id].r : 10000))
                return 0;
        }
    });

    return data;
}

function displayData() {
    var data = getData();
    s.athleteCount = data.length;
    curAth = data.filter(function(d) {
        return d.id == contextAthleteId
    });
    data = data.slice(stBatchNumber * stBatchSize, stBatchSize);
    if (curAth.length > 0) {
        curAth = clone(curAth[0]);
        curAth.id = -curAth.id;
        data = d3.merge([
            [curAth],
            [{
                id: 0
            }], data
        ]);
    }

    var trs = d3.select('#dataTable tbody')
        .selectAll('tr')
        .data(data, function(d) {
            return d.id
        }).enter().append('tr')
        .classed('info', function(d) {
            return Math.abs(d.id) == contextAthleteId
        });

    trs.append('td')
        .attr('title', function(d) {
            if (d.id == 0) return null;
            var score = (s.athleteCount + 1 - d.r) * (100 / (s.athleteCount + 1));
            return 'Position score: ' + f2dp(score) + ' (' + suf(d.r) + ' of ' + fInt(s.athleteCount) + ')';
        })
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : suf(d.r)
        });

    trs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : d.segs + ' / ' + segs.length; // + (d.segs >= s.segCountBonusFrom ? ' <span class="label label-warning" style="font-size:8px;font-weight:normal;padding:2px">' + d.countBonus + '</span>' : '');
        });

    trs.append('td')
        .attr('id', function(d) {
            return 'ath' + d.id
        })
        .style('max-width', '150px')
        .html(function(d) {
            //return '<a href="http://app.strava.com/athletes/' + d.id + '" target="_blank"><img class="shProfileImg" src="' + (d.img == 'avatar/athlete/large.png' ? 'http://d26ifou2tyrp3u.cloudfront.net/assets/avatar/athlete/large-162a16b208e0364313ec8ce239ffd7a8.png' : d.img) + '">' + d.name + (d.gender == 'F' ? ' (F)' : '') + '</a>'
            return d.id == 0 ? '&nbsp;' : '<a href="http://www.strava.com/athletes/' + Math.abs(d.id) + '" target="_blank">' + d.name + (d.gender == 'F' ? ' (F)' : '') + '</a>'
        });

    for (var i = 0; i < segs.length; i++) {
        trs.append('td')
            .style('padding-right', function(d) {
                if ((typeof(d['seg' + segs[i].id]) !== 'undefined') &&
                    (d['seg' + segs[i].id].isKom)) {
                    return '19px';
                } else {
                    return null;
                }
            })
            .attr('title', function(d) {
                if (typeof(d['seg' + segs[i].id]) !== 'undefined') {
                    return fDate(new Date(d['seg' + segs[i].id].w)) + (d['seg' + segs[i].id].score == 0 ? '' : ', position score: ' + f2dp(d['seg' + segs[i].id].score) + ' (' + suf(d['seg' + segs[i].id].r) + ' of ' + fInt(s.segments[i].result.entriesF.length) + ')');
                }
                return null;
            })
            .html(function(d) {
                if (typeof(d['seg' + segs[i].id]) !== 'undefined' && s_s) {
                    var content = suf(d['seg' + segs[i].id].r);

                    /*if (d['seg' + segs[i].id].kqomBonus > 0) {
                        content += ' <span class="label label-warning" style="font-size:8px;font-weight:normal;padding:2px">' + d['seg' + segs[i].id].kqomBonus + '</span>';
                    } else*/
                    if (d['seg' + segs[i].id].isKom) {
                        content += ' <img src="' + https + '://' + assetPrefix + '/img/kom.png">';
                    }

                    if (fDate(new Date()) == fDate(new Date(d['seg' + segs[i].id].w))) {
                        return '<span class="label label-success">' + content + '</span>';
                    } else {
                        return content;
                    }
                }
                return '&nbsp;';
            });

        trs.append('td')
            .style('border-left-width', '0px')
            .attr('title', function(d) {
                if (typeof(d['seg' + segs[i].id]) !== 'undefined') {
                    return fDate(new Date(d['seg' + segs[i].id].w)) + (d['seg' + segs[i].id].score == 0 ? '' : ', position score: ' + f2dp(d['seg' + segs[i].id].score) + ' (' + suf(d['seg' + segs[i].id].r) + ' of ' + fInt(s.segments[i].result.entriesF.length) + ')');
                }
                return null;
            })
            .html(function(d) {
                if (typeof(d['seg' + segs[i].id]) === 'undefined') return '&nbsp;';
                return (d['seg' + segs[i].id].ai == -1 ? (s_s ? d['seg' + segs[i].id].t.toDayHrMinSec2() : '✔') : '<a href="http://www.strava.com/activities/' + d['seg' + segs[i].id].ai + (d['seg' + segs[i].id].ei < 0 ? '' : '#' + d['seg' + segs[i].id].ei) + '" target="_blank">' + (s_s ? d['seg' + segs[i].id].t.toDayHrMinSec2() : '✔') + '</a>');
            });
    }

    trs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : d.timeTotal == -1 ? 'n/a' : d.timeTotal.toDayHrMinSec2();
        });

    trs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : d.bonusTotal;
        });

    trs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : f1dp(d.placingScore);
        });

    trs.append('td')
        .classed('ar', true)
        .html(function(d) {
            return d.id == 0 ? '&nbsp;' : f1dp(d.totalScore);
        });

    if (s.id == 122) {
        trs.append('td')
            .classed('ar', true)
            .html(function(d) {
                return d.id == 0 || d.gcTime == 1000000 ? '&nbsp;' : d.gcTime.toHrMinSec() + '&nbsp;<span style="min-width:35px;text-align:right;display:inline-block">(' + suf(d.tr) + ')</span>';
            });
    }

    d3.select('#dataInfo').html('Showing ' + Math.min(a.length, stBatchSize) + ' of ' + a.length);

    if (stBatchSize < a.length) {
        d3.selectAll('#loadNext50Btn, #loadAllBtn').style('display', null);
    } else {
        d3.selectAll('#loadNext50Btn, #loadAllBtn').style('display', 'none');
    }

    setScrollingTableSize();
}

getSegments();

if (shid >= 8 && shid <= 15) {
    d3.select('#dataTable thead th:nth-child(2)').text('Completed');
}

if (!s_s) {
    d3.select(d3.select('#hunterDesc').node().parentElement).insert('p', 'p').attr('id', 'summitLimits').classed('alert alert-info', true)
        .html('<b>Important note:</b> Segment times and 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>.');
}

d3.select('#tableFooter')
    .style('margin-top', '4px')
    .append('button')
    .attr('id', 'loadNext50Btn')
    .style('display', 'none')
    .classed('btn btn-primary', true)
    .text('Show next 50 rows')
    .on('click', function() {
        stBatchSize += 50;
        displayData();
    });

d3.select('#tableFooter')
    .append('button')
    .attr('id', 'loadAllBtn')
    .style('display', 'none')
    .classed('btn btn-primary', true)
    .text('Show all')
    .on('click', function() {
        stBatchSize = a.length;
        displayData();
    });

d3.select('#tableFooter')
    .append('span')
    .style('margin-left', '20px')
    .attr('id', 'dataInfo');


var infoUl = d3.select('#infoPanel');
infoUl.append('li').html('Full info on how these leaderboards work please see <a target="_blank" href="https://blog.veloviewer.com/updated-100-climbs-segment-hunter-strava-leaderboards/">this blog post</a>.');
infoUl.append('li').html('<span class="label label-success">4th</span> - Segment placings with this green background were achieved today. Go give them some Kudos!');
infoUl.append('li').html('Clicking on the column headers will reorder the rows.');
infoUl.append('li').html('Clicking on an athlete\'s time for a segment will take you to their activity\'s segment details page on Strava. Direct link to give your Kudos or leave some banter.');
infoUl.append('li').html('Clicking on an athlete\'s name will take you to their Strava profile.');
infoUl.append('li').append('div').attr('id', 'deleteSegmentHunterEffortsWrapper').append('button')
    .classed('btn btn-primary', true)
    .on('click', function() {
        if (confirm('Are you sure you want to delete your data from all of these club-based leaderboards?')) {
            d3.select('#deleteSegmentHunterEffortsWrapper').html('Deleting...');
            $.ajax({
                url: '/api/deleteSegmentHunterEfforts.php',
                async: true,
                success: function(result) {
                    if (typeof(result.error) === 'undefined') {
                        refreshData();
                        alert('All your data has been deleted from these leaderboards (although may remain in the cache for a couple of hours).');
                        d3.select('#deleteSegmentHunterEffortsWrapper').html('All your data has been deleted from these leaderboards (although may remain in the cache for a couple of hours).');
                    } else {
                        alert('Unable to delete data :-(');
                    }
                },
                error: function(xhr, textStatus, errorThrown) {
                    d3.select('#deleteSegmentHunterEffortsWrapper').html('Error deleting :-(');
                    alert('Unable to delete data :-(');
                }
            });
        }
    })
    .html('Delete all of your data from these leaderboards');


if (contextAthleteId <= 0 && d3.select('#hunterDesc').node() !== null) {
    d3.select(d3.select('#hunterDesc').node().parentElement)
        .insert('div', ':first-child').style({
            'margin-left': '-4px',
            'margin-top': '-7px'
        }).html('<a href="https://www.strava.com/oauth/authorize?client_id=36&scope=read,read_all,profile:read_all,profile:write,activity:read,activity:read_all,activity:write&redirect_uri=' + document.location.href + '&response_type=code&approval_prompt=auto&state=private"><img style="height:40px" src="https://cf.veloviewer.com/img/btn_strava_connectwith_orange.svg"></a> <div style="display: inline-block;vertical-align: middle;">to view your own position</div>')
}

function filterData(points, tau) {
    tau = tau || 30; //30 seconds default
    var directions = [-1, 1];
    var yPrev, xPrev, ySmooth;

    var xds = points[3].x - points[0].x;
    var yds = points[3].y - points[0].y;
    var xde = points[points.length - 1].x - points[points.length - 3].x;
    var yde = points[points.length - 1].y - points[points.length - 3].y;

    points = d3.merge([d3.range(0, tau).map(function(d) { return { x: points[0].x + (d - tau) * xds, y: points[0].y + (d - tau) * yds } }), points, d3.range(0, tau).map(function(d) { return { x: points.last().x + d * xde, y: points.last().y + d * yde } })]);

    for (var a = 0; a < directions.length; a++) {
        var direction = directions[a];

        for (var i = 0; i < points.length; i++) {
            var n = direction == 1 ? i : points.length - 1 - i;

            var x = points[n].x;
            var y = points[n].y;

            if (ySmooth == null || xPrev == null || yPrev == null) {
                ySmooth = y
            } else {
                var u = Math.abs(x - xPrev) / tau;
                var z = Math.exp(-u);
                var dy = y - yPrev;

                if (u > 0) {
                    ySmooth = ySmooth * z + y * (1 - z) + (dy / u) * ((u + 1) * z - 1);
                }
            }

            //trace([y, ySmooth])
            yPrev = y;
            xPrev = x;

            if (direction == -1) {
                points[n].ySmooth = ySmooth;
            } else {
                points[n].ySmooth = (points[n].ySmooth + ySmooth) / 2;
            }
        }
    }
    return points.slice(tau, points.length - tau);
}
