Add waypoint Icons & Track Colors Scripts
This commit is contained in:
1 parent
a5eff5c637
commit
a2faa7acf1
3 files changed
+442
No files matched your search
+211
@@ -0,0 +1,211 @@
|
||||
// ==UserScript==
|
||||
// @name GaiaGps Track Colors Mass Updater
|
||||
// @description Add missing feature: Mass update of GaiaGps Track Colors
|
||||
// @version 1.0
|
||||
// @grant none
|
||||
// @match https://www.gaiagps.com/datasummary/tracks/
|
||||
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min.js
|
||||
// @namespace https://greasyfork.org/users/583371
|
||||
// ==/UserScript==
|
||||
|
||||
console.log('computes');
|
||||
|
||||
function init() {
|
||||
$Selector = $('<div class="MuiPaper-root MuiMenu-paper MuiPopover-paper MuiPaper-elevation8 MuiPaper-rounded" style="opacity: 1; transform: none; min-width: 40px; transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 67ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; top: 440px; left: 114px; transform-origin: 0px 0px 0px;" tabindex="-1"><ul class="MuiList-root MuiMenu-list MuiList-padding" role="listbox" tabindex="-1" style="display: flex; flex-wrap: wrap; width: 217px; flex-grow: 1; justify-content: center;"><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#FFEF00" data-value="#FFEF00"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(255, 239, 0); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#A4A4A4" data-value="#A4A4A4"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(164, 164, 164); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#000000" data-value="#000000"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(0, 0, 0); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#BAF200" data-value="#BAF200"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(186, 242, 0); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#FFC900" data-value="#FFC900"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(255, 201, 0); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#7AD915" data-value="#7AD915"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(122, 217, 21); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#784D3E" data-value="#784D3E"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(120, 77, 62); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#F42410" data-value="#F42410"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(244, 36, 16); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#E834EC" data-value="#E834EC"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(232, 52, 236); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#A726BC" data-value="#A726BC"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(167, 38, 188); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="#5726C2" data-value="#5726C2"><div style="position: relative; height: 24px; width: 24px; background-color: rgb(87, 38, 194); border-radius: 4px;"></div></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root Mui-selected MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button Mui-selected" tabindex="0" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: Line truncated
|
||||
|
||||
$Selector.css('z-index', '10000').find('li').click(setColor);
|
||||
//Background
|
||||
$Background = $('<div>', {'aria-hidden':'true', 'style':'position: fixed; inset: 0px; background-color: transparent;'}).click(function(){toggleSelector(false);});
|
||||
|
||||
//Add Selector & background to DOM
|
||||
$Selector.add($Background).hide().appendTo('body');
|
||||
|
||||
//Extra style
|
||||
$('body').append($('<style>').text('@keyframes rainbow { 0%, 100% {background-color: red;} 17% {background-color: orange;} 33% {background-color: yellow;} 50% {background-color: green;} 67% {background-color: blue;} 83% {background-color: purple;} }'));
|
||||
|
||||
//Button for mass update
|
||||
$MassUpdate = $('<button>', {'type':'button', 'class':'ga-button ga-button-flat ga-toolbar-button', 'disabled':'disabled'})
|
||||
.data('track-id', 'mass')
|
||||
.click(function(){toggleSelector(true, $(this));})
|
||||
.append($('<i>', {'class':'material-icons ibmid'}).text('color_lens'))
|
||||
.append(' ')
|
||||
.append($('<span>', {'class':'ibmid ga-toolbar-button-text'}).text('Change Color'));
|
||||
|
||||
$('#DatasummaryController .ga-toolbar .ga-toolbar-left .ga-button').eq(1).after($MassUpdate);
|
||||
|
||||
//Event on checkboxes to enable mass icon button
|
||||
$('.ngGrid .col0 input[type="checkbox"]').change(function(){
|
||||
$MassUpdate.prop('disabled', ($('.ngGrid .col0 input[type="checkbox"]:checked').length == 0));
|
||||
});
|
||||
|
||||
//DOM Elements
|
||||
$Grid = $('.ngGrid');
|
||||
$Head = $Grid.find('.ngTopPanel');
|
||||
$Body = $Grid.find('.ngViewport');
|
||||
|
||||
iColorColLeft = $Grid.find('.colt0').width();
|
||||
iCol1Width = $Grid.find('.ngHeaderCell.ng-scope.col1.colt1').width();
|
||||
iCol1Left = parseInt($Grid.find('.ngHeaderCell.ng-scope.col1.colt1').css('left'));
|
||||
|
||||
//Insert column header
|
||||
$Head.find('.ngHeaderContainer .ngHeaderCell.col0').after(
|
||||
$('<div>', {'class':'ngHeaderCell ng-scope col0-1 colt0-1', 'style':'width:'+iColorColWidth+'px; left:'+iColorColLeft+'px;'})
|
||||
.append($('<div>', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iColorColHeight+'px;'}).text(' '))
|
||||
.append($('<div>')
|
||||
.append($('<div>', {'class':'ngHeaderSortColumn ngSorted', 'style':'cursor: pointer;'})
|
||||
.append($('<div>', {'class':'ngHeaderText ng-binding', 'style':'width:'+iColorColWidth+'px;'}).text('Color'))
|
||||
)
|
||||
)
|
||||
);
|
||||
$Head.find('.ngHeaderCell.colt1').width(iCol1Width - iColorColWidth);
|
||||
$Head.find('.col1').width(iCol1Width - iColorColWidth).css('left', (iCol1Left + iColorColWidth)+'px');
|
||||
|
||||
//Update Colors
|
||||
update();
|
||||
|
||||
//Event on DOM change: First track of the page
|
||||
oObserver = new MutationObserver((oMutations) => {update();});
|
||||
oObserver.observe($('.ngGrid .ngCanvas .ngCell.col1 .ngCellText.col1 .ng-binding')[0], {attributes: true});
|
||||
}
|
||||
|
||||
function wait(fIsFinished, fAction) {
|
||||
console.log('waiting for page to load');
|
||||
if(typeof oWaitTimer != "undefined") clearTimeout(oWaitTimer);
|
||||
if(fIsFinished()) fAction();
|
||||
else oWaitTimer = setTimeout(function(){wait(fIsFinished, fAction);}, 100);
|
||||
}
|
||||
|
||||
function update(bWaited) {
|
||||
bWaited = bWaited || false;
|
||||
|
||||
if(!bWaited) {
|
||||
if(typeof oTimer != "undefined") clearTimeout(oTimer);
|
||||
oTimer = setTimeout((() => {update(true);}), 200);
|
||||
return;
|
||||
}
|
||||
|
||||
oObserver.disconnect();
|
||||
oObserver.observe($('.ngGrid .ngCanvas .ngCell.col1 .ngCellText.col1 .ng-binding')[0], {attributes: true});
|
||||
|
||||
console.log('updating');
|
||||
|
||||
$Body.find('.ngRow').each(function(key, elem){
|
||||
var $Row = $(elem);
|
||||
var sTrackId = $Row.find('.col1 a.ng-binding').attr('href').split('/')[3];
|
||||
|
||||
if(!asTracks[sTrackId].post) {
|
||||
$.get({url:'https://www.gaiagps.com/api/objects/track/'+sTrackId+'/', id:sTrackId}).done(function(asTrack) {
|
||||
asTracks[this.id].post = asTrack;
|
||||
refreshRow($Row, this.id);
|
||||
});
|
||||
}
|
||||
else refreshRow($Row, sTrackId);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshRow($Row, sTrackId) {
|
||||
|
||||
//Color
|
||||
var sColor = asTracks[sTrackId].post.features[0].properties.hexcolor;
|
||||
if(!sColor) sColor = 'rgb(0,0,0)';
|
||||
|
||||
//Add Color
|
||||
if(!$Row.find('.ngCell.col0-1.colt0-1').length) {
|
||||
|
||||
//Move Col1
|
||||
$Row.find('.colt1').width(iCol1Width - iColorColWidth);
|
||||
$Row.find('.col1').width(iCol1Width - iColorColWidth).css('left', (iCol1Left + iColorColWidth)+'px');
|
||||
|
||||
$Row.find('.ngCell.col0.colt0').after(
|
||||
$('<div>', {'class':'ngCell col0-1 colt0-1', 'style':'cursor: pointer; width:'+iColorColWidth+'px; left:'+iColorColLeft+'px;'})
|
||||
.data('color', sColor)
|
||||
.data('track-id', this.id)
|
||||
.append($('<div>', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iColorColHeight+'px;'}))
|
||||
.append($('<div>')
|
||||
.append($('<div>', {'style':'ngSelectionCell'})
|
||||
.append($('<div>', {'class':'track-color', 'style':'position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); height: 24px; width: 24px; background-color: '+sColor+'; border-radius: 4px;'}))
|
||||
)
|
||||
)
|
||||
//Display Selector
|
||||
.click(function(){
|
||||
toggleSelector(null, $(this));
|
||||
})
|
||||
);
|
||||
}
|
||||
//Update Color
|
||||
else {
|
||||
$Row.find('.ngCell.col0-1.colt0-1')
|
||||
.data('color', sColor)
|
||||
.data('track-id', sTrackId)
|
||||
.find('.track-color')
|
||||
.css('background', sColor);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelector(bShow, $Track){
|
||||
bShow = bShow || !$Selector.is(':visible');
|
||||
$Selector.add($Background).toggle(bShow);
|
||||
if(bShow) {
|
||||
$Selector
|
||||
.data('track', $Track)
|
||||
.css({top: ($Track.offset().top + iColorColHeight)+'px', left: ($Track.offset().left)+'px'})
|
||||
.find('li[data-value="'+$Track.data('color')+'"]').addClass('Mui-selected');
|
||||
}
|
||||
else {
|
||||
$Selector
|
||||
.find('li.Mui-selected').removeClass('Mui-selected');
|
||||
}
|
||||
return bShow;
|
||||
}
|
||||
|
||||
function setColor(){
|
||||
var sColor = $(this).attr('data-value');
|
||||
|
||||
//List of tracks to change (selected checkboxes or just the one)
|
||||
var a$Tracks = [];
|
||||
if($Selector.data('track').data('track-id') == 'mass') a$Tracks = $('.ngGrid .ngViewport .ngRow .ngCell.col0 .ngSelectionCheckbox:checked').parents('.ngRow').find('.ngCell.col0-1').toArray();
|
||||
else a$Tracks.push($Selector.data('track'));
|
||||
|
||||
$.each(a$Tracks, function(iKey, oTrack){
|
||||
var $Track = $(oTrack);
|
||||
$Track.find('.track-color').css('animation', 'rainbow 1s infinite');
|
||||
var sTrackId = $Track.data('track-id');
|
||||
|
||||
asTracks[sTrackId].post.features[0].properties.color = sColor;
|
||||
asTracks[sTrackId].post.features[0].properties.hexcolor = sColor;
|
||||
|
||||
var asPost = {
|
||||
type: asTracks[sTrackId].post.features[0].type,
|
||||
properties: asTracks[sTrackId].post.features[0].properties,
|
||||
geometry: asTracks[sTrackId].post.features[0].geometry,
|
||||
};
|
||||
|
||||
asPost.properties.localId = sTrackId;
|
||||
|
||||
$.ajax({
|
||||
url: 'https://www.gaiagps.com/api/objects/track/'+sTrackId+'/',
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(asPost)
|
||||
})
|
||||
.done(function(asResult){
|
||||
$.ajax({url:'https://www.gaiagps.com/api/objects/track/'+sTrackId+'/'}).done(function(asTrack){
|
||||
asTracks[sTrackId].post = asTrack;
|
||||
$Track.find('.track-color').css({'background': asTrack.features[0].properties.hexcolor, 'animation':'none'});
|
||||
toggleSelector(false);
|
||||
});
|
||||
})
|
||||
.fail(()=>{
|
||||
$Track.find('.track-color').css({'animation':'none'});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var iColorColWidth = 50, iColorColHeight = 59;
|
||||
|
||||
//Tracks
|
||||
asTracks = {};
|
||||
$.ajax({url:'https://www.gaiagps.com/api/objects/track/?routepoints=false&show_archived=true&show_filed=true'}).done((asData) => {
|
||||
for(var i in asData) asTracks[asData[i].id] = asData[i];
|
||||
wait(function(){return ($('.ngViewport .ngRow').length > 0);}, init);
|
||||
});
|
||||
+900
@@ -0,0 +1,900 @@
|
||||
// ==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
|
||||
// ==/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 */
|
||||
/* Personnal modifications have been applied, care on upgrade */
|
||||
let gpxParser = function () {
|
||||
this.xmlSource = "";
|
||||
this.metadata = {};
|
||||
this.waypoints = [];
|
||||
this.tracks = [];
|
||||
this.routes = [];
|
||||
};
|
||||
|
||||
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');
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
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"));
|
||||
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;;
|
||||
|
||||
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");
|
||||
|
||||
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 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"));
|
||||
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;
|
||||
|
||||
routepoints.push(pt);
|
||||
}
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 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"));
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
gpxParser.prototype.getElementValue = function(parent, needle){
|
||||
let elem = parent.querySelector(needle);
|
||||
if(elem != null){
|
||||
return $('<div>').html(elem.innerHTML != undefined ? elem.innerHTML : elem.childNodes[0].data).text();
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
gpxParser.prototype.queryDirectSelector = function(parent, needle) {
|
||||
|
||||
let elements = parent.querySelectorAll(needle);
|
||||
let finalElem = elements[0];
|
||||
|
||||
if(elements.length > 1) {
|
||||
let directChilds = parent.childNodes;
|
||||
|
||||
for(idx in directChilds) {
|
||||
elem = directChilds[idx];
|
||||
if(elem.tagName === needle) {
|
||||
finalElem = elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 += '<trkpt lat="'+this.tracks[i].points[j].lat+'" lon="'+this.tracks[i].points[j].lon+'">'+
|
||||
'<ele>'+(this.tracks[i].points[j].ele || '')+'</ele>'+
|
||||
'</trkpt>';
|
||||
}
|
||||
sTrack += '<trk>'+
|
||||
'<name>'+(this.tracks[i].name || '')+'</name>'+
|
||||
'<desc>'+(this.tracks[i].desc || '')+'</desc>'+
|
||||
'<src>'+(this.tracks[i].src || '')+'</src>'+
|
||||
'<trkseg>'+sTrkSeg+'</trkseg>'+
|
||||
'</trk>';
|
||||
}
|
||||
|
||||
var sGPX = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'+
|
||||
'<gpx version="1.1" creator="Franzz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">'+
|
||||
'<metadata><name>'+sName+'</name></metadata>'+
|
||||
sTrack+
|
||||
'</gpx>';
|
||||
|
||||
return sGPX;
|
||||
};
|
||||
*/
|
||||
|
||||
class Gaia {
|
||||
static get URL() { return 'https://www.gaiagps.com'; }
|
||||
static get API() { return Gaia.URL+'/api/objects'; }
|
||||
|
||||
constructor() {
|
||||
this.asFiles = [];
|
||||
this.aoWaypoints = [];
|
||||
this.aoTracks = [];
|
||||
this.sFolderId = '';
|
||||
this.asFolder = {};
|
||||
this.sFolderName = '';
|
||||
this.progress = {current:0, total:0};
|
||||
}
|
||||
|
||||
/* FIXME: adapts on GaiaGPS upgrade */
|
||||
setLayout() {
|
||||
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();
|
||||
|
||||
//Add Feedback box
|
||||
this.$Feedback = ($('#ggu-feedback').length > 0)?$('#ggu-feedback'):($('<div>', {
|
||||
'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'):($('<input>', {
|
||||
'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) {
|
||||
var sFormattedMsg = sType.charAt(0).toUpperCase()+sType.slice(1)+': '+sMsg+(sMsg.slice(-1)=='.'?'':'.');
|
||||
console.log(sFormattedMsg);
|
||||
|
||||
let sColor = 'black';
|
||||
switch(sType) {
|
||||
case 'error': sColor = 'red'; break;
|
||||
case 'warning': sColor = 'orange'; break;
|
||||
case 'info': sColor = '#2D5E38'; break;
|
||||
}
|
||||
this.$Feedback.append($('<p>', {'style': 'color: '+sColor+';'}).text(sFormattedMsg));
|
||||
|
||||
this.$Feedback.scrollTop(this.$Feedback.prop("scrollHeight"));
|
||||
}
|
||||
|
||||
incProgress() {
|
||||
if(!this.progress.current) {
|
||||
this.progress.$Done = $('<div>', {'style':'overflow:hidden; background:#2D5E38; color: white; text-align:right;'});
|
||||
this.progress.$Left = $('<div>', {'style':'overflow:hidden; background:white; color: #2D5E38; text-align:left;'});
|
||||
this.progress.$Box = $('<div>', {'id':'ggu-progress', 'style':'margin-top:1em;'})
|
||||
.append($('<div>', {'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++;
|
||||
|
||||
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/');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//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)
|
||||
this.sFolderName = this.$InputName.val();
|
||||
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);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
//Parse GPX files to consolidate tracks & waypoints
|
||||
parseFile(sFileName, oContent) {
|
||||
this.feedback('info', 'Parsing file "'+sFileName+'"');
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
//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
|
||||
createFolder() {
|
||||
this.feedback('info', 'Looking for existing folder "'+this.sFolderName+'"...');
|
||||
|
||||
$.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');
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Build & Upload Track File
|
||||
uploadTrack(iIndex) {
|
||||
iIndex = iIndex || 0;
|
||||
|
||||
if(iIndex == 0) this.feedback('info', 'Uploading tracks...');
|
||||
|
||||
let aoTrack = this.aoTracks[iIndex];
|
||||
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,
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
//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 = $('<span>').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);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
uploadWayPoints(iIndex) {
|
||||
iIndex = iIndex || 0;
|
||||
|
||||
//Upload waypoints
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
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() {
|
||||
this.feedback('info', 'Assigning all elements to folder "'+this.asFolder.properties.name+'"');
|
||||
|
||||
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
|
||||
}
|
||||
//Assign waypoints to folder
|
||||
asFolder.waypoints = [];
|
||||
for(var w in this.aoWaypoints) asFolder.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);
|
||||
|
||||
$.ajax({
|
||||
url: Gaia.API+'/folder/'+asFolder.id+'/',
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(asFolder)
|
||||
}).done(() => {
|
||||
this.incProgress();
|
||||
this.feedback('info', 'Done');
|
||||
}).fail(() => {
|
||||
this.feedback('warning', 'Failed to assign waypoints & tracks to folder ID "'+asFolder.id+'". Retrying...');
|
||||
this.assignElementsToFolder();
|
||||
});
|
||||
}
|
||||
|
||||
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...');
|
||||
let oGaia = new Gaia();
|
||||
|
||||
/* To be adjusted on Gaia's updates */
|
||||
|
||||
//Side panel
|
||||
$('path[d="M5 4v2h14V4H5zm0 10h4v6h6v-6h4l-7-7-7 7z"]').parent().parent().on('click', () => {
|
||||
setTimeout(() => {
|
||||
oGaia.setLayout();
|
||||
}, 500);
|
||||
});
|
||||
@@ -0,0 +1,228 @@
|
||||
// ==UserScript==
|
||||
// @name GaiaGps Waypoints Mass Updater
|
||||
// @description Add missing feature: Mass update of GaiaGps Waypoints
|
||||
// @version 1.2
|
||||
// @grant none
|
||||
// @match https://www.gaiagps.com/datasummary/waypoints/
|
||||
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min.js
|
||||
// @namespace https://greasyfork.org/users/583371
|
||||
// ==/UserScript==
|
||||
|
||||
console.log('computes');
|
||||
|
||||
function init() {
|
||||
|
||||
//Event on checkboxes to enable mass icon button
|
||||
$('.ngGrid .col0 input[type="checkbox"]').change(function(){
|
||||
$MassUpdate.prop('disabled', ($('.ngGrid .col0 input[type="checkbox"]:checked').length == 0));
|
||||
});
|
||||
|
||||
//DOM Elements
|
||||
$Grid = $('.ngGrid');
|
||||
$Head = $Grid.find('.ngTopPanel');
|
||||
$Body = $Grid.find('.ngViewport');
|
||||
|
||||
iIconColLeft = $Grid.find('.colt0').width();
|
||||
iCol1Width = $Grid.find('.ngHeaderCell.ng-scope.col1.colt1').width();
|
||||
iCol1Left = parseInt($Grid.find('.ngHeaderCell.ng-scope.col1.colt1').css('left'));
|
||||
|
||||
//Insert column header
|
||||
$Head.find('.ngHeaderContainer .ngHeaderCell.col0').after(
|
||||
$('<div>', {'class':'ngHeaderCell ng-scope col0-1 colt0-1', 'style':'width:'+iIconColWidth+'px; left:'+iIconColLeft+'px;'})
|
||||
.append($('<div>', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iIconColHeight+'px;'}).text(' '))
|
||||
.append($('<div>')
|
||||
.append($('<div>', {'class':'ngHeaderSortColumn ngSorted', 'style':'cursor: pointer;'})
|
||||
.append($('<div>', {'class':'ngHeaderText ng-binding', 'style':'width:'+iIconColWidth+'px;'}).text('Icon'))
|
||||
)
|
||||
)
|
||||
);
|
||||
$Head.find('.ngHeaderCell.colt1').width(iCol1Width - iIconColWidth);
|
||||
$Head.find('.col1').width(iCol1Width - iIconColWidth).css('left', (iCol1Left + iIconColWidth)+'px');
|
||||
|
||||
//Update Icons
|
||||
update();
|
||||
|
||||
//Event on DOM change: First waypoint of the page
|
||||
oObserver = new MutationObserver((oMutations) => {update();});
|
||||
oObserver.observe($('.ngGrid .ngCanvas .ngCell.col1 .ngCellText.col1 .ng-binding')[0], {attributes: true});
|
||||
|
||||
//Event on buttons
|
||||
//$('.ga-toolbar-right .ga-toggleicons').click(() => {update();});
|
||||
}
|
||||
|
||||
function update(bWaited) {
|
||||
bWaited = bWaited || false;
|
||||
|
||||
if(!bWaited) {
|
||||
if(typeof oTimer != "undefined") clearTimeout(oTimer);
|
||||
oTimer = setTimeout((() => {update(true);}), 200);
|
||||
return;
|
||||
}
|
||||
|
||||
oObserver.disconnect();
|
||||
oObserver.observe($('.ngGrid .ngCanvas .ngCell.col1 .ngCellText.col1 .ng-binding')[0], {attributes: true});
|
||||
|
||||
console.log('updating');
|
||||
|
||||
$Body.find('.ngRow').each(function(key, elem){
|
||||
var $Row = $(elem);
|
||||
var sWaypointId = $Row.find('.col1 a.ng-binding').attr('href').split('/')[3];
|
||||
|
||||
//Icon
|
||||
var sIcon = asWaypoints[sWaypointId].icon;
|
||||
if(sIcon=='') sIcon = 'red-pin-down.png';
|
||||
|
||||
//Add Icon
|
||||
if(!$Row.find('.ngCell.col0-1.colt0-1').length) {
|
||||
|
||||
//Move Col1
|
||||
$Row.find('.colt1').width(iCol1Width - iIconColWidth);
|
||||
$Row.find('.col1').width(iCol1Width - iIconColWidth).css('left', (iCol1Left + iIconColWidth)+'px');
|
||||
|
||||
$Row.find('.ngCell.col0.colt0').after(
|
||||
$('<div>', {'class':'ngCell col0-1 colt0-1', 'style':'cursor: pointer; width:'+iIconColWidth+'px; left:'+iIconColLeft+'px;'})
|
||||
.data('icon', sIcon)
|
||||
.data('waypoint-id', sWaypointId)
|
||||
.append($('<div>', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iIconColHeight+'px;'}))
|
||||
.append($('<div>')
|
||||
.append($('<div>', {'style':'ngSelectionCell', 'style':'margin: 10px 0;text-align: center;'})
|
||||
.append($('<img>', {'src':asIcons[sIcon].src, style:'width: 20px;', 'class':'waypoint-icon'}))
|
||||
)
|
||||
)
|
||||
//Display Selector
|
||||
.click(function(){
|
||||
toggleSelector(null, $(this));
|
||||
})
|
||||
);
|
||||
}
|
||||
//Update Icon
|
||||
else {
|
||||
$Row.find('.ngCell.col0-1.colt0-1')
|
||||
.data('icon', sIcon)
|
||||
.data('waypoint-id', sWaypointId)
|
||||
.find('img.waypoint-icon')
|
||||
.attr('src', asIcons[sIcon].src);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSelector(bShow, $Waypoint){
|
||||
bShow = bShow || !$Selector.is(':visible');
|
||||
$Selector.add($Background).toggle(bShow);
|
||||
if(bShow) {
|
||||
$Selector
|
||||
.data('waypoint', $Waypoint)
|
||||
.css({top: ($Waypoint.offset().top + iIconColHeight)+'px', left: ($Waypoint.offset().left)+'px'})
|
||||
.find('li[data-value="'+$Waypoint.data('icon')+'"]').addClass('Mui-selected');
|
||||
}
|
||||
else {
|
||||
$Selector
|
||||
.find('li.Mui-selected').removeClass('Mui-selected');
|
||||
}
|
||||
return bShow;
|
||||
}
|
||||
|
||||
function setIcon(){
|
||||
var sIcon = $(this).attr('data-value');
|
||||
|
||||
var a$Waypoints = [];
|
||||
if($Selector.data('waypoint').data('waypoint-id') == 'mass') a$Waypoints = $('.ngGrid .ngViewport .ngRow .ngCell.col0 .ngSelectionCheckbox:checked').parents('.ngRow').find('.ngCell.col0-1').toArray();
|
||||
else a$Waypoints.push($Selector.data('waypoint'));
|
||||
|
||||
$.each(a$Waypoints, function(iKey, oWaypoint){
|
||||
var $Waypoint = $(oWaypoint);
|
||||
$Waypoint.find('img').attr('src', sLoader);
|
||||
var sWaypointId = $Waypoint.data('waypoint-id');
|
||||
|
||||
$.ajax({'url':'https://www.gaiagps.com/api/objects/waypoint/'+sWaypointId+'/'}).done(function(asWaypoint){
|
||||
$.ajax({
|
||||
url: 'https://elevation.gaiagps.com/geojson/',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(asWaypoint.geometry)
|
||||
})
|
||||
.done(function(asGeo){
|
||||
asWaypoint.geometry = asGeo;
|
||||
asWaypoint.properties.icon = sIcon;
|
||||
$.ajax({
|
||||
url: 'https://www.gaiagps.com/api/objects/waypoint/'+sWaypointId+'/',
|
||||
type: 'PUT',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(asWaypoint)
|
||||
})
|
||||
.done(function(asResult){
|
||||
$.ajax({url:'https://www.gaiagps.com/api/objects/waypoint/'+sWaypointId+'/'}).done(function(asWaypoint){
|
||||
$Waypoint.find('img').attr('src', asIcons[asWaypoint.properties.icon].src);
|
||||
toggleSelector(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var iIconColWidth = 50,
|
||||
iIconColHeight = 40;
|
||||
|
||||
sLoader = ' data:image/gif;base64,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';
|
||||
|
||||
asIcons = {};
|
||||
$Icons = $('<ul class="MuiList-root MuiMenu-list MuiList-padding" role="listbox" tabindex="-1" style="display: flex; flex-wrap: wrap; width: 480px; height: 376px; flex-grow: 1;"><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="black pin" data-value="black-pin.png"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAEBklEQVRo3r2ZQWgUVxjHv12iQVyiCK4JiK2KARFPNqIX2fYm1IsHQZD2pmTJoUYEL8tCCt4kHnITaUUDoijkIOSg5KJQ8dCgTUsOira0LnoKm4KJyfw8vPlmdndms/vezOx/j2/e9/+9975535u3Ig5iP+NMM8cideosMsc04+yX7EWRCV7RTq+YoJideYEqdTqpTpVCFvanqdGtapxO1zxHFS+Mv+rNUqbEMAUKDFOizCyrDU/gUSWXlv0W7jUMzhtlAIn5DTBKrRHiHlvSGX1gv+JV2Bprrr+tVFjxGhCSzwLVcOzHNjTX37HGeagmTz0/2Et2d2UvCLt5GeZCknSkoJlf87q3NwjBLNQSvJQ6/StdTn7zQgS54LoMFHXbqVjbC0Il3Jrcdkd+1umPy/xDTLJAnToLTHIo9o0IlmHCDcDf80cjoTczxXrT1rfOFJsjz40GNcKt4vm73kDE/kns/vskgjAQ7o72lZJx03M2Mq6ptiVgKvLsrDaN2wNMm57lyNqvtwVYj+RCWZum7QHmTM9SS8jJDcvgZMvTJW2YswdYND2HW0IubAiw0PL0sDYs2gP4e0ChJWS9w2mk+elC0GAP8Mn07E8E0B8UUnuAj6ZnMdESFLXhoz3Aa9PzQKIkPKANr+0BnpueJxK9hie06bk9wG3T80KijeiCNt2xB6jET6rNVtywYBV7gDOm5+OYOtdtMRIe6wNn7AH2mp7/e32x1b5zORb6WNZitNelHv5r+o44HUcEYUTH/5/becA/jl9yBrikAPfdAMY0uVwBgmQdcwPws2DN2+5kv521JBnQeCg76wRwNsmBzAe4aiLMOAHMKMBVd4AjugiD1va7wgU4Iu7Sb6zL1gCXky+AiAgXTZS/rAH+VICLyQB2smriHLeyPx7cZbBTkomHJtINK4AbCvBQkorv/cOWV+javkBdE/BUcoA+3vuL2TXATzr+9/RJcunJ4B82dWW/ib/dTwGxADtYNvF+7ArgB7VfZoekI67rmTfX0T7HHwpwXdISe/js51RHgFNq/5k9kp70iPq0I8BTBbgtaYrDGve7De2/DY+JhyVd8cjEfbYhwDO1fyRpi2809sm29ifD8Y9I+uKBiT3f5l3I8bvaP5AsxEHWTPxzsQDnwo+Eg5KN+MU4vI18tAv9vFWAXyUr8RUrxuNKBOBKcBPA15KduKa1cVfLASyof9ckS7GND8bnZhPATR3/B7ZJtuK8XsQfDeyPEvyhc16yFnnmjdcL8ghCnhc6/nnykr3Cy79y82UklKQ34q7xW/KGGGJJp/+u9EoMseR/M32a8a/zWGJIeif9cm7QmPRS5PUWzddvPUm/JoR9vAns37BPei8GucU73nGLQfcoXwDnhWdKwxgN/gAAAABJRU5ErkJggg==" width="24"></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="blue pin" data-value="blue-pin-down.png"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAExElEQVR4AeWaA7AsRxfH+/ti27adFGPbSbniFGLbKMW2nTzbtt8+2/bep4v5Zf/Fvqf2re5uz+zmV3Wqpma6z7/7tHvXhQI4Cngc+BXoA0wF0jI9652+KY3SuloA2Bd4HUhRPCnllY9qrPjOwCtAmpaTli/5rJbK3wwsofwske8kV/x/wCtARBbqm6Kox7zlPNp/Ele2G8Fpvw1k3697IdOz3umb0igt2YmkIa2kVX4H4C+ysHTD5uiRfpPY/5te7PR594JMaZVHecmOtHZIUsvbyrO5sSl6fdh09vm6pypVkimvfMhX9iAkoCcAr2Rr9QtbDVMlymLyJZ8YpJ2ECS/CI7ViHcf81E8FL6vJp3wbpH1znEvdEtvytvLlDoI0sqwOO8fd9TVOTbevjElDWrEOBWBfu8nRZKUChjBpGdJBd4zAG7brFzrbn/3HID4eN4dJK9Ok6xtketY7fSt0dcg2FF53oTB7e63ZeQu9+5c9+CI1D7vH8dA3pVHavP6kaUiFO9WZHZ42Lvkq32fBSgpEafMGQZp2xxjkFAk8joe2rvlaS61aJMqT16+0DY+HCMCveGj/nm/Mq6GKRXnyzQnSNvwaIgB98NAhJlchNbmViPLm9C1tQ58QAZiKh05yuQqpGb5ElDenb2kbpoYIQBoPHWdzFVLLXIkob07f0jakQwRgEx57fNkjtgBI27A5RACW43H4931iGwLSNiwPEYCZeJyqOSCmSVDahpkhAjAMj8vbDo9tGZS2YViIAPyMx0P9Jsa2EZK24ZcQAXgpSzcNthXOM7xeChGA2/HoPX+FChP8MCSTtuH2EAE4Ao/1DY3Rrl94BQ5wHJZJsy6jTXOOcCEAFuJx3j9DVaigJk3DIhcKew3+3KCpwQMgTcPfIQPwoJm0ggdAmoYHXSjsPNAYRdGB3/YOVnlpSTPs+M9zLXZnj/HBAiAtQ8qFBngbj46zlwYLgLQMb8cRgLPsMDjih74Vr7w0snT/s1wcAOPxeH5w5VcDaRhSLi6Ax/CYsqqu4gGYvCqN4bE4A7APUI/HRa0r9/OYfBukvY+LE6A1Ht9Pml+xAMi3obWLG+BaPNL1jZG5JyyLyad805zrkhCArYHFeDwzaErZA/D0wCkYFkvbJQF7R7AgvZHd7JG2BSZf8zM+DS+5pADsCdThcV+vVNkCcG/Gl6FOmi5JAB/kv9EtzSZmfBk+cEkDOBRowOPWzqNbXHn5MDRIyyURe2E6eNHqFgdAPgw/u6QCnILhavvjaRF2VSavRRouyQCd8BiyuPReoLyGTi7pAGdjuLHjqKIrrzxZOMdVA0ArPMYvX1d0AMZl8hhauWoBOAFoxOOenoXfGCmtoUk+XTUBfI/H3HUb8v6ULlMapTX84KoN4DBgMx4vDZmWNwBKY5CPw101ArxrT4q5rs30zZ745MNVK8BuwDI8fpy8YIsB0DeD8u7mqhngfpoTnd9qKLbyeqdveCivq3aA/wNj8Ri1dA07e5XXs94ZlOf/rhYALsTD/sFSzxblcbUE8Aceazc3REf+2BeZnvFQWldrAAeo3ubXpE0yPJRGaV0tAjxIfh50tYaZEIexZYYqjatlgCOBWRj0Tt/cfwFgf+BHYK5Mz3rnYuBfL+mM2UqtTGQAAAAASUVORK5CYII=" width="24"></li><li class="MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button" tabindex="-1" role="option" aria-disabled="false" style="padding-left: 8px; padding-right: 8px;" title="brown pin" data-value="brown-pin.png"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAE2klEQVR4AeWaA5AkyRvF84+zbdu2bdu2zbm1jmvbtm3btr07Vv/u3jE7o4fbk1U994vIiIqqzO/l9xKFbuML4BTgY6ANMAxYACSr6FjndE11VNeUBoDDgfLALIqO2pRXjERMfF+gDJDMrqMYZRQzUZJ/GFhP/Fmv2GFO/D9AGSBCDHKysyILp4ykV/2yNPnmWaq/dTsVnrhQRcc6p2uojuoSm4g0pBW25PcCOhKD5G2bIj3rlaHikxeR9MDphSqqqzZqS2w6SjNMI98R3BHPjAxuU53yj1+gpIpV1FYxFCu2CSGYCUCZWKPe4LPHlERcimLlMRvKhGHDi2Cxftl8fnj5OnU8rkUxFdtB2g8Heatb7468m3y8TYgxE9arL0FPfa1TZ9qXTJFGjD2hTBBPeMlYaLNSB30UaTkke31iBCq4U7+wu33Nd+9mTI9mbFi5iIy0FBUd65yuFfruEGMplDe+cJ/tdc8uqNNlHzmH8X1akZubSx7omuqoboHxpOkwy9tbnfuEV9BDjhJaMmMshUR1CzRBmtImmlN8GPAxFnp0LWi0NKpFRG0KjCtth499GNAGCz2/F7TmNbWLiNoUuCdI26GNDwOGYaGXmPw6qc2tmKhtvrGl7TDMhwELsNCbXH6d1A5fTNQ239jSdljgw4BkLPQ6m18ndZsrJmqbb2xpLine truncated
|
||||
$Icons.find('li').each((iKey, sElem)=>{
|
||||
var $Elem = $(sElem);
|
||||
asIcons[$Elem.attr('data-value')] = {'title': $Elem.attr('title'), 'src':$Elem.find('img').attr('src')};
|
||||
});
|
||||
|
||||
//Background
|
||||
$Background = $('<div>', {'aria-hidden':'true', 'style':'position: fixed; inset: 0px; background-color: transparent;'}).click(function(){toggleSelector(false);});
|
||||
|
||||
//Build Icon Selector
|
||||
$Selector = $('<div>', {
|
||||
'class':'MuiPaper-root MuiMenu-paper MuiPopover-paper MuiPaper-elevation8 MuiPaper-rounded',
|
||||
'style':'z-index: 1300; opacity: 1; transform: none; min-width: 52px; transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 67ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transform-origin: 0px 0px 0px;'
|
||||
})
|
||||
.append($('<ul>', {
|
||||
'class':'MuiList-root MuiMenu-list MuiList-padding',
|
||||
'role':'listbox',
|
||||
'tabindex':'-1',
|
||||
'style':'display: flex; flex-wrap: wrap; width: 480px; flex-grow: 1;'
|
||||
}));
|
||||
|
||||
$.each(asIcons, function(sIcon, asAttr) {
|
||||
$Selector.find('ul').append(
|
||||
$('<li>', {
|
||||
'class': 'MuiButtonBase-root MuiListItem-root MuiMenuItem-root MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button',
|
||||
'tabindex': '-1',
|
||||
'role': 'option',
|
||||
'style': 'padding-left: 8px; padding-right: 8px;',
|
||||
'title': asAttr.title,
|
||||
'data-value': sIcon
|
||||
})
|
||||
.click(setIcon)
|
||||
.append(
|
||||
$('<img>', {'src': asAttr.src, 'width': '24'})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
//Add Selector & background to DOM
|
||||
$Selector.add($Background).hide().appendTo('body');
|
||||
|
||||
//waypoints
|
||||
asWaypoints = {};
|
||||
$.ajax({url:'https://www.gaiagps.com/api/objects/waypoint/?routepoints=false&show_archived=true&show_filed=true'}).done((asData) => {
|
||||
for(var i in asData) {
|
||||
asWaypoints[asData[i].id] = asData[i];
|
||||
}
|
||||
setTimeout(init, 2000);
|
||||
});
|
||||
|
||||
//Icon Button for mass update
|
||||
$MassUpdate = $('<button>', {'type':'button', 'class':'ga-button ga-button-flat ga-toolbar-button', 'disabled':'disabled'})
|
||||
.data('waypoint-id', 'mass')
|
||||
.click(function(){toggleSelector(true, $(this));})
|
||||
.append($('<i>', {'class':'material-icons ibmid'}).text(''))
|
||||
.append(' ')
|
||||
.append($('<span>', {'class':'ibmid ga-toolbar-button-text'}).text('Icon'));
|
||||
|
||||
$('#DatasummaryController .ga-toolbar .ga-toolbar-left .ga-button').eq(1).after($MassUpdate);
|
||||
Reference in new issue
Block a user