').html(elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data).text();
+ return elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data;
}
return elem;
-}
+};
+
+/**
+ * Search the value of a direct child XML DOM element
+ *
+ * @param {Element} parent - Parent DOM Element
+ * @param {string} needle - Name of the searched element
+ *
+ * @return {} The element value
+ */
gpxParser.prototype.queryDirectSelector = function(parent, needle) {
let elements = parent.querySelectorAll(needle);
@@ -206,232 +243,8 @@ gpxParser.prototype.queryDirectSelector = function(parent, needle) {
}
return finalElem;
-}
-
-/*
-gpxParser.prototype.calculDistance = function(points) {
- let distance = {};
- let totalDistance = 0;
- let cumulDistance = [];
- for (var i = 0; i < points.length - 1; i++) {
- totalDistance += this.calcDistanceBetween(points[i],points[i+1]);
- cumulDistance[i] = totalDistance;
- }
- cumulDistance[points.length - 1] = totalDistance;
-
- distance.total = totalDistance;
- distance.cumul = cumulDistance;
-
- return distance;
-}
-
-gpxParser.prototype.calcDistanceBetween = function (wpt1, wpt2) {
- let latlng1 = {};
- latlng1.lat = wpt1.lat;
- latlng1.lon = wpt1.lon;
- let latlng2 = {};
- latlng2.lat = wpt2.lat;
- latlng2.lon = wpt2.lon;
- var rad = Math.PI / 180,
- lat1 = latlng1.lat * rad,
- lat2 = latlng2.lat * rad,
- sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
- sinDLon = Math.sin((latlng2.lon - latlng1.lon) * rad / 2),
- a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
- c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- return 6371000 * c;
-}
-
-gpxParser.prototype.calcElevation = function (points) {
- var dp = 0,
- dm = 0,
- ret = {};
-
- for (var i = 0; i < points.length - 1; i++) {
- var diff = parseFloat(points[i + 1].ele) - parseFloat(points[i].ele);
-
- if (diff < 0) {
- dm += diff;
- } else if (diff > 0) {
- dp += diff;
- }
- }
-
- var elevation = [];
- var sum = 0;
-
- for (var i = 0, len = points.length; i < len; i++) {
- var ele = parseFloat(points[i].ele);
- elevation.push(ele);
- sum += ele;
- }
-
- ret.max = Math.max.apply(null, elevation) || null;
- ret.min = Math.min.apply(null, elevation) || null;
- ret.pos = Math.abs(dp) || null;
- ret.neg = Math.abs(dm) || null;
- ret.avg = sum / elevation.length || null;
-
- return ret;
};
-gpxParser.prototype.calculSlope = function(points, cumul) {
- let slopes = [];
-
- for (var i = 0; i < points.length - 1; i++) {
- let point = points[i];
- let nextPoint = points[i+1];
- let elevationDiff = nextPoint.ele - point.ele;
- let distance = cumul[i+1] - cumul[i];
-
- let slope = (elevationDiff * 100) / distance;
- slopes.push(slope);
- }
-
- return slopes;
-}
-
-gpxParser.prototype.toGeoJSON = function () {
- var GeoJSON = {
- "type": "FeatureCollection",
- "features": [],
- "properties": {
- "name": this.metadata.name,
- "desc": this.metadata.desc,
- "time": this.metadata.time,
- "author": this.metadata.author,
- "link": this.metadata.link,
- },
- };
-
- for(idx in this.tracks) {
- let track = this.tracks[idx];
-
- var feature = {
- "type": "Feature",
- "geometry": {
- "type": "LineString",
- "coordinates": []
- },
- "properties": {
- }
- };
-
- feature.properties.name = track.name;
- feature.properties.cmt = track.cmt;
- feature.properties.desc = track.desc;
- feature.properties.src = track.src;
- feature.properties.number = track.number;
- feature.properties.link = track.link;
- feature.properties.type = track.type;
-
- for(idx in track.points) {
- let pt = track.points[idx];
-
- var geoPt = [];
- geoPt.push(pt.lon);
- geoPt.push(pt.lat);
- geoPt.push(pt.ele);
-
- feature.geometry.coordinates.push(geoPt);
- }
-
- GeoJSON.features.push(feature);
- }
-
- for(idx in this.routes) {
- let track = this.routes[idx];
-
- var feature = {
- "type": "Feature",
- "geometry": {
- "type": "LineString",
- "coordinates": []
- },
- "properties": {
- }
- };
-
- feature.properties.name = track.name;
- feature.properties.cmt = track.cmt;
- feature.properties.desc = track.desc;
- feature.properties.src = track.src;
- feature.properties.number = track.number;
- feature.properties.link = track.link;
- feature.properties.type = track.type;
-
-
- for(idx in track.points) {
- let pt = track.points[idx];
-
- var geoPt = [];
- geoPt.push(pt.lon);
- geoPt.push(pt.lat);
- geoPt.push(pt.ele);
-
- feature.geometry.coordinates.push(geoPt);
- }
-
- GeoJSON.features.push(feature);
- }
-
- for(idx in this.waypoints) {
- let pt = this.waypoints[idx];
-
- var feature = {
- "type": "Feature",
- "geometry": {
- "type": "Point",
- "coordinates": []
- },
- "properties": {
- }
- };
-
- feature.properties.name = pt.name;
- feature.properties.cmt = pt.cmt;
- feature.properties.desc = pt.desc;
-
- feature.geometry.coordinates = [pt.lon, pt.lat, pt.ele];
-
- GeoJSON.features.push(feature);
- }
-
- return GeoJSON;
-}
-
-if(typeof module !== 'undefined'){
- require('jsdom-global')();
- module.exports = gpxParser;
-}
-
-gpxParser.prototype.getGPX = function(sName) {
- var sTrack = '';
-
- for(var i=0 ; i < this.tracks.length ; i++) {
- var sTrkSeg = '';
- for(var j=0 ; j < this.tracks[i].points.length ; j++) {
- sTrkSeg += '
';
-
- return sGPX;
-};
-*/
class Gaia {
static get URL() { return 'https://www.gaiagps.com'; }
@@ -441,26 +254,26 @@ class Gaia {
this.asFiles = [];
this.aoWaypoints = [];
this.aoTracks = [];
- this.sFolderId = '';
- this.asFolder = {};
- this.sFolderName = '';
+ this.asFolders = {};
this.progress = {current:0, total:0};
}
- /* FIXME: adapts on GaiaGPS upgrade */
setLayout() {
+
+ /* FIXME: adapts on GaiaGPS upgrade */
let $InputButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button');
//If the button is found (=displayed)
if($InputButton.length > 0) {
- this.$InputBox = $InputButton.parent();
+ this.$InputBox = $InputButton.parent();
//Add Feedback box
- this.$Feedback = ($('#ggu-feedback').length > 0)?$('#ggu-feedback'):($('
', {
+ this.$Feedback = ($('#ggu-feedback').length > 0)?$('#ggu-feedback'):($('
', {
'id':'ggu-feedback',
'style':'width:calc(100% + 64px); height:400px; margin:1em 0 0 -32px; display:inline-block; text-align:left; overflow:auto;'
}).insertAfter($InputButton));
+ /*
//Add Folder Name Input next to button
this.$InputName = ($('#ggu-inputname').length > 0)?$('#ggu-inputname'):($('', {
'type':'text',
@@ -469,8 +282,9 @@ class Gaia {
'placeholder':'Folder Name',
'style': 'border-width:2px; border-color:rgba(0, 0, 0, 0.1); border-radius:4px; font-family:Inter,Helvetica Neue,Helvetica,Arial; font-size:15px; padding:8px 12px; margin-bottom:12px;'
}).val('PCT').prependTo(this.$InputBox));
-
- //Reset File Input
+ */
+
+ //Reset File Input DOM Element
let $InputFile = $('input[type=file]');
this.$InputFile = $InputFile.clone()
.insertAfter($InputFile)
@@ -560,7 +374,6 @@ class Gaia {
else this.feedback('info', 'Parsing input files');
//Get Folder Name (text input)
- this.sFolderName = this.$InputName.val();
let aoReaders = [];
let iCount = 0;
this.asFiles = this.$InputFile.prop('files');
@@ -571,16 +384,18 @@ class Gaia {
iCount++;
this.feedback('info', 'Reading file "'+oFileReader.name+'" ('+iCount+'/'+this.asFiles.length+')');
this.parseFile(oFileReader.name, asResult.target.result);
- if(iCount == this.asFiles.length) {
- this.progress.total = this.aoTracks.length + this.aoWaypoints.length + 2; //+1: Create forlder +1: Assign objects to folder
- this.createFolder();
+ this.asFolders[oFileReader.name] = {};
+
+ if(iCount == this.asFiles.length) {
+ this.progress.total = this.aoTracks.length + this.aoWaypoints.length + this.asFiles.length * 2; //2 extra actions per file: Create folder + Assign objects to folder
+ this.createFolders();
}
};
})(this.asFiles[i]);
aoReaders[i].readAsText(this.asFiles[i]);
}
}
-
+
//Parse GPX files to consolidate tracks & waypoints
parseFile(sFileName, oContent) {
this.feedback('info', 'Parsing file "'+sFileName+'"');
@@ -591,8 +406,8 @@ class Gaia {
//Waypoints
for(var w in oGPX.waypoints) {
oGPX.waypoints[w].filename = sFileName;
- /*var sWaypointName = oGPX.waypoints[w].name;
- if(sWaypointName.indexOf('Milestone - ') != -1 && sWaypointName.substr(-2) != '00') { //1 milestone every 100 miles
+ var sWaypointName = oGPX.waypoints[w].name;
+ /* if(sWaypointName.indexOf('Milestone - ') != -1 && sWaypointName.substr(-2) != '00') { //1 milestone every 100 miles
this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"');
}
else */this.aoWaypoints.push(oGPX.waypoints[w]);
@@ -606,37 +421,47 @@ class Gaia {
}
//Delete existing folder with same name & recreating it
- createFolder() {
- this.feedback('info', 'Looking for existing folder "'+this.sFolderName+'"...');
+ createFolders() {
+ let iCount = 0;
+ $.each(this.asFolders, (sFileName, asFolder) => {
- $.get(Gaia.API+'/folder/?search='+this.sFolderName).done((asFolders) => {
- if(asFolders != '' && !$.isEmptyObject(asFolders)) {
- for(var f in asFolders) {
- this.feedback('info', 'Deleting "'+asFolders[f].title+'" folder');
- $.ajax({
- url: Gaia.API+'/folder/'+asFolders[f].id+'/',
- type: 'DELETE'
- });
- }
- }
- else this.feedback('info', 'No folder named "'+this.sFolderName+'" found');
+ //Folder Name
+ let sFolderName = sFileName.replace(/\.[^\.]+$/, '');
+
+ this.feedback('info', 'Looking for existing folder "'+sFolderName+'"...');
+ $.get(Gaia.API+'/folder/?search='+sFolderName).done((asFolders) => {
+ if(asFolders != '' && !$.isEmptyObject(asFolders)) {
+ for(var f in asFolders) {
+ this.feedback('info', 'Deleting "'+asFolders[f].title+'" folder');
+ $.ajax({
+ url: Gaia.API+'/folder/'+asFolders[f].id+'/',
+ type: 'DELETE'
+ });
+ }
+ }
+ else this.feedback('info', 'No folder named "'+sFolderName+'" found');
- this.feedback('info', 'Creating folder "'+this.sFolderName+'"');
- $.post({
- url: Gaia.API+'/folder/',
- contentType: 'application/json',
- data: JSON.stringify({imported: true, title: this.sFolderName})
- }).done((asFolder) => {
- this.feedback('info', 'Folder "'+asFolder.properties.name+'" created');
- this.asFolder = asFolder;
- this.sFolderId = asFolder.id;
- this.sFolderName = asFolder.properties.name;
- this.incProgress();
- this.uploadTrack();
- }).fail(() => {
- this.feedback('error', 'Folder "'+this.sFolderName+'" could not be created');
- });
- });
+ this.feedback('info', 'Creating folder "'+sFolderName+'"');
+ let sTime = (new Date()).toISOString();
+ $.post({
+ url: Gaia.API+'/folder/',
+ contentType: 'application/json',
+ data: JSON.stringify({
+ title: sFolderName,
+ imported: true
+ })
+ }).done((asFolder) => {
+ this.feedback('info', 'Folder "'+asFolder.properties.name+'" created');
+ this.asFolders[sFileName] = asFolder;
+ this.incProgress();
+
+ iCount++;
+ if(iCount == Object.keys(this.asFolders).length) this.uploadTrack();
+ }).fail(() => {
+ this.feedback('error', 'Folder "'+sFolderName+'" could not be created');
+ });
+ });
+ });
}
//Build & Upload Track File
@@ -648,13 +473,14 @@ class Gaia {
let aoTrack = this.aoTracks[iIndex];
this.feedback('info', 'Uploading track "'+aoTrack.name+'"');
- //Set color: Convert Garmin Colors
let sColor = '#4ABD32';
switch(aoTrack.color) {
- case 'DarkBlue': sColor = '#2D3FC7'; break; //Personal Color
- case 'Magenta': sColor = '#B60DC3'; break; //Personal Color
-
- //Garmin Colors
+
+ //Personal Colors
+ case 'DarkBlue': sColor = '#2D3FC7'; break;
+ case 'Magenta': sColor = '#B60DC3'; break;
+
+ //Garmin Colors
case 'Black': sColor = '#000000'; break;
case 'DarkRed': sColor = '#F90553'; break;
case 'DarkGreen': sColor = '#009B89'; break;
@@ -668,7 +494,7 @@ class Gaia {
case 'Green': sColor = '#36C03B'; break;
case 'Yellow': sColor = '#FFF011'; break;
case 'Blue': sColor = '#2D3FC7'; break;
- //case 'Magenta': sColor = '#B60DC3'; break;
+ //case 'Magenta': sColor = '#B60DC3'; break;
case 'Cyan': sColor = '#00C3DD'; break;
case 'White': sColor = '#FFFFFF'; break;
case 'Transparent': sColor = '#784D3E'; break;
@@ -683,30 +509,30 @@ class Gaia {
//Convert to geojson
let sPostedData = JSON.stringify({
- type: 'feature',
- properties: {
- color: sColor,
- title: aoTrack.name,
- time_created: aoTrack.time,
- routing_mode: null,
- notes: aoTrack.desc,
- //distance: 168405.62350073704,
- isValid: true,
- isLatestImport: true,
- filename: aoTrack.filename,
- localId: 'track'+(iIndex + 1),
- writable: true,
- archived: false,
- isPublicTrack: false,
- isLocallyCreated: true,
- type: 'track',
- parent_folder_id: this.sFolderId,
- hexcolor: sColor
+ geometry: {
+ coordinates: [aoCoords],
+ type: 'MultiLineString'
},
- geometry: {
- type: 'MultiLineString',
- coordinates: [aoCoords]
- }
+ properties: {
+ archived: false,
+ color: sColor,
+ //distance: 168405.62350073704,
+ filename: aoTrack.filename,
+ hexcolor: sColor,
+ isLatestImport: true,
+ isLocallyCreated: true,
+ isPublicTrack: false,
+ isValid: true,
+ localId: 'track'+(iIndex + 1),
+ notes: aoTrack.desc,
+ parent_folder_id: this.asFolders[aoTrack.filename].id,
+ routing_mode: null,
+ time_created: aoTrack.time || (new Date()).toISOString(),
+ title: aoTrack.name,
+ type: 'track',
+ writable: true
+ },
+ type: 'Feature'
});
var self = this;
@@ -777,33 +603,34 @@ class Gaia {
//Upload waypoints
var sWaypointName = this.aoWaypoints[iIndex].name;
+ var aoWaypoint = this.aoWaypoints[iIndex];
- this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+sWaypointName+')');
+ this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+aoWaypoint.name+')');
var asPost = {
- type: 'Feature',
geometry: {
- type: 'Point',
coordinates: [
- this.aoWaypoints[iIndex].lon,
- this.aoWaypoints[iIndex].lat,
- this.aoWaypoints[iIndex].ele
- ]
+ aoWaypoint.lon,
+ aoWaypoint.lat,
+ aoWaypoint.ele
+ ],
+ type: 'Point'
},
properties: {
- title: sWaypointName,
- time_created: this.aoWaypoints[iIndex].time,
- icon: Gaia.getIconName(this.aoWaypoints[iIndex].sym),
- isValid: true,
- isLatestImport: true,
- filename: this.aoWaypoints[iIndex].filename,
- writable: true,
- localId: iIndex+'',
- notes: this.aoWaypoints[iIndex].desc,
- archived: false,
- isLocallyCreated: true,
- type: 'waypoint',
- parent_folder_id: this.sFolderId
- }
+ archived: false,
+ filename: aoWaypoint.filename,
+ icon: Gaia.getIconName(aoWaypoint.sym),
+ isLatestImport: true,
+ isLocallyCreated: true,
+ isValid: true,
+ localId: iIndex+'',
+ notes: aoWaypoint.desc,
+ parent_folder_id: this.asFolders[aoWaypoint.filename].id,
+ time_created: aoWaypoint.time || (new Date()).toISOString(),
+ title: aoWaypoint.name,
+ type: 'waypoint',
+ writable: true
+ },
+ type: 'Feature'
};
let sData = JSON.stringify(asPost);
@@ -831,43 +658,56 @@ class Gaia {
iIndex++;
this.incProgress();
if(iIndex < this.aoWaypoints.length) this.uploadWayPoints(iIndex);
- else this.assignElementsToFolder();
+ else this.assignElementsToFolders();
}).fail(() => {
this.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+asWaypoint.properties.title+'" (Stage 2). Trying again...');
this.confirmWayPoint(iIndex, asWaypoint, sPostedData);
});
}
- assignElementsToFolder() {
- this.feedback('info', 'Assigning all elements to folder "'+this.asFolder.properties.name+'"');
+ assignElementsToFolders(iIndex) {
+ iIndex = iIndex || 0;
+ let asFolders = Object.keys(this.asFolders).map(key => this.asFolders[key]);
+ let asFolder = asFolders[iIndex];
- let asFolder = {
- cover_photo_id: this.asFolder.properties.cover_photo_id,
- id: this.asFolder.id,
- name: this.asFolder.properties.name,
- notes: this.asFolder.properties.notes,
- time_created: this.asFolder.properties.time_created,
- updated_date: this.asFolder.properties.updated_date
+ this.feedback('info', 'Assigning elements of folder "'+asFolder.properties.name+'"');
+
+ //Folder metadata
+ let asData = {
+ cover_photo_id: asFolder.properties.cover_photo_id,
+ id: asFolder.id,
+ name: asFolder.properties.name,
+ notes: asFolder.properties.notes,
+ time_created: asFolder.properties.time_created,
+ updated_date: asFolder.properties.updated_date
}
+
//Assign waypoints to folder
- asFolder.waypoints = [];
- for(var w in this.aoWaypoints) asFolder.waypoints.push(this.aoWaypoints[w].properties.id);
-
+ asData.waypoints = [];
+ for(var w in this.aoWaypoints) {
+ if(this.aoWaypoints[w].parent_folder_id = asFolder.id) asData.waypoints.push(this.aoWaypoints[w].properties.id);
+ }
+
//Assign tracks to folder
- asFolder.tracks = [];
- for(var t in this.aoTracks) asFolder.tracks.push(this.aoTracks[t].features[0].id);
+ asData.tracks = [];
+ for(var t in this.aoTracks) {
+ if(this.aoTracks[t].parent_folder_id = asFolder.id) asData.tracks.push(this.aoTracks[t].features[0].id);
+ }
$.ajax({
url: Gaia.API+'/folder/'+asFolder.id+'/',
type: 'PUT',
contentType: 'application/json',
- data: JSON.stringify(asFolder)
+ data: JSON.stringify(asData)
}).done(() => {
+ iIndex++;
this.incProgress();
- this.feedback('info', 'Done');
+ this.feedback('info', 'Tracks & waypoints assigned to folder "'+asFolder.properties.name+'"');
+ if(iIndex < asFolders.length) this.assignElementsToFolders(iIndex);
+ else this.feedback('info', 'Done');
}).fail(() => {
- this.feedback('warning', 'Failed to assign waypoints & tracks to folder ID "'+asFolder.id+'". Retrying...');
- this.assignElementsToFolder();
+ this.feedback('warning', 'Failed to assign waypoints & tracks to folder "'+asFolder.properties.name+'". Trying again...');
+ this.assignElementsToFolders(iIndex);
});
}
@@ -906,14 +746,23 @@ class Gaia {
}
}
-console.log('Loading GaiaGps Uploader...');
+console.log('Loading GaiaGps Uploader 3.1');
let oGaia = new Gaia();
-/* To be adjusted on Gaia's updates */
+MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
-//Side panel
-$('path[d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"]').parent().parent().on('click', () => {
- setTimeout(() => {
- oGaia.setLayout();
- }, 500);
+let observer = new MutationObserver((mutations, observer) => {
+
+ /* FIXME: adapts on GaiaGPS upgrade */
+ let $Import = $('div[aria-label="Import Data"]');
+ if($Import.length > 0) {
+ observer.disconnect();
+ $Import.parent('li').on('click', () => { setTimeout(() => { oGaia.setLayout(); }, 500)});
+ }
+
});
+
+observer.observe(document, {
+ subtree: true,
+ attributes: true
+});
\ No newline at end of file