// ==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( $('
', {'class':'ngHeaderCell ng-scope col0-1 colt0-1', 'style':'width:'+iIconColWidth+'px; left:'+iIconColLeft+'px;'}) .append($('
', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iIconColHeight+'px;'}).text(' ')) .append($('
') .append($('
', {'class':'ngHeaderSortColumn ngSorted', 'style':'cursor: pointer;'}) .append($('
', {'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( $('
', {'class':'ngCell col0-1 colt0-1', 'style':'cursor: pointer; width:'+iIconColWidth+'px; left:'+iIconColLeft+'px;'}) .data('icon', sIcon) .data('waypoint-id', sWaypointId) .append($('
', {'class':'ngVerticalBar ngVerticalBarVisible', 'style':'height: '+iIconColHeight+'px;'})) .append($('
') .append($('
', {'style':'ngSelectionCell', 'style':'margin: 10px 0;text-align: center;'}) .append($('', {'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 = $('
'); $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 = $('
', {'aria-hidden':'true', 'style':'position: fixed; inset: 0px; background-color: transparent;'}).click(function(){toggleSelector(false);}); //Build Icon Selector $Selector = $('
', { '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($('
    ', { '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( $('
  • ', { '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( $('', {'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 = $('