From 9224be15ebaaaa36c15a22e2a608aebd4d9db9b6 Mon Sep 17 00:00:00 2001 From: Franzz Date: Mon, 7 Feb 2022 17:35:25 +0100 Subject: [PATCH] Gaia updater v3.1 --- gaia/upload.js | 567 ++++++++++++++++++------------------------------- 1 file changed, 208 insertions(+), 359 deletions(-) diff --git a/gaia/upload.js b/gaia/upload.js index 5cc52bc..7a99f7b 100644 --- a/gaia/upload.js +++ b/gaia/upload.js @@ -1,20 +1,27 @@ // ==UserScript== // @name GaiaGps Uploader -// @description Allow the user to upload multiple files at once and more than 1000 waypoints -// @version 3.0 -// @grant none -// @match https://www.gaiagps.com/map/* -// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min.js // @namespace https://greasyfork.org/users/583371 +// @description Allow the user to upload multiple files at once and more than 1000 waypoints +// @grant none +// @version 3.1 +// @author Franzz +// @match https://www.gaiagps.com/map/* +// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js // ==/UserScript== /* jshint esversion: 6 */ //Ctrl+Alt+Shift+I to open network tab on addon -/* GPXParser - v3.0.6 - https://github.com/Luuka/GPXParser.js/blob/master/src/GPXParser.js */ +/* GPXParser - v3.0.8 - https://github.com/Luuka/GPXParser.js/blob/master/src/GPXParser.js */ /* Personnal modifications have been applied, care on upgrade */ -let gpxParser = function () { + +/** + * GPX file parser + * + * @constructor + */ + let gpxParser = function () { this.xmlSource = ""; this.metadata = {}; this.waypoints = []; @@ -22,13 +29,20 @@ let gpxParser = function () { this.routes = []; }; +/** + * Parse a gpx formatted string to a GPXParser Object + * + * @param {string} gpxstring - A GPX formatted String + * + * @return {gpxParser} A GPXParser object + */ gpxParser.prototype.parse = function (gpxstring) { let keepThis = this; let domParser = new window.DOMParser(); this.xmlSource = domParser.parseFromString(gpxstring, 'text/xml'); - metadata = this.xmlSource.querySelector('metadata'); + let metadata = this.xmlSource.querySelector('metadata'); if(metadata != null){ this.metadata.name = this.getElementValue(metadata, "name"); this.metadata.desc = this.getElementValue(metadata, "desc"); @@ -70,9 +84,11 @@ gpxParser.prototype.parse = function (gpxstring) { for (let idx in wpts){ var wpt = wpts[idx]; let pt = {}; - pt.name = keepThis.getElementValue(wpt, "name") + pt.name = keepThis.getElementValue(wpt, "name"); pt.lat = parseFloat(wpt.getAttribute("lat")); pt.lon = parseFloat(wpt.getAttribute("lon")); + //let floatValue = parseFloat(keepThis.getElementValue(wpt, "ele")); + //pt.ele = isNaN(floatValue) ? null : floatValue; pt.ele = parseFloat(keepThis.getElementValue(wpt, "ele")) || null; pt.cmt = keepThis.getElementValue(wpt, "cmt"); pt.desc = keepThis.getElementValue(wpt, "desc"); @@ -80,7 +96,7 @@ gpxParser.prototype.parse = function (gpxstring) { //let time = keepThis.getElementValue(wpt, "time"); //pt.time = time == null ? null : new Date(time); - pt.time = (keepThis.getElementValue(wpt, "time") || keepThis.metadata.time) || null;; + pt.time = (keepThis.getElementValue(wpt, "time") || keepThis.metadata.time) || null; keepThis.waypoints.push(pt); } @@ -115,6 +131,8 @@ gpxParser.prototype.parse = function (gpxstring) { let pt = {}; pt.lat = parseFloat(rtept.getAttribute("lat")); pt.lon = parseFloat(rtept.getAttribute("lon")); + //let floatValue = parseFloat(keepThis.getElementValue(rtept, "ele")); + //pt.ele = isNaN(floatValue) ? null : floatValue; pt.ele = parseFloat(keepThis.getElementValue(rtept, "ele")) || null; //let time = keepThis.getElementValue(rtept, "time"); @@ -127,7 +145,7 @@ gpxParser.prototype.parse = function (gpxstring) { //route.distance = keepThis.calculDistance(routepoints); //route.elevation = keepThis.calcElevation(routepoints); //route.slopes = keepThis.calculSlope(routepoints, route.distance.cumul); - route.points = routepoints; + route.points = routepoints; keepThis.routes.push(route); } @@ -159,11 +177,13 @@ gpxParser.prototype.parse = function (gpxstring) { let trackpoints = []; let trkpts = [].slice.call(trk.querySelectorAll('trkpt')); - for (let idxIn in trkpts){ + for (let idxIn in trkpts){ var trkpt = trkpts[idxIn]; let pt = {}; pt.lat = parseFloat(trkpt.getAttribute("lat")); pt.lon = parseFloat(trkpt.getAttribute("lon")); + //let floatValue = parseFloat(keepThis.getElementValue(trkpt, "ele")); + //pt.ele = isNaN(floatValue) ? null : floatValue; pt.ele = parseFloat(keepThis.getElementValue(trkpt, "ele")) || null; //let time = keepThis.getElementValue(trkpt, "time"); @@ -175,20 +195,37 @@ gpxParser.prototype.parse = function (gpxstring) { //track.distance = keepThis.calculDistance(trackpoints); //track.elevation = keepThis.calcElevation(trackpoints); //track.slopes = keepThis.calculSlope(trackpoints, track.distance.cumul); - track.points = trackpoints; + track.points = trackpoints; keepThis.tracks.push(track); } }; +/** + * Get value from a XML DOM element + * + * @param {Element} parent - Parent DOM Element + * @param {string} needle - Name of the searched element + * + * @return {} The element value + */ gpxParser.prototype.getElementValue = function(parent, needle){ let elem = parent.querySelector(needle); if(elem != null){ - return $('
').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 += ''+ - ''+(this.tracks[i].points[j].ele || '')+''+ - ''; - } - sTrack += ''+ - ''+(this.tracks[i].name || '')+''+ - ''+(this.tracks[i].desc || '')+''+ - ''+(this.tracks[i].src || '')+''+ - ''+sTrkSeg+''+ - ''; - } - - var sGPX = ''+ - ''+ - ''+sName+''+ - sTrack+ - ''; - - 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