diff --git a/script/gaia_upload.js b/script/gaia_upload.js index 11e54a5..e79df4d 100644 --- a/script/gaia_upload.js +++ b/script/gaia_upload.js @@ -434,26 +434,57 @@ class Gaia { static get URL() { return 'https://www.gaiagps.com'; } static get API() { return Gaia.URL+'/api/objects'; } - constructor($InputFile, $InputButton) { + constructor() { this.asFiles = []; this.aoWaypoints = []; this.aoTracks = []; this.sFolderId = ''; this.asFolder = {}; this.sFolderName = ''; - this.$Feedback = $('
', {'style':'position:fixed;width:100%;height:25%;bottom:0;background:white;z-index:10000;border-top:1px solid #CCC;box-sizing:border-box;font-size:1.2em;overflow:auto;padding:0 1em;'}); - this.$InputName = $('', {'type':'text', 'name':'folder_name', '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;'}); + this.progress = {current:0, total:0}; + } - this.$InputBox = $InputButton.parent(); + /* FIXME: adapts on GaiaGPS upgrade */ + setLayout() { + let $InputButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button'); - //Reboot object to remove events - this.$InputFile = $InputFile.clone().insertAfter($InputFile); - $InputFile.remove(); + //If the button is found (=displayed) + if($InputButton.length > 0) { + this.$InputBox = $InputButton.parent(); - this.$InputButton = $InputButton.clone().insertAfter($InputButton); - $InputButton.remove(); + //Add Feedback box + 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)); - this.setLayout(); + //Add Folder Name Input next to button + this.$InputName = ($('#ggu-inputname').length > 0)?$('#ggu-inputname'):($('', { + 'type':'text', + 'id': 'ggu-inputname', + 'name':'ggu-inputname', + '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 + let $InputFile = $('input[type=file]'); + this.$InputFile = $InputFile.clone() + .insertAfter($InputFile) + .attr('multiple', 'multiple') + .attr('name', 'files[]') + .change(() => { this.readInputFiles(); }); + $InputFile.remove(); + + //Reset button + this.$InputButton = $InputButton.clone() + .insertAfter($InputButton) + .click(() => {this.$InputFile.click();}); + $InputButton.remove(); + + //Clear all upload notifications + this.resetNotif(); + } } feedback(sType, sMsg) { @@ -464,32 +495,45 @@ class Gaia { switch(sType) { case 'error': sColor = 'red'; break; case 'warning': sColor = 'orange'; break; - case 'info': sColor = 'green'; break; + case 'info': sColor = '#2D5E38'; break; } this.$Feedback.append($('

', {'style': 'color: '+sColor+';'}).text(sFormattedMsg)); this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight")); } - //Modify Gaia DOM Interface - setLayout() { - //Add Feedback box - this.$Feedback.appendTo($('body')); + incProgress() { + if(!this.progress.current) { + this.progress.$Done = $('

', {'style':'overflow:hidden; background:#2D5E38; color: white; text-align:right;'}); + this.progress.$Left = $('
', {'style':'overflow:hidden; background:white; color: #2D5E38; text-align:left;'}); + this.progress.$Box = $('
', {'id':'ggu-progress', 'style':'margin-top:1em;'}) + .append($('
', {'style':'display:flex; width:calc(100% + 64px); margin-left:-32px; border-radius:3px; border: 1px solid #2D5E38; box-sizing: border-box;'}) + .append(this.progress.$Done) + .append(this.progress.$Left) + ); + this.$Feedback.before(this.progress.$Box); + } - //Add Event on button - this.$InputButton.click(() => {this.$InputFile.click();}); + this.progress.current++; - //Set event on file selection - this.$InputFile - .attr('multiple', 'multiple') - .attr('name', 'files[]') - .change(() => {this.readInputFiles();}); - - //Set default Name - this.$InputName.val('PCT').prependTo(this.$InputBox); - - //Clear all upload notifications - this.resetNotif(); + if(this.progress.current < this.progress.total) { + let iRatio = Math.round(this.progress.current / this.progress.total * 100); + let sTextDone = '', sTextLeft = ''; + if(iRatio < 50) { + sTextDone = ''; + sTextLeft = ' '+iRatio+'% ('+this.progress.current+' / '+this.progress.total+')'; + } + else { + sTextDone = '('+this.progress.current+' / '+this.progress.total+') '+iRatio+'% '; + sTextLeft = ''; + } + this.progress.$Done.css('flex', iRatio+' 1 0%').html(sTextDone); + this.progress.$Left.css('flex', (100 - iRatio)+' 1 0%').html(sTextLeft); + } + else { + this.progress.$Box.remove(); + this.progress = {current:0, total:0}; + } } //Marking all upload notifications as read @@ -524,7 +568,10 @@ 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.createFolder(); + 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.asFiles[i]); aoReaders[i].readAsText(this.asFiles[i]); @@ -581,94 +628,103 @@ class Gaia { 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'); }); }); } //Build & Upload Track File - uploadTrack(iIndex, bSecondTry) { + uploadTrack(iIndex) { iIndex = iIndex || 0; - bSecondTry = bSecondTry || false; if(iIndex == 0) this.feedback('info', 'Uploading tracks...'); - if(iIndex < this.aoTracks.length) { - let aoTrack = this.aoTracks[i]; + let aoTrack = this.aoTracks[iIndex]; + this.feedback('info', 'Uploading track "'+aoTrack.name+'"'); - this.feedback('info', 'Uploading track "'+aoTrack.name+'"'); - - //Set color - let sColor = '#4ABD32'; - switch(aoTrack.color) { - case 'DarkBlue': sColor = '#2D3FC7'; break; - case 'Magenta': sColor = '#B60DC3'; break; - } - - //Add track points - let aoCoords = []; - for(var p in aoTrack.points) { - let pt = aoTrack.points[p]; - aoCoords.push([pt.lon, pt.lat, pt.ele, 0]); - } - - //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: { - type: 'MultiLineString', - coordinates: [aoCoords] - } - }); - - var self = this; - $.post({ - url: Gaia.API+'/track/', - contentType: 'application/json', - data: sPostedData, - postedData: sPostedData, - trackName: aoTrack.name - }).done(function(asTrack) { - self.aoTracks[iIndex] = asTrack; - - $.ajax({ - url: Gaia.API+'/track/'+asTrack.features[0].id+'/', - type: 'PUT', - contentType: 'application/json', - data: this.postedData, - trackName: this.trackName - }).done(function() { - self.feedback('info', 'Track "'+this.trackName+'" uploaded'); - self.uploadTrack(++iIndex); - }).fail(function() { - self.feedback('error', 'Track "'+this.trackName+'" failed to upload. Retrying...'); - self.uploadTrack(iIndex, true); - }); - }); + //Set color + let sColor = '#4ABD32'; + switch(aoTrack.color) { + case 'DarkBlue': sColor = '#2D3FC7'; break; + case 'Magenta': sColor = '#B60DC3'; break; } - else { - self.feedback('info', 'All tracks uploaded'); - self.uploadWayPoints(); + + //Add track points + let aoCoords = []; + for(var p in aoTrack.points) { + let pt = aoTrack.points[p]; + aoCoords.push([pt.lon, pt.lat, pt.ele, 0]); } + + //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: { + type: 'MultiLineString', + coordinates: [aoCoords] + } + }); + + var self = this; + $.post({ + url: Gaia.API+'/track/', + contentType: 'application/json', + data: sPostedData, + trackName: aoTrack.name + }).done(function(asTrack) { + self.aoTracks[iIndex] = asTrack; + self.confirmTrack(iIndex, asTrack, this.data); + }).fail(function() { + self.feedback('error', 'Track "'+this.trackName+'" upload failed (stage 1). Retrying...'); + self.uploadTrack(iIndex); + }); + } + + confirmTrack(iIndex, asTrack, sPostedData) { + iIndex = iIndex || 0; + + var self = this; + $.ajax({ + url: Gaia.API+'/track/'+asTrack.features[0].id+'/', + type: 'PUT', + contentType: 'application/json', + data: sPostedData, + trackName: asTrack.features[0].properties.title + }).done(function() { + self.feedback('info', 'Track "'+this.trackName+'" uploaded'); + self.incProgress(); + iIndex++; + if(iIndex < self.aoTracks.length) self.uploadTrack(iIndex); + else { + self.feedback('info', 'All tracks uploaded'); + self.uploadWayPoints(); + } + }).fail(function() { + self.feedback('error', 'Track "'+this.trackName+'" upload failed (stage 2). Retrying...'); + self.confirmTrack(iIndex, asTrack, sPostedData); + }); } /* @@ -694,69 +750,70 @@ class Gaia { } */ - uploadWayPoints(iIndex, bSecondTry) { + uploadWayPoints(iIndex) { iIndex = iIndex || 0; - bSecondTry = bSecondTry || false; //Upload waypoints - if(iIndex < this.aoWaypoints.length) { - var sWaypointName = this.aoWaypoints[iIndex].name; + var sWaypointName = this.aoWaypoints[iIndex].name; - this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+sWaypointName+')'); - var asPost = { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [ - this.aoWaypoints[iIndex].lon, - this.aoWaypoints[iIndex].lat, - this.aoWaypoints[iIndex].ele - ] - }, - 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 - } - }; + this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+sWaypointName+')'); + var asPost = { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [ + this.aoWaypoints[iIndex].lon, + this.aoWaypoints[iIndex].lat, + this.aoWaypoints[iIndex].ele + ] + }, + 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 + } + }; - let sData = JSON.stringify(asPost); - var self = this; - $.post({ - url: Gaia.API+'/waypoint/', - contentType: 'application/json', - data: sData, - postedData: sData - }).done(function(asWaypoint) { - //Update local waypoint with all server info (including ID) - self.aoWaypoints[iIndex] = asWaypoint; + let sData = JSON.stringify(asPost); + var self = this; + $.post({ + url: Gaia.API+'/waypoint/', + contentType: 'application/json', + data: sData + }).done(function(asWaypoint) { + self.aoWaypoints[iIndex] = asWaypoint; + self.confirmWayPoint(iIndex, asWaypoint, this.data); + }).fail(function(){ + self.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+sWaypointName+'" (Stage 1). Trying again...'); + self.uploadWayPoints(iIndex); + }); + } - $.ajax({ - url: Gaia.API+'/waypoint/'+asWaypoint.properties.id+'/', - type: 'PUT', - contentType: 'application/json', - data: this.postedData - }).always(() => { - self.uploadWayPoints(++iIndex); - }); - }).fail(function(){ - self.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' ('+sWaypointName+'). Trying again...'); - self.uploadWayPoints(iIndex, true); - }); - } - - //Done uploading, assigning waypoints & tracks to folder - else this.assignElementsToFolder(); + confirmWayPoint(iIndex, asWaypoint, sPostedData) { + $.ajax({ + url: Gaia.API+'/waypoint/'+asWaypoint.properties.id+'/', + type: 'PUT', + contentType: 'application/json', + data: sPostedData + }).done(() => { + iIndex++; + this.incProgress(); + if(iIndex < this.aoWaypoints.length) this.uploadWayPoints(iIndex); + else this.assignElementsToFolder(); + }).fail(() => { + this.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+asWaypoint.properties.title+'" (Stage 2). Trying again...'); + this.confirmWayPoint(iIndex, asWaypoint, sPostedData); + }); } assignElementsToFolder() { @@ -784,9 +841,11 @@ class Gaia { contentType: 'application/json', data: JSON.stringify(asFolder) }).done(() => { + this.incProgress(); this.feedback('info', 'Done'); }).fail(() => { - this.feedback('error', 'WFailed to assign waypoints & tracks to folder ID "'+asFolder.id+'"'); + this.feedback('warning', 'Failed to assign waypoints & tracks to folder ID "'+asFolder.id+'". Retrying...'); + this.assignElementsToFolder(); }); } @@ -826,9 +885,13 @@ class Gaia { } console.log('Loading GaiaGps Uploader...'); +let oGaia = new Gaia(); -//To be adjusted regularly -$FileInput = $('input[type=file]'); -$FileButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button'); +/* To be adjusted on Gaia's updates */ -let oGaia = new Gaia($FileInput, $FileButton); +//Side panel +$('path[d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"]').parent().parent().on('click', () => { + setTimeout(() => { + oGaia.setLayout(); + }, 500); +});