diff --git a/gaia/upload.js b/gaia/upload.js index 7a99f7b..46cddd1 100644 --- a/gaia/upload.js +++ b/gaia/upload.js @@ -1,12 +1,13 @@ // ==UserScript== -// @name GaiaGps Uploader +// @name GaiaGps Uploader // @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 +// @grant none +// @version 3.1.1 // @author Franzz -// @match https://www.gaiagps.com/map/* -// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js +// @license GNU GPLv3 +// @match https://www.gaiagps.com/map/* +// @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js // ==/UserScript== /* jshint esversion: 6 */ @@ -22,11 +23,11 @@ * @constructor */ let gpxParser = function () { - this.xmlSource = ""; - this.metadata = {}; - this.waypoints = []; - this.tracks = []; - this.routes = []; + this.xmlSource = ""; + this.metadata = {}; + this.waypoints = []; + this.tracks = []; + this.routes = []; }; /** @@ -37,391 +38,392 @@ * @return {gpxParser} A GPXParser object */ gpxParser.prototype.parse = function (gpxstring) { - let keepThis = this; + let keepThis = this; - let domParser = new window.DOMParser(); - this.xmlSource = domParser.parseFromString(gpxstring, 'text/xml'); + let domParser = new window.DOMParser(); + this.xmlSource = domParser.parseFromString(gpxstring, 'text/xml'); - let metadata = this.xmlSource.querySelector('metadata'); - if(metadata != null){ - this.metadata.name = this.getElementValue(metadata, "name"); - this.metadata.desc = this.getElementValue(metadata, "desc"); - this.metadata.time = this.getElementValue(metadata, "time"); + let metadata = this.xmlSource.querySelector('metadata'); + if(metadata != null){ + this.metadata.name = this.getElementValue(metadata, "name"); + this.metadata.desc = this.getElementValue(metadata, "desc"); + this.metadata.time = this.getElementValue(metadata, "time"); - let author = {}; - let authorElem = metadata.querySelector('author'); - if(authorElem != null){ - author.name = this.getElementValue(authorElem, "name"); - author.email = {}; - let emailElem = authorElem.querySelector('email'); - if(emailElem != null){ - author.email.id = emailElem.getAttribute("id"); - author.email.domain = emailElem.getAttribute("domain"); - } + let author = {}; + let authorElem = metadata.querySelector('author'); + if(authorElem != null){ + author.name = this.getElementValue(authorElem, "name"); + author.email = {}; + let emailElem = authorElem.querySelector('email'); + if(emailElem != null){ + author.email.id = emailElem.getAttribute("id"); + author.email.domain = emailElem.getAttribute("domain"); + } - let link = {}; - let linkElem = authorElem.querySelector('link'); - if(linkElem != null){ - link.href = linkElem.getAttribute('href'); - link.text = this.getElementValue(linkElem, "text"); - link.type = this.getElementValue(linkElem, "type"); - } - author.link = link; - } - this.metadata.author = author; + let link = {}; + let linkElem = authorElem.querySelector('link'); + if(linkElem != null){ + link.href = linkElem.getAttribute('href'); + link.text = this.getElementValue(linkElem, "text"); + link.type = this.getElementValue(linkElem, "type"); + } + author.link = link; + } + this.metadata.author = author; - let link = {}; - let linkElem = this.queryDirectSelector(metadata, 'link'); - if(linkElem != null){ - link.href = linkElem.getAttribute('href'); - link.text = this.getElementValue(linkElem, "text"); - link.type = this.getElementValue(linkElem, "type"); - this.metadata.link = link; - } - } + let link = {}; + let linkElem = this.queryDirectSelector(metadata, 'link'); + if(linkElem != null){ + link.href = linkElem.getAttribute('href'); + link.text = this.getElementValue(linkElem, "text"); + link.type = this.getElementValue(linkElem, "type"); + this.metadata.link = link; + } + } - var wpts = [].slice.call(this.xmlSource.querySelectorAll('wpt')); - for (let idx in wpts){ - var wpt = wpts[idx]; - let pt = {}; - 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"); - pt.sym = keepThis.getElementValue(wpt, "sym"); + var wpts = [].slice.call(this.xmlSource.querySelectorAll('wpt')); + for (let idx in wpts){ + var wpt = wpts[idx]; + let pt = {}; + 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"); + pt.sym = keepThis.getElementValue(wpt, "sym"); - //let time = keepThis.getElementValue(wpt, "time"); - //pt.time = time == null ? null : new Date(time); - pt.time = (keepThis.getElementValue(wpt, "time") || keepThis.metadata.time) || null; + //let time = keepThis.getElementValue(wpt, "time"); + //pt.time = time == null ? null : new Date(time); + pt.time = (keepThis.getElementValue(wpt, "time") || keepThis.metadata.time) || null; - keepThis.waypoints.push(pt); - } + keepThis.waypoints.push(pt); + } - var rtes = [].slice.call(this.xmlSource.querySelectorAll('rte')); - for (let idx in rtes){ - let rte = rtes[idx]; - let route = {}; - route.name = keepThis.getElementValue(rte, "name"); - route.cmt = keepThis.getElementValue(rte, "cmt"); - route.desc = keepThis.getElementValue(rte, "desc"); - route.src = keepThis.getElementValue(rte, "src"); - route.number = keepThis.getElementValue(rte, "number"); + var rtes = [].slice.call(this.xmlSource.querySelectorAll('rte')); + for (let idx in rtes){ + let rte = rtes[idx]; + let route = {}; + route.name = keepThis.getElementValue(rte, "name"); + route.cmt = keepThis.getElementValue(rte, "cmt"); + route.desc = keepThis.getElementValue(rte, "desc"); + route.src = keepThis.getElementValue(rte, "src"); + route.number= keepThis.getElementValue(rte, "number"); - let type = keepThis.queryDirectSelector(rte, "type"); - route.type = type != null ? type.innerHTML : null; + let type = keepThis.queryDirectSelector(rte, "type"); + route.type = type != null ? type.innerHTML : null; - let link = {}; - let linkElem = rte.querySelector('link'); - if(linkElem != null){ - link.href = linkElem.getAttribute('href'); - link.text = keepThis.getElementValue(linkElem, "text"); - link.type = keepThis.getElementValue(linkElem, "type"); - } - route.link = link; + let link = {}; + let linkElem= rte.querySelector('link'); + if(linkElem != null){ + link.href = linkElem.getAttribute('href'); + link.text = keepThis.getElementValue(linkElem, "text"); + link.type = keepThis.getElementValue(linkElem, "type"); + } + route.link = link; - let routepoints = []; - var rtepts = [].slice.call(rte.querySelectorAll('rtept')); + let routepoints = []; + var rtepts = [].slice.call(rte.querySelectorAll('rtept')); - for (let idxIn in rtepts){ - let rtept = rtepts[idxIn]; - 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; + for (let idxIn in rtepts){ + let rtept = rtepts[idxIn]; + 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"); - //pt.time = time == null ? null : new Date(time); - pt.time = (keepThis.getElementValue(rtept, "time") || keepThis.metadata.time) || null; + //let time = keepThis.getElementValue(rtept, "time"); + //pt.time = time == null ? null : new Date(time); + pt.time = (keepThis.getElementValue(rtept, "time") || keepThis.metadata.time) || null; - routepoints.push(pt); - } + routepoints.push(pt); + } - //route.distance = keepThis.calculDistance(routepoints); - //route.elevation = keepThis.calcElevation(routepoints); - //route.slopes = keepThis.calculSlope(routepoints, route.distance.cumul); - route.points = routepoints; + //route.distance = keepThis.calculDistance(routepoints); + //route.elevation = keepThis.calcElevation(routepoints); + //route.slopes = keepThis.calculSlope(routepoints, route.distance.cumul); + route.points = routepoints; - keepThis.routes.push(route); - } + keepThis.routes.push(route); + } - var trks = [].slice.call(this.xmlSource.querySelectorAll('trk')); - for (let idx in trks){ - let trk = trks[idx]; - let track = {}; + var trks = [].slice.call(this.xmlSource.querySelectorAll('trk')); + for (let idx in trks){ + let trk = trks[idx]; + let track = {}; - track.name = keepThis.getElementValue(trk, "name"); - track.cmt = keepThis.getElementValue(trk, "cmt"); - track.desc = keepThis.getElementValue(trk, "desc"); - track.src = keepThis.getElementValue(trk, "src"); - track.number = keepThis.getElementValue(trk, "number"); - track.color = keepThis.getElementValue(trk, "DisplayColor"); - track.time = keepThis.metadata.time; + track.name = keepThis.getElementValue(trk, "name"); + track.cmt = keepThis.getElementValue(trk, "cmt"); + track.desc = keepThis.getElementValue(trk, "desc"); + track.src = keepThis.getElementValue(trk, "src"); + track.number= keepThis.getElementValue(trk, "number"); + track.color = keepThis.getElementValue(trk, "DisplayColor"); + track.time = keepThis.metadata.time; - let type = keepThis.queryDirectSelector(trk, "type"); - track.type = type != null ? type.innerHTML : null; + let type = keepThis.queryDirectSelector(trk, "type"); + track.type = type != null ? type.innerHTML : null; - let link = {}; - let linkElem = trk.querySelector('link'); - if(linkElem != null){ - link.href = linkElem.getAttribute('href'); - link.text = keepThis.getElementValue(linkElem, "text"); - link.type = keepThis.getElementValue(linkElem, "type"); - } - track.link = link; + let link = {}; + let linkElem= trk.querySelector('link'); + if(linkElem != null){ + link.href = linkElem.getAttribute('href'); + link.text = keepThis.getElementValue(linkElem, "text"); + link.type = keepThis.getElementValue(linkElem, "type"); + } + track.link = link; - let trackpoints = []; - let trkpts = [].slice.call(trk.querySelectorAll('trkpt')); - 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 trackpoints = []; + let trkpts = [].slice.call(trk.querySelectorAll('trkpt')); + 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"); - //pt.time = time == null ? null : new Date(time); - pt.time = (keepThis.getElementValue(trkpt, "time") || keepThis.metadata.time) || null; + //let time = keepThis.getElementValue(trkpt, "time"); + //pt.time = time == null ? null : new Date(time); + pt.time = (keepThis.getElementValue(trkpt, "time") || keepThis.metadata.time) || null; - trackpoints.push(pt); - } - //track.distance = keepThis.calculDistance(trackpoints); - //track.elevation = keepThis.calcElevation(trackpoints); - //track.slopes = keepThis.calculSlope(trackpoints, track.distance.cumul); - track.points = trackpoints; + trackpoints.push(pt); + } + //track.distance = keepThis.calculDistance(trackpoints); + //track.elevation = keepThis.calcElevation(trackpoints); + //track.slopes = keepThis.calculSlope(trackpoints, track.distance.cumul); + track.points = trackpoints; - keepThis.tracks.push(track); - } + 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 + * @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 elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data; - } - return elem; + let elem = parent.querySelector(needle); + if(elem != null){ + 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 + * @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); - let finalElem = elements[0]; + let elements = parent.querySelectorAll(needle); + let finalElem = elements[0]; - if(elements.length > 1) { - let directChilds = parent.childNodes; + if(elements.length > 1) { + let directChilds = parent.childNodes; - for(idx in directChilds) { - elem = directChilds[idx]; - if(elem.tagName === needle) { - finalElem = elem; - } - } - } + for(idx in directChilds) { + elem = directChilds[idx]; + if(elem.tagName === needle) { + finalElem = elem; + } + } + } - return finalElem; + return finalElem; }; class Gaia { - static get URL() { return 'https://www.gaiagps.com'; } - static get API() { return Gaia.URL+'/api/objects'; } + static get URL() { return 'https://www.gaiagps.com'; } + static get API() { return Gaia.URL+'/api/objects'; } - constructor() { - this.asFiles = []; - this.aoWaypoints = []; - this.aoTracks = []; - this.asFolders = {}; - this.progress = {current:0, total:0}; - } + constructor() { + this.asFiles = []; + this.aoWaypoints = []; + this.aoTracks = []; + this.asFolders = {}; + this.progress = {current:0, total:0}; + } - setLayout() { - - /* FIXME: adapts on GaiaGPS upgrade */ - let $InputButton = $('a[href="https://help.gaiagps.com/hc/en-us/articles/360052763513"]').parent().find('button'); + 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(); + //If the button is found (=displayed) + if($InputButton.length > 0) { + this.$InputBox = $InputButton.parent(); - //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)); + //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)); - /* - //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)); + /* + //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 DOM Element - let $InputFile = $('input[type=file]'); - this.$InputFile = $InputFile.clone() - .insertAfter($InputFile) - .attr('multiple', 'multiple') - .attr('name', 'files[]') - .change(() => { this.readInputFiles(); }); - $InputFile.remove(); + + //Reset File Input DOM Element + 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(); + //Reset button + this.$InputButton = $InputButton.clone() + .insertAfter($InputButton) + .click(() => {this.$InputFile.click();}); + $InputButton.remove(); - //Clear all upload notifications - this.resetNotif(); - } - } + //Clear all upload notifications + this.resetNotif(); + } + } - feedback(sType, sMsg) { - var sFormattedMsg = sType.charAt(0).toUpperCase()+sType.slice(1)+': '+sMsg+(sMsg.slice(-1)=='.'?'':'.'); - console.log(sFormattedMsg); + feedback(sType, sMsg) { + let sColor = 'black'; + let sIcon = ''; + switch(sType) { + case 'error': sColor = 'red'; sIcon = '\u274C'; break; + case 'warning': sColor = 'orange'; sIcon = '\u26A0'; break; + case 'info': sColor = '#2D5E38'; sIcon = '\u2713'; break; + } - let sColor = 'black'; - switch(sType) { - case 'error': sColor = 'red'; break; - case 'warning': sColor = 'orange'; break; - case 'info': sColor = '#2D5E38'; break; - } - this.$Feedback.append($('

', {'style': 'color: '+sColor+';'}).text(sFormattedMsg)); + var sFormattedMsg = sIcon+' '+sMsg+(sMsg.slice(-1)=='.'?'':'.'); + console.log(sFormattedMsg); + + this.$Feedback.append($('

', {'style': 'color: '+sColor+';'}).text(sFormattedMsg)); + this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight")); + } - this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight")); - } + 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); + } - 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); - } + this.progress.current++; - this.progress.current++; + 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}; + } + } - 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 + resetNotif() { + this.feedback('info', 'Marking all upload notifications as read'); + $.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => { + for(var i in asNotifs) { + if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) { + $.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/'); + } + } + }); + } - //Marking all upload notifications as read - resetNotif() { - this.feedback('info', 'Marking all upload notifications as read'); - $.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => { - for(var i in asNotifs) { - if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) { - $.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/'); - } - } - }); - } + //Parse files from input (multiple) + readInputFiles() { + if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { + this.feedback('error', 'File APIs are not fully supported in this browser'); + return; + } + else this.feedback('info', 'Parsing input files'); - //Parse files from input (multiple) - readInputFiles() { - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) { - this.feedback('error', 'File APIs are not fully supported in this browser'); - return; - } - else this.feedback('info', 'Parsing input files'); - - //Get Folder Name (text input) - let aoReaders = []; - let iCount = 0; - this.asFiles = this.$InputFile.prop('files'); - for(var i=0 ; i < this.asFiles.length ; i++) { - aoReaders[i] = new FileReader(); - aoReaders[i].onload = ((oFileReader) => { - return (asResult) => { - iCount++; - this.feedback('info', 'Reading file "'+oFileReader.name+'" ('+iCount+'/'+this.asFiles.length+')'); - this.parseFile(oFileReader.name, asResult.target.result); + //Get Folder Name (text input) + let aoReaders = []; + let iCount = 0; + this.asFiles = this.$InputFile.prop('files'); + for(var i=0 ; i < this.asFiles.length ; i++) { + aoReaders[i] = new FileReader(); + aoReaders[i].onload = ((oFileReader) => { + return (asResult) => { + iCount++; + this.feedback('info', 'Reading file "'+oFileReader.name+'" ('+iCount+'/'+this.asFiles.length+')'); + this.parseFile(oFileReader.name, asResult.target.result); 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.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]); - } - } + } + }; + })(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+'"'); + //Parse GPX files to consolidate tracks & waypoints + parseFile(sFileName, oContent) { + this.feedback('info', 'Parsing file "'+sFileName+'"'); - var oGPX = new gpxParser(); - oGPX.parse(oContent); + var oGPX = new gpxParser(); + oGPX.parse(oContent); - //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 - this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"'); - } - else */this.aoWaypoints.push(oGPX.waypoints[w]); - } + //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 + this.feedback('info', 'Ignoring milestone waypoint "'+sWaypointName+'"'); + } + else */this.aoWaypoints.push(oGPX.waypoints[w]); + } - //Tracks - for(var t in oGPX.tracks) { - oGPX.tracks[t].filename = sFileName; - this.aoTracks.push(oGPX.tracks[t]); - } - } + //Tracks + for(var t in oGPX.tracks) { + oGPX.tracks[t].filename = sFileName; + this.aoTracks.push(oGPX.tracks[t]); + } + } - //Delete existing folder with same name & recreating it - createFolders() { + //Delete existing folder with same name & recreating it + createFolders() { let iCount = 0; $.each(this.asFolders, (sFileName, asFolder) => { @@ -462,60 +464,60 @@ class Gaia { }); }); }); - } + } - //Build & Upload Track File - uploadTrack(iIndex) { - iIndex = iIndex || 0; + //Build & Upload Track File + uploadTrack(iIndex) { + iIndex = iIndex || 0; - if(iIndex == 0) this.feedback('info', 'Uploading tracks...'); + if(iIndex == 0) this.feedback('info', 'Uploading tracks...'); - let aoTrack = this.aoTracks[iIndex]; - this.feedback('info', 'Uploading track "'+aoTrack.name+'"'); + let aoTrack = this.aoTracks[iIndex]; + this.feedback('info', 'Uploading track "'+aoTrack.name+'"'); - let sColor = '#4ABD32'; - switch(aoTrack.color) { - - //Personal Colors - case 'DarkBlue': sColor = '#2D3FC7'; break; - case 'Magenta': sColor = '#B60DC3'; break; + let sColor = '#4ABD32'; + switch(aoTrack.color) { + + //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; - case 'DarkYellow': sColor = '#DCEE0E'; break; - //case 'DarkBlue': sColor = '#5E23CA'; break; - case 'DarkMagenta': sColor = '#B60DC3'; break; - case 'DarkCyan': sColor = '#00ACF8'; break; - case 'LightGray': sColor = '#A4A4A4'; break; - case 'DarkGray': sColor = '#577B8E'; break; - case 'Red': sColor = '#F90553'; break; - case 'Green': sColor = '#36C03B'; break; - case 'Yellow': sColor = '#FFF011'; break; - case 'Blue': sColor = '#2D3FC7'; break; - //case 'Magenta': sColor = '#B60DC3'; break; - case 'Cyan': sColor = '#00C3DD'; break; - case 'White': sColor = '#FFFFFF'; break; - case 'Transparent': sColor = '#784D3E'; break; - } + case 'Black': sColor = '#000000'; break; + case 'DarkRed': sColor = '#F90553'; break; + case 'DarkGreen': sColor = '#009B89'; break; + case 'DarkYellow': sColor = '#DCEE0E'; break; + //case 'DarkBlue': sColor = '#5E23CA'; break; + case 'DarkMagenta': sColor = '#B60DC3'; break; + case 'DarkCyan': sColor = '#00ACF8'; break; + case 'LightGray': sColor = '#A4A4A4'; break; + case 'DarkGray': sColor = '#577B8E'; break; + case 'Red': sColor = '#F90553'; break; + case 'Green': sColor = '#36C03B'; break; + case 'Yellow': sColor = '#FFF011'; break; + case 'Blue': sColor = '#2D3FC7'; break; + //case 'Magenta': sColor = '#B60DC3'; break; + case 'Cyan': sColor = '#00C3DD'; break; + case 'White': sColor = '#FFFFFF'; break; + case 'Transparent': sColor = '#784D3E'; 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]); - } + //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({ + //Convert to geojson + let sPostedData = JSON.stringify({ geometry: { - coordinates: [aoCoords], + coordinates: [aoCoords], type: 'MultiLineString' - }, - properties: { + }, + properties: { archived: false, - color: sColor, + color: sColor, //distance: 168405.62350073704, filename: aoTrack.filename, hexcolor: sColor, @@ -528,81 +530,81 @@ class Gaia { 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 - }, + title: aoTrack.name, + type: 'track', + writable: true + }, type: 'Feature' - }); + }); - 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); - }); - } + 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; + 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); - }); - } + 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); + }); + } - /* - //Wait for file to be processed by Gaia - checkNotif() { - this.feedback('info', 'Waiting for Gaia to process consolidated track'); - $.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => { - for(var i in asNotifs) { - if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) { - this.feedback('info', 'Notification '+asNotifs[i].id+' found. Marking as read'); - var $Notif = $('').html(asNotifs[i].html); - this.sFolderId = $Notif.find('a').attr('href').split('/')[3]; - $.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/'); - } - } + /* + //Wait for file to be processed by Gaia + checkNotif() { + this.feedback('info', 'Waiting for Gaia to process consolidated track'); + $.get(Gaia.URL+'/social/notifications/popup/').done((asNotifs) => { + for(var i in asNotifs) { + if(!asNotifs[i].isViewed && asNotifs[i].html.indexOf('has completed') != -1) { + this.feedback('info', 'Notification '+asNotifs[i].id+' found. Marking as read'); + var $Notif = $('').html(asNotifs[i].html); + this.sFolderId = $Notif.find('a').attr('href').split('/')[3]; + $.post(Gaia.URL+'/social/notifications/'+asNotifs[i].id+'/markviewed/'); + } + } - if(this.sFolderId != '') { - this.setTracksColor(); - this.uploadWayPoints(); - } - else setTimeout((() => {this.checkNotif();}), 1000); - }); - } - */ + if(this.sFolderId != '') { + this.setTracksColor(); + this.uploadWayPoints(); + } + else setTimeout((() => {this.checkNotif();}), 1000); + }); + } + */ - uploadWayPoints(iIndex) { - iIndex = iIndex || 0; + uploadWayPoints(iIndex) { + iIndex = iIndex || 0; - //Upload waypoints - var sWaypointName = this.aoWaypoints[iIndex].name; + //Upload waypoints + var sWaypointName = this.aoWaypoints[iIndex].name; var aoWaypoint = this.aoWaypoints[iIndex]; this.feedback('info', 'Uploading waypoint '+(iIndex + 1)+'/'+this.aoWaypoints.length+' ('+aoWaypoint.name+')'); @@ -633,8 +635,8 @@ class Gaia { type: 'Feature' }; - let sData = JSON.stringify(asPost); - var self = this; + let sData = JSON.stringify(asPost); + var self = this; $.post({ url: Gaia.API+'/waypoint/', contentType: 'application/json', @@ -646,123 +648,122 @@ class Gaia { self.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+sWaypointName+'" (Stage 1). Trying again...'); self.uploadWayPoints(iIndex); }); - } + } - 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.assignElementsToFolders(); - }).fail(() => { - this.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+asWaypoint.properties.title+'" (Stage 2). Trying again...'); - this.confirmWayPoint(iIndex, asWaypoint, sPostedData); - }); - } + 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.assignElementsToFolders(); + else this.feedback('info', 'Done'); + }).fail(() => { + this.feedback('error', 'Failed to upload waypoint #'+(iIndex + 1)+' "'+asWaypoint.properties.title+'" (Stage 2). Trying again...'); + this.confirmWayPoint(iIndex, asWaypoint, sPostedData); + }); + } - assignElementsToFolders(iIndex) { - iIndex = iIndex || 0; - let asFolders = Object.keys(this.asFolders).map(key => this.asFolders[key]); - let asFolder = asFolders[iIndex]; + /* + assignElementsToFolders(iIndex) { + iIndex = iIndex || 0; + let asFolders = Object.keys(this.asFolders).map(key => this.asFolders[key]); + let asFolder = asFolders[iIndex]; - this.feedback('info', 'Assigning elements of folder "'+asFolder.properties.name+'"'); + 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 - 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 - 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); - } + //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 + 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 + 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(asData) - }).done(() => { - iIndex++; - this.incProgress(); - 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 "'+asFolder.properties.name+'". Trying again...'); - this.assignElementsToFolders(iIndex); - }); - } + $.ajax({ + url: Gaia.API+'/folder/'+asFolder.id+'/', + type: 'PUT', + contentType: 'application/json', + data: JSON.stringify(asData) + }).done(() => { + iIndex++; + this.incProgress(); + 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 "'+asFolder.properties.name+'". Trying again...'); + this.assignElementsToFolders(iIndex); + }); + } + */ - static getIconName(sGarminName) { - var asMapping = { - 'Trail Head': 'trailhead', - 'Water Source': 'water-24.png', - 'Truck': 'car-24.png', - 'Post Office': 'resupply', - 'Flag, Blue': 'blue-pin-down.png', - 'Car': 'car-24.png', - 'Flag, Red': 'red-pin-down.png', - 'Campground': 'campsite-24.png', - 'Flag, Green': 'green-pin.png', - 'Powerline': 'petroglyph', - 'Shopping Center': 'cafe-24.png', - 'Lodging': 'lodging-24.png', - 'Water Hydrant': 'red-pin-down.png', - 'Drinking Water': 'potable-water', - 'Toll Booth': 'ranger-station', - 'Summit': 'peak', - 'Park': 'park-24.png', - 'Forest': 'forest', - 'Cemetery': 'cemetery-24.png', - 'Bridge': 'dam-24.png', - 'Restaurant': 'restaurant-24.png', - 'Picnic Area': 'picnic', - 'Residence': 'city-24.png', - 'City (Capitol)': 'city-24.png', - 'Ski Resort': 'skiing-24.png', - 'Restroom': 'toilets-24.png', - 'Ground Transportation': 'car-24.png', - 'Church': 'ghost-town' - }; - return (sGarminName in asMapping)?asMapping[sGarminName]:'red-pin-down.png'; - } + static getIconName(sGarminName) { + var asMapping = { + 'Trail Head': 'trailhead', + 'Water Source': 'water-24.png', + 'Truck': 'car-24.png', + 'Post Office': 'resupply', + 'Flag, Blue': 'blue-pin-down.png', + 'Car': 'car-24.png', + 'Flag, Red': 'red-pin-down.png', + 'Campground': 'campsite-24.png', + 'Flag, Green': 'green-pin.png', + 'Powerline': 'petroglyph', + 'Shopping Center': 'cafe-24.png', + 'Lodging': 'lodging-24.png', + 'Water Hydrant': 'red-pin-down.png', + 'Drinking Water': 'potable-water', + 'Toll Booth': 'ranger-station', + 'Summit': 'peak', + 'Park': 'park-24.png', + 'Forest': 'forest', + 'Cemetery': 'cemetery-24.png', + 'Bridge': 'dam-24.png', + 'Restaurant': 'restaurant-24.png', + 'Picnic Area': 'picnic', + 'Residence': 'city-24.png', + 'City (Capitol)': 'city-24.png', + 'Ski Resort': 'skiing-24.png', + 'Restroom': 'toilets-24.png', + 'Ground Transportation': 'car-24.png', + 'Church': 'ghost-town' + }; + return (sGarminName in asMapping)?asMapping[sGarminName]:'red-pin-down.png'; + } } -console.log('Loading GaiaGps Uploader 3.1'); +console.log('Loading GaiaGps Uploader 3.1.1'); let oGaia = new Gaia(); MutationObserver = window.MutationObserver || window.WebKitMutationObserver; let observer = new MutationObserver((mutations, observer) => { - - /* FIXME: adapts on GaiaGPS upgrade */ + + /* 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)}); - } - + 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 +observer.observe(document, { subtree: true, attributes: true}); \ No newline at end of file