• gcgpxviewer.js

  • ¶
    gcgpxviewer.js 2.0.6
    https://gc-gpx-viewer.vaguelibre.net/
    (c) 2014 - Surfoo
    email: surfooo at gmail dot com 
    
    (function(_) {
  • ¶

    ‘use strict’;

        var typeCaches, sizeCaches, circle, control, map, parser, doc,
            objOptionLabel = document.getElementById('display_labels'),
            objOptionPerimeter = document.getElementById('display_perimeters'),
            circleList = [],
            polylineList = [],
            markers = [],
            circleOpacity = 0.8,
            circleColor = '#c11414',
            circleFillOpacity = 0.25,
            unitType = ['o', 'Ko', 'Mo', 'Go'];
  • ¶

    Types of geocaches from geocaching.com The key is the term used in the GPX file and the value is used for the filename below

        typeCaches = {
            'Traditional': 'type_traditional',
            'Traditional Cache': 'type_traditional',
            'Multi-cache': 'type_multi',
            'Multi-Cache': 'type_multi',
            'Virtual Cache': 'type_virtual',
            'Letterbox Hybrid': 'type_letterbox',
            'Event Cache': 'type_event',
            'Unknown Cache': 'type_mystery',
            'Mystery Cache': 'type_mystery',
            'Mystery': 'type_mystery',
            'Project APE Cache': 'type_ape',
            'Webcam Cache': 'type_webcam',
            'Cache In Trash Out Event': 'type_cito',
            'Earthcache': 'type_earth',
            'Mega-Event Cache': 'type_mega',
            'GPS Adventures Exhibit': 'type_event',
            'Wherigo Cache': 'type_wherigo',
            'Wherigo': 'type_wherigo',
            'Lost and Found Event Caches': 'type_event',
            'Groundspeak HQ': 'type_hq',
            'Groundspeak Lost and Found Celebration': 'type_event',
            'Community Celebration Event': 'type_event',
            'Groundspeak Block Party': 'type_event',
            'Giga-Event Cache': 'type_giga'
        };
  • ¶

    Size list geocaches

        sizeCaches = {
            'micro': 'Micro',
            'small': 'Small',
            'regular': 'Regular',
            'large': 'Large',
            'not_chosen': 'Not chosen',
            'not chosen': 'Not chosen',
            'unknown': 'Unknown',
            'other': 'Other',
            'virtual': 'Virtual'
        };
  • ¶

    Display geocaches on the map

        var displayCaches = function(wpts) {
            var icon, wpt, sym, latlng, oMarker, infoContent, i, nbWpts, grdspk, elmGccode, elmName, elmDifficulty,
                elmTerrain, elmOwner, elmContainer, elmType, elmDate, elmSize, iconFile;
  • ¶

    for each geocaches

            for (i = 0, nbWpts = wpts.length; i < nbWpts; ++i) {
                wpt = wpts[i];
                sym = wpt.getElementsByTagName('sym')[0].childNodes[0] || false;
                if (sym && (sym.nodeValue !== 'Geocache' &&
                        sym.nodeValue !== 'Geocache Found' &&
                        sym.nodeValue !== '')) {
                    continue;
                }
  • ¶

    Retrieve all informations in the waypoint

                grdspk = wpt.getElementsByTagNameNS('*', 'cache');
                /*console.log(grdspk[0].getAttribute('id'));*/
                elmGccode = wpt.getElementsByTagName('name')[0].childNodes[0].nodeValue;
                elmName = grdspk[0].getElementsByTagNameNS('*', 'name');
                elmDifficulty = grdspk[0].getElementsByTagNameNS('*', 'difficulty');
                elmDifficulty = elmDifficulty[0].childNodes[0].nodeValue;
                elmTerrain = grdspk[0].getElementsByTagNameNS('*', 'terrain');
                elmTerrain = elmTerrain[0].childNodes[0].nodeValue;
                elmOwner = grdspk[0].getElementsByTagNameNS('*', 'owner');
                elmContainer = grdspk[0].getElementsByTagNameNS('*', 'container');
                elmContainer = elmContainer[0].childNodes[0].nodeValue.toLowerCase();
                elmType = grdspk[0].getElementsByTagNameNS('*', 'type');
                elmType = elmType[0].childNodes[0].nodeValue;
                elmDate = new Date(wpt.getElementsByTagName('time')[0].childNodes[0].nodeValue);
                elmDate = elmDate.format('yyyy/mm/dd');
                elmSize = sizeCaches.unknown;
                iconFile = 'type_unknown';
  • ¶

    Search for the cache icon

                if (_.has(typeCaches, elmType)) {
                    iconFile = typeCaches[elmType];
                }
                icon = L.icon({
                    iconSize: [22, 22],
                    iconUrl: 'img/' + iconFile + '.map.png',
                    iconPopin: 'img/' + iconFile + '.png'
                });
  • ¶

    Search for the cache size

                if (_.has(sizeCaches, elmContainer)) {
                    elmSize = sizeCaches[elmContainer];
                }
    
                latlng = new L.latLng(parseFloat(wpt.getAttribute('lat')), parseFloat(wpt.getAttribute('lon')));
  • ¶

    Create the marker

                oMarker = L.marker(latlng, {
                    icon: icon
                }).bindLabel(elmGccode, {
                    opacity: objOptionLabel.checked ? 1 : 0,
                    noHide: true,
                    direction: 'auto'
                }).addTo(map);
  • ¶

    Content for the popup

                infoContent = '<div class="infowindow">';
                infoContent += '<div class="code">' + elmGccode + '</div>';
                infoContent += '    <h4>';
                infoContent += '        <img src="' + icon.options.iconPopin + '" width="20" alt="" />';
                infoContent += '        <a href="https://coord.info/' + encodeURIComponent(elmGccode) + '" onclick="window.open(this.href);return false;" title="' + elmName[0].childNodes[0].nodeValue + '">' + elmName[0].childNodes[0].nodeValue + '</a>';
                infoContent += '    </h4>';
                infoContent += '    <dl style="float:left;margin-right:2em;width:50%;">';
                if (elmOwner[0].childNodes[0]) {
                    infoContent += '        <dt>Created by:</dt>';
                    infoContent += '        <dd title="' + elmOwner[0].childNodes[0].nodeValue + '">';
                    infoContent += '<a href="https://www.geocaching.com/profile/?u=' + encodeURIComponent(elmOwner[0].childNodes[0].nodeValue) + '" onclick="window.open(this.href);return false;">' + elmOwner[0].childNodes[0].nodeValue + '</a></dd>';
                }
                infoContent += '        <dt>Difficulty:</dt>';
                infoContent += '        <dd>' + elmDifficulty + '</dd>';
                infoContent += '        <dt>Cache size:</dt>';
                infoContent += '        <dd>' + elmSize + '</dd>';
                infoContent += '    </dl>';
                infoContent += '    <dl style="margin-left:50%">';
                infoContent += '        <dt>Date Hidden:</dt>';
                infoContent += '        <dd>' + elmDate + '</dd>';
                infoContent += '        <dt>Terrain:</dt>';
                infoContent += '        <dd>' + elmTerrain + '</dd>';
                infoContent += '    </dl>';
                infoContent += '</div>';
    
                oMarker.bindPopup(L.popup({
                    maxWidth: 500
                }).setLatLng(latlng).setContent(infoContent));
                markers.push(oMarker);
  • ¶

    Set perimeter

                circle = new L.circle(latlng, 161, {
                    weight: 2,
                    color: circleColor,
                    opacity: objOptionPerimeter.checked ? circleOpacity : 0,
                    fillColor: objOptionPerimeter.checked ? circleColor : 'transparent',
                    fillOpacity: objOptionPerimeter.checked ? circleFillOpacity : 0,
                    clickable: false
                });
                circle.addTo(map);
                circleList.push(circle);
  • ¶

    Extends the bounds to contain the given point

                bounds.extend(latlng);
            }
        };
  • ¶

    Display tracks on the map

        var displayTracks = function(wpts) {
            var wpt, latlng, i = 0,
                j, k,
                nb = wpts.length,
                trksegs, trkseg, trkpts, trkpt, nbTrkSegs, nbTrkPts, path;
    
            for (i; i < nb; ++i) {
                wpt = wpts[i];
                trksegs = wpt.getElementsByTagName('trkseg');
    
                for (j = 0, nbTrkSegs = trksegs.length; j < nbTrkSegs; ++j) {
                    trkseg = trksegs[j];
                    path = [];
                    trkpts = trkseg.getElementsByTagName('trkpt');
                    for (k = 0, nbTrkPts = trkpts.length; k < nbTrkPts; ++k) {
                        trkpt = trkpts[k];
                        latlng = new L.latLng(parseFloat(trkpt.getAttribute('lat')), parseFloat(trkpt.getAttribute('lon')));
                        path.push(latlng);
                        bounds.extend(latlng);
                    }
                    var polyline = new L.Polyline(path, {
                        color: 'red'
                    });
                    polyline.addTo(map);
                    polylineList.push(polyline);
                }
            }
    
        };
  • ¶

    function to display informations on the maps, waypoints or tracks

        var display = function(data) {
            var waypoints = data.documentElement.getElementsByTagName('wpt'),
                trks = data.documentElement.getElementsByTagName('trk');
            if (waypoints.length === 0 && trks.length === 0) {
                alert('No waypoints found.');
            } else if (waypoints.length > 0) {
                displayCaches(waypoints);
            }
    
            if (waypoints.length === 0 && trks.length === 0) {
                alert('No tracks found.');
            } else if (trks.length > 0) {
                displayTracks(trks);
            }
    
            if (bounds.isValid()) {
                map.fitBounds(bounds);
            }
        };
  • ¶

    Toggle labels according to the old value

        var toggleLabels = function() {
            _.each(markers, function(value, key) {
                if (objOptionLabel.checked) {
                    markers[key].setOpacity(1, true);
                    markers[key].showLabel();
                } else {
                    markers[key].hideLabel();
                }
            });
    
            localStorage.setItem('option_label', +objOptionLabel.checked);
        };
  • ¶

    Toggle perimeters acccording to the old value

        var togglePerimeters = function() {
            _.each(markers, function(value, key) {
                circleList[key].setStyle({
                    opacity: objOptionPerimeter.checked ? circleOpacity : 0,
                    fillColor: objOptionPerimeter.checked ? circleColor : 'transparent',
                    fillOpacity: objOptionPerimeter.checked ? circleFillOpacity : 0
                });
            });
    
            localStorage.setItem('option_perimeter', +objOptionPerimeter.checked);
        };
  • ¶

    Clear the map by removing all layers

        var clear = function() {
            _.each(circleList, function(value, key) {
                map.removeLayer(circleList[key]);
            });
            _.each(markers, function(value, key) {
                map.removeLayer(markers[key]);
            });
            _.each(polylineList, function(value, key) {
                map.removeLayer(polylineList[key]);
            });
    
            circleList.length = 0;
            markers.length = 0;
            polylineList.length = 0;
            bounds = new L.LatLngBounds();
    
            var element = document.getElementById('list');
            while (element.firstChild) {
                element.removeChild(element.firstChild);
            }
        };
  • ¶

    Save the position and the zoom in localstorage

        var savePosition = function() {
            localStorage.setItem('zoom', map.getZoom());
            localStorage.setItem('latitude', map.getCenter().lat.toFixed(5));
            localStorage.setItem('longitude', map.getCenter().lng.toFixed(5));
        };
  • ¶

    Save the position of the sidebard (hidden or not) in localstorage

        var saveSidebar = function() {
            localStorage.setItem('sidebar', +sidebar.isVisible());
        };
  • ¶

    Save the current baselayer in localstorage

        var saveBaseLayer = function() {
            localStorage.setItem('baselayer', control.getActiveBaseLayer().name);
        };
    
        var bounds = new L.LatLngBounds();
  • ¶

    Init function to create the user interface

        var load = function() {
            var mapboxAccessToken = 'pk.eyJ1Ijoic3VyZm9vIiwiYSI6InpDWEQxRFkifQ.fclPYEBfTQBOmMGeUHJkKw';
    
            var mapboxLayer = L.tileLayer('https://{s}.tiles.mapbox.com/v4/surfoo.la14jo4j/{z}/{x}/{y}.png?access_token=' + mapboxAccessToken);
    
            var osmLegacyLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
                maxZoom: 18,
                minZoom: 3,
                attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://www.openstreetmap.org/">OpenStreetMap</a>'
            });
    
            var bingLayerAerial = new L.BingLayer("AlBwVzyGYtseSIzUAiDWBVI6k8OLGHVnEYuSfWSzNtQJ7eltNWiL1wCkdbD6T_JJ", {
                type: 'Aerial'
            });
    
            var bingLayerAerialWithLabels = new L.BingLayer("AlBwVzyGYtseSIzUAiDWBVI6k8OLGHVnEYuSfWSzNtQJ7eltNWiL1wCkdbD6T_JJ", {
                type: 'AerialWithLabels'
            });
    
            var bingLayerRoad = new L.BingLayer("AlBwVzyGYtseSIzUAiDWBVI6k8OLGHVnEYuSfWSzNtQJ7eltNWiL1wCkdbD6T_JJ", {
                type: 'Road'
            });
    
            var stamenToner = new L.StamenTileLayer("toner");
    
            var stamenWaterColor = new L.StamenTileLayer("watercolor");
  • ¶

    Default values

            var currentLatitude = 46,
                currentLongitude = 2.9,
                currentZoom = 6,
                currentSidebar = 1,
                currentBaseLayer = 'OSM Legacy',
                option_label = 1,
                option_perimeter = 1;
  • ¶

    List of base layers

            var baseLayers = {
                "OSM Legacy": osmLegacyLayer,
                "Bing Aerial": bingLayerAerial,
                "Bing Road": bingLayerRoad,
                "Bing Hybrid": bingLayerAerialWithLabels,
                "MapBox Light": mapboxLayer,
                "Stamen Toner": stamenToner,
                "Stamen WaterColor": stamenWaterColor
            };
    
            var overlays = {};
  • ¶

    Set some of default values for the map

            if (window.localStorage) {
                currentLatitude = _.isNull(localStorage.getItem('latitude')) ? currentLatitude : parseFloat(localStorage.getItem('latitude'));
                currentLongitude = _.isNull(localStorage.getItem('longitude')) ? currentLongitude : parseFloat(localStorage.getItem('longitude'));
                currentZoom = _.isNull(localStorage.getItem('zoom')) ? currentZoom : +localStorage.getItem('zoom');
                if (localStorage.getItem('baselayer') !== null && _.has(baseLayers, localStorage.getItem('baselayer'))) {
                    currentBaseLayer = localStorage.getItem('baselayer');
                }
                currentSidebar = localStorage.getItem('sidebar') === null ? currentSidebar : +localStorage.getItem('sidebar');
                option_label = localStorage.getItem('option_label') === null ? option_label : +localStorage.getItem('option_label');
                option_perimeter = localStorage.getItem('option_perimeter') === null ? option_perimeter : +localStorage.getItem('option_perimeter');
            }
  • ¶

    Create the map

            map = L.map('map', {
                center: new L.LatLng(currentLatitude, currentLongitude),
                zoom: currentZoom,
                layers: _.values(_.pick(baseLayers, currentBaseLayer)),
                attributionControl: false,
                zoomControl: true,
                inertia: false
            });
  • ¶

    Active Layers

            control = L.control.activeLayers(baseLayers, overlays);
            control.addTo(map);
  • ¶

    Scale options

            L.control.scale({
                'maxWidth': 200
            }).addTo(map);
  • ¶

    Sidebar options

            sidebar = L.control.sidebar('sidebar', {
                position: 'left',
                autoPan: false
            });
            map.addControl(sidebar);
            document.getElementById('sidebar').style.display = 'block';
            if (currentSidebar) {
                _.delay(function() {
                    sidebar.show();
                }, 250);
            }
            if (option_label) {
                objOptionLabel.checked = 'checked';
            }
            if (option_perimeter) {
                objOptionPerimeter.checked = 'checked';
            }
  • ¶

    Fullscreen options

            map.addControl(new L.Control.FullScreen());
  • ¶

    Geolocation options

            L.control.locate({
                drawCircle: false
            }).addTo(map);
  • ¶

    Options

            map.addControl(new L.Control.Options());
  • ¶

    EventListeners

            document.getElementById('clear').addEventListener('click', clear);
            objOptionLabel.addEventListener('change', toggleLabels);
            objOptionPerimeter.addEventListener('change', togglePerimeters);
            L.DomEvent.addListener(map, 'zoomend', savePosition);
            L.DomEvent.addListener(map, 'moveend', savePosition);
            L.DomEvent.addListener(map, 'baselayerchange', saveBaseLayer);
            sidebar.on('hide', saveSidebar);
            sidebar.on('show', saveSidebar);
        };
  • ¶

    App Cache, reload the web app by user request to get the latest version

        var onUpdateReady = function() {
            _.delay(function() {
                if (confirm('Geocaching GPX Viewer has been updated!\n\nDo you want to reload the page to use the new version?')) {
                    window.location.reload(true);
                }
            }, 0);
            return false;
        };
  • ¶

    Listener for App Cache

        window.applicationCache.addEventListener('updateready', onUpdateReady);
        if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
            onUpdateReady();
        }
  • ¶

    Flattr Button

        (function() {
            var f, s = document.getElementById('flattr');
            f = document.createElement('iframe');
            f.src = '//api.flattr.com/button/view/?uid=Surfoo&button=compact&url=' + encodeURIComponent(document.URL);
            f.title = 'Flattr';
            f.height = 20;
            f.width = 110;
            f.style.borderWidth = 0;
            f.style.verticalAlign = 'middle';
            s.parentNode.insertBefore(f, s);
        }());
  • ¶

    Output information

        var Output = function(msg) {
            var li = document.createElement('li'),
                list = document.getElementById('list');
            li.innerHTML = msg;
            list.appendChild(li);
        };
  • ¶

    Output file information

        var ParseFile = function(file) {
            var reader = new FileReader(),
                el = document.getElementById('loader'),
                fileinfo = [{
                    'name': file.name,
                    'size': file.size
                }];
    
            reader.onprogress = function() {
                el.style.display = 'block';
            };
    
            reader.onload = function(e) {
                if (window.DOMParser) {
                    parser = new DOMParser();
                    doc = parser.parseFromString(e.target.result, 'text/xml');
                } else {
                    doc = new ActiveXObject('Microsoft.XMLDOM');
                    doc.async = 'false';
                    doc.loadXML(e.target.result);
                }
    
                doc = parser.parseFromString(e.target.result, 'application/xml');
                if (!doc || doc.documentElement.tagName !== 'gpx') {
                    alert(fileinfo[0].name + ' in an invalid file.');
                    return false;
                }
    
                var size = fileinfo[0].size,
                    unitCount = 0;
                while (size >= 1024) {
                    size = size / 1024;
                    ++unitCount;
                }
    
                Output(fileinfo[0].name + ' (' + size.toFixed(2) + unitType[unitCount] + ')');
                display(doc);
            };
    
            reader.onloadend = function() {
                el.style.display = 'none';
            };
    
            reader.readAsText(file, 'UTF-8');
        };
    
        var FileSelectHandler = function(e) {
  • ¶

    fetch FileList object

            var files = e.target.files || e.dataTransfer.files;
  • ¶

    process all File objects

            _.each(files, function(value) {
                ParseFile(value);
            });
    
        };
  • ¶

    call initialization file

        if (window.File && window.FileList && window.FileReader) {
            document.getElementById('files').addEventListener('change', FileSelectHandler, false);
        } else {
            alert('Your browser doesn\'t support this feature, please upgrade it (or use Firefox or Chrome).');
        }
    
        window.onload = load();
    })(_);