' + template(GM_getResourceText("script_infoHTML"), { 'script_name' : GM.info.script.name, 'script_version' : GM.info.script.version, }) + '
' + '' + (GM_getValue('userid') ? infoUser() : infoNoUser()) + '
' + '// ==UserScript== // @name Violent Coords // @namespace http://violentcoords.com // @version 0.3 // @description Exchange Corrected Coordinates. // @author Violent Cacher // @resource coordCSS https://barnyruilt.alwaysdata.net/style.css // @resource homepageHTML https://barnyruilt.alwaysdata.net/template/homepage.html // @resource no_user_infoHTML https://barnyruilt.alwaysdata.net/template/no_user_info.html // @resource membershipHTML https://barnyruilt.alwaysdata.net/template/membership.html // @resource user_infoHTML https://barnyruilt.alwaysdata.net/template/user_info.html // @resource script_infoHTML https://barnyruilt.alwaysdata.net/template/script_info.html // @match https://www.geocaching.com/account/settings/membership // @match https://www.geocaching.com/geocache/* // @match https://www.geocaching.com/map/* // @match *://*.barnygeeft.com/* // @grant GM_addStyle // @grant GM_deleteValue // @grant GM_getResourceText // @grant GM_getValue // @grant GM_setValue // @grant GM_setClipboard // @grant GM_xmlhttpRequest // @connect barnyruilt.alwaysdata.net // @downloadURL none // ==/UserScript== // https://github.com/Tampermonkey/tampermonkey/issues/475 (function() { 'use strict'; const link_id = 'linkToCorrectedCoords'; const state_id = 'elementForStateInformation'; const url = 'https://barnyruilt.alwaysdata.net'; const wait_time_increase = 200; var button_info = { error: { class: 'gc-pink-background', text: '☠', }, found: { class: 'gc-green-background', text: '✓', title: 'Corrected Coordinate found, click to update', }, loading: { class: 'gc-gray-background', text: '❄', //animate: ['◷', '◶', '◵', '◴'], //animate: ['▝', '▗', '▖', '▘' ], animate: ['⠈⠉', '⠀⠙', '⠀⠸', '⠀⢰', '⠀⣠', '⢀⣀', '⣀⡀', '⣄⠀', '⡆⠀', '⠇⠀', '⠋⠀', '⠉⠁'], timeout: 97, title: 'Loading', }, location: { class: 'gc-blue-background', text: '⊕', title: 'Click to get Corrected Coordinate at this location', }, min: { class: 'gc-yellow-background', text: '?', title: 'You\'ve requested this coordinate', }, multipleoff: { class: 'gc-blue-background', text: '⬀ Bulk submit', title: 'Enable bulk submit', }, multiplerunning: { class: 'gc-green-background', text: '⬈ Bulk submit', title: 'Bulk submit in progress', }, notfound: { class: 'gc-red-background', text: '✗', title: 'No Corrected Coordinate Available', }, plus: { class: 'gc-yellow-background', text: '✓', title: 'You\'ve contributed this coordinate', }, unknown: { class: 'gc-blue-background', text: '?', title: 'Click to check for Corrected Coordinates', }, wait: { class: 'gc-yellow-background', text: '◔', title: 'Not so fast cowboy', }, }; var time_period = [ { name: 'seconds', factor: 1000 }, { name: 'minutes', factor: 60 }, { name: 'hours', factor: 60 }, { name: 'days', factor: 24 }, { name: 'months', factor: 31 }, { name: 'years', factor: 12 }, ] var wait_time = 0; var button_timeout = null; var multiple_timer = null; // Geocaching functions function geocachePage() { //console.debug('geocachePage'); if ('undefined' === typeof(userInfo)) { setTimeout(geocachePage, 50); return; } GM_setValue('userid', userInfo.ID); username = document.getElementsByClassName('username').value; //userid = userInfo.ID; GM_setValue('username', username); //GM_setValue('userid', userInfo.ID); createButton(); waitForCorrected(); } function waitForCorrected() { //console.debug('waitForCorrected'); gccode = document.getElementById('ctl00_ContentBody_CoordInfoLinkControl1_uxCoordInfoCode').innerHTML; coords = document.querySelector('span.myLatLon'); coords = coords ? coords.innerHTML : '' if (coords) { note = document.getElementById('viewCacheNote'); note = note ? note.innerHTML : ''; updateButton('loading'); sendCoordinate(); } else { setTimeout(waitForCorrected, 150); } } function postData(data) { return Object.keys(data).map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])).join('&'); } function updateButton(state, title = '') { if (button_timeout) { clearTimeout(button_timeout); button_timeout = null; } if (button_info[state].animate) { if (!button_info[state].frame) { button_info[state].frame = 0; } if (!button_info[state].timeout) { button_info[state].timeout = 314; } button_info[state].text = button_info[state].animate[button_info[state].frame]; button_info[state].frame = (button_info[state].frame + 1) % button_info[state].animate.length; button_timeout = setTimeout(() => updateButton(state, title), button_info[state].timeout); } if (('' === title) && (button_info[state].title)) { title = button_info[state].title } var button = document.getElementById(link_id); var old_state = button.getAttribute('data-state'); if (old_state != state) { if (old_state) { button.classList.remove(button_info[old_state].class); button.classList.remove(button_info[old_state].class + '-hover'); } button.setAttribute('data-state', state); button.classList.add(button_info[state].class); button.classList.add(button_info[state].class + '-hover'); } button.title = title; button.innerHTML = button_info[state].text; } function createButton() { var button = document.createElement('div'); button.id = link_id; button.style = 'margin-right: 0.5em'; button.classList.add('coords-box'); button.setAttribute('data-state', 'wait'); button.addEventListener('click', onClick); var link = document.getElementById('uxLatLonLink'); link.parentNode.insertBefore(button, link) updateButton('unknown'); } function createLocationButton() { var br = document.createElement('br'); var button = document.createElement('div'); button.id = link_id; button.style = 'margin-right: 0.5em'; button.classList.add('coords-box'); button.setAttribute('data-state', 'wait'); button.addEventListener('click', onClick); var span = document.createElement('span'); span.id = state_id; span.innerHTML = 'Click to get closest corrected coordinates'; var node = document.getElementById('m_cacheTypes') insertAfter(button, node); insertAfter(span, button); updateButton('location') } function onClick() { var args = {} switch (document.getElementById(link_id).getAttribute('data-state')) { case 'found': getCoordinate(); break; case 'location': locationCoordinate(); break; case 'multipleoff': multipleStart(); break; case 'multiplerunning': multipleStop(); break; case 'unknown': checkCoordinate(); break; } return false } function checkCoordinate() { updateButton('loading'); console.log('Checking: ' + gccode); GM_xmlhttpRequest({ url: url, method: 'POST', headers: { 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', }, data: postData({ 'gccode': gccode }), onload: (data) => { if (JSON.parse(data.responseText).hasCorrected) { updateButton('found'); } else { updateButton('notfound'); } }, onerror: (data) => { updateButton('error', 'Something went wrong while checking coordinates'); console.error(data) }, }); } function getCoordinate() { updateButton('loading'); if (requestToken(getCoordinate)) { return; } GM_xmlhttpRequest({ url: url, method: 'POST', headers: { 'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, data: postData({ 'prid': userInfo.ID, 'gccode': gccode, 'token': GM_getValue('token_value'), }), onload: (resp) => { var data = JSON.parse(resp.responseText) if (data.error) { updateButton('error', data.error); } else if (data.wait) { waitCoordinate(Date.now() + 1000 * data.wait, 'found') } else if (data.latitude && data.longitude) { if((Math.abs(data.latitude) > 90) || (Math.abs(data.latitude) > 180)) { updateButton('error', 'Invalid coordinates'); } else { updateCoordinate(data) } } else { updateButton('notfound', 'No coordinates returned'); console.error(data); } }, onerror: (data) => { updateButton('error', 'Something went wrong while retreiving coordinates'); console.error(data) }, }); } function updateCoordinate(data) { $.getJSON({ url: 'https://www.geocaching.com/seek/cache_details.aspx/SetUserCoordinate', type: "POST", dataType: "json", contentType : "application/json", data: JSON.stringify({ dto: { data: { lat: data.latitude, lng: data.longitude, }, ut: userToken, } }), success: () => location.reload(), // Because groundspeak is also to lazy to use the response to update the page error: (data) => { updateButton('error', 'Something went wrong while updating coordinate'); console.error(data); }, }); } function locationCoordinate() { updateButton('loading'); if (requestToken(locationCoordinate)) { return; } var center = MapSettings.Map.getCenter(); GM_xmlhttpRequest({ url: url, method: 'POST', headers: { 'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, data: postData({ // Because sometimes center contains functions and sometimes properties 'lat': (center.lat instanceof Function) ? center.lat() : center.lat, 'lng': (center.lng instanceof Function) ? center.lng() : center.lng, 'token': GM_getValue('token_value'), }), onload: (resp) => { var data = JSON.parse(resp.responseText) if (data.error) { updateButton('error', data.error); } else if (data.wait) { waitCoordinate(Date.now() + 1000 * data.wait, 'location') } else if (data.latitude && data.longitude) { console.log(data) updateButton('location'); document.getElementById(state_id).innerHTML = '' + data.gccode + ': ' + data.distance + ' km'; } else { updateButton('error', 'No coordinates returned'); console.error(data); } }, onerror: (data) => { updateButton('error', 'Something went wrong while retreiving coordinates'); console.error(data) }, }); } function multipleClear() { GM_deleteValue('multiple_skip'); _setById('cc_skip', '?') _setById('cc_available', '?') return false; } function multipleCoordinate() { if (requestToken(multipleCoordinate)) { return; } updateButton('loading'); GM_xmlhttpRequest({ url: url, method: 'POST', headers: { //'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, data: postData({ 'multiple' : 'm', 'skip' : GM_getValue('multiple_skip') || 0, 'take' : 50, 'token': GM_getValue('token_value'), }), onload: (resp) => { var data = JSON.parse(resp.responseText); console.log(data); GM_setValue('multiple_skip', (GM_getValue('multiple_skip')*1 || 0) + data['count']); _addById('cc_set', data['updates']); _addById('cc_fullCallsRemaining', -data['count']); _setById('cc_available', data['total']); _setById('cc_skip', GM_getValue('multiple_skip')); updateButton('multiplerunning'); multiple_timer = setTimeout(multipleCoordinate, 17659); }, error: (data) => { updateButton('error', 'Something went wrong while proccesing multiple'); console.error(data) }, }); } function multipleStart() { GM_setValue('multiple', true); multipleCoordinate(); } function multipleStop() { if (multiple_timer) { clearTimeout(multiple_timer); multiple_timer = null; } updateButton('multipleoff'); GM_setValue('multiple', false); } function sendCoordinate() { //console.debug('sendCoordinate'); if (requestToken(sendCoordinate)) { return; } GM_xmlhttpRequest({ url: url, method: 'POST', headers: { //'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, data: postData({ 'gccode': gccode, 'note': note, 'prid': userInfo.ID, 'token': GM_getValue('token_value'), }), onload: (data) => { var json = JSON.parse(data.responseText); console.log(json); updateButton((1 == json['direction']) ? 'plus' : 'min'); }, error: (data) => { updateButton('error', 'Something went wrong while sending coordinates'); console.error(data) }, }); } function waitCoordinate(timestamp, next) { var delta = timestamp - Date.now(); if (delta > 0) { var i = 1; delta /= time_period[0].factor; var wait = time_period[0].factor / 4; while ((delta > 99) && (i < time_period.length)) { delta /= time_period[i].factor; wait *= time_period[i].factor; i++; } updateButton('wait', 'Please wait ' + Math.ceil(delta) + ' ' + time_period[i-1].name); setTimeout(() => waitCoordinate(timestamp, next), wait); return; } updateButton(next); } function requestToken(func = null) { //console.log("requestToken()"); //console.log(Date.now() + ' now') //console.log(GM_getValue('token_expires') + ' expires'); //console.log((GM_getValue('token_requested') || 0) + ' requested'); if ( ((GM_getValue('token_expires') || 0) < (Date.now() + 5*60*1000)) && ( ((GM_getValue('token_requested') || 0) < (Date.now() - 10*60*1000)) || ((GM_getValue('token_expires') || 0) >= (GM_getValue('token_requested') || 0)) ) ) { getToken(); } if (func) { if (GM_getValue('token_expires') < Date.now()) { console.log(GM_getValue('token_expires') + ' expires'); console.log(GM_getValue('token_requested') + ' requested'); wait_time += wait_time_increase; console.debug('Waiting for token: ' + wait_time) setTimeout(func, wait_time); } else { return false; } } return true; } function getToken() { console.log('getToken()'); GM_setValue('token_requested', Date.now()); GM_xmlhttpRequest({ url: "https://www.geocaching.com/account/oauth/token", method: 'POST', headers: { //'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, onload: (resp) => { var result = JSON.parse(resp.responseText); GM_setValue('token_value', result.access_token); GM_setValue('token_expires', Date.now() + 1000 * (result.expires_in - 60)); // - 60 seconds as a safety } }); } // Homepage functions function template(text, vars) { return text.replace(/\{\{(\w*)\}\}/g, (_, x) => vars[x]); } function insertAfter(node, target) { target .parentNode .insertBefore(node, target.nextSibling); } function infoClear() { GM_deleteValue('username'); GM_deleteValue('userid'); GM_deleteValue('token_value'); GM_deleteValue('token_expires'); GM_deleteValue('token_requested'); location.reload(); } function setClipboard(event) { GM_setClipboard(event.target.getAttribute('data-set-clipboard')); } function infoUser() { var expires = new Date(GM_getValue('token_expires')); return template(GM_getResourceText("user_infoHTML"), { 'expires_iso' : expires.toISOString(), 'token_class' : GM_getValue('token_expires') < Date.now() ? 'strike' : '', 'token_value' : GM_getValue('token_value'), 'token_value_short' : GM_getValue('token_value').substr(0,8) + '...' + GM_getValue('token_value').substr(-16), 'userid' : userid, 'username' : username, }) } function _resetTimeString(seconds) { if (!seconds) { return '-'; } var date = new Date(Date.now() + 1000 * seconds); return [ ('0'+date.getHours()).slice(-2), ('0'+date.getMinutes()).slice(-2), ('0'+date.getSeconds()).slice(-2)].join(':'); } function _addById(id, value) { //console.log("addById("+id+", "+_getById(id)+" + "+value+")"); _setById(id, _getById(id)*1 + value) } function _getById(id, value) { var elem = document.getElementById(id); if (!elem) { return elem; } return elem.innerHTML; } function _setById(id, value) { //console.log('setById('+id+', '+value+')'); var elem = document.getElementById(id); if (elem) { elem.innerHTML = value; } } function addToMembershipPage(data) { if (GM_getValue('multiple_userid') != data['id']) { GM_setValue('multiple_skip', 0); GM_setValue('multiple_available', '?'); } GM_setValue('multiple_userid', data['id']) data['link_id'] = link_id; data['premium_display'] = data['premium'] ? 'block' : 'none'; data['liteCallsResetTime'] = _resetTimeString(data['liteCallsSecondsToLive']); data['fullCallsResetTime'] = _resetTimeString(data['fullCallsSecondsToLive']); data['skip'] = GM_getValue('multiple_skip')*1 || '?'; data['available'] = GM_getValue('multiple_available'); data['available'] = '?' var div = document.createElement('div'); div.innerHTML = template(GM_getResourceText("membershipHTML"), data) var node = document.getElementById('stateIndex'); insertAfter(div, document.getElementById('stateIndex')); GM_getValue('multiple') ? multipleStart() : multipleStop(); document.getElementById(link_id).addEventListener('click', onClick); document.getElementById('bulk_submit_reset').addEventListener('click', multipleClear); } function membershipPage() { if (requestToken(membershipPage)) { return }; GM_xmlhttpRequest({ url: url, method: 'POST', headers: { //'Content-type': 'application/x-www-forms-urlencoded', 'Accept': 'application/json', }, data: postData({ 'stats': 'x', 'prid': GM_getValue('userid'), 'token': GM_getValue('token_value'), }), onload: (resp) => { console.log(resp); addToMembershipPage(JSON.parse(resp.responseText)); }, onerror: (data) => { console.error('fout'); console.error(data); }, }); } function infoNoUser() { return GM_getResourceText("user_infoHTML"); } function homepage() { document.body.innerHTML = '
' + template(GM_getResourceText("script_infoHTML"), { 'script_name' : GM.info.script.name, 'script_version' : GM.info.script.version, }) + '
' + '' + (GM_getValue('userid') ? infoUser() : infoNoUser()) + '
' + '