// ==UserScript==
/* globals jQuery, $, L, waitForKeyElements, cloneInto */
// @name Delorme Grid Multi-state Overlay
// @author rragan (derived from cachetur Assistant code)
// @version 1.0.0.5
// @description Companion script for geocaching.com
// @include https://www.geocaching.com/play/map*
// @include http://www.geocaching.com/play/map*
// @include https://www.geocaching.com/map/*
// @include http://www.geocaching.com/map/*
// @include https://www.geocaching.com/play/map*
// @include http://www.geocaching.com/play/map*
// @include https://www.geocaching.com/geocache/*
// @include http://www.geocaching.com/geocache/*
// @include https://www.geocaching.com/seek/cache_details.aspx*
// @include https://www.geocaching.com/plan/lists/BM*
// @include http://www.geocaching.com/play/geotours/*
// @include https://www.geocaching.com/play/geotours/*
// @include http://project-gc.com/*
// @include https://project-gc.com/*
// @connect self
// @connect rragan.atwebpages.com
// @connect raw.githubusercontent.com
// @grant GM_xmlhttpRequest
// @grant GM_info
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_addStyle
// @grant GM_addStyle
// @grant unsafeWindow
// @run-at document-end
// @copyright 2021+, rragan
// @require https://code.jquery.com/jquery-latest.js
// @require https://gist.github.com/raw/2625891/waitForKeyElements.js
// @updateURL https://github.com/rragan/DeLormeGridOverlay/raw/main/multiState.meta.js
// @downloadURL https://github.com/rragan/DeLormeGridOverlay/raw/main/multiState.user.js
// ==/UserScript==
function wait4containers() {
let mapNode = document.querySelector("div.map-container, .leaflet-container");
if (mapNode == null) {
// the node doesn't exist yet, wait and try again
window.setTimeout(wait4containers, 300);
return;
}
//Handy tool to convert GSAK data to JSON grid form: https://www.convertcsv.com/csv-to-json.htm Use CSV to JSON array
// Generic State DeLorme Grid coordinates
this.$ = this.jQuery = jQuery.noConflict(true);
let _dgPage = "unknown";
let _gridLayer = [];
let _pagenumberLayer = [];
let _initialized = false;
let _pageNumbersShown = false;
let _dgGridColor = "#CC00FF";
let _dgGridArchivedColor = "#000";
let _dgWhichGrid = "";
let _stateCode = "";
let _newView = "";
let _changedState;
let options = '';
// # Sign icon
let numberImg = '';
console.log("Starting Delorme Grid " + GM_info.script.version);
$("#gc-header, #GCHeader").show();
let pathname = window.location.pathname;
let domain = document.domain;
if (domain === "www.geocaching.com") {
if (pathname.indexOf("/play/map") > -1) _dgPage = "gc_map_new";
else if (pathname.indexOf("/map/") > -1) _dgPage = "gc_map";
}
console.log("Running in " + _dgPage + " mode");
if (_dgPage === "gc_map_new") {
var checkExist = setInterval(function() {
if ($('.mapboxgl-canvas').length) {
console.log("Exists!");
clearInterval(checkExist);
}
}, 100); // check every 100ms
// See if Cachetur Assistant has got the map object. If so, just use it.
if (unsafeWindow.cacheturGCMap) {
console.log("MULTISTATE was BEAT TO MAP BY CTA");
unsafeWindow.delormeGCMap = unsafeWindow.cacheturGCMap;
} else if (unsafeWindow.gcMap) { // If anyone has set the object in a shared place, use it
console.log("MULTISTATE sees MAP from other extension");
unsafeWindow.delormeGCMap = unsafeWindow.gcMap;
} else {
console.log("DeLorme Doing dirty trick to take over Geocaching.com's leaflet object");
let originalLMap = L.Map;
L.Map = function(div, settings) {
unsafeWindow.delormeGCMap = new originalLMap(div, settings);
L.Map = originalLMap;
console.log("MULTISTATE got MAP");
unsafeWindow.gcMap = unsafeWindow.delormeGCMap; // Save a copy in a shared place for others
return unsafeWindow.delormeGCMap;
};
}
}
$(document).ready(function() {
ctInit();
});
// Init the menu and data needed
function ctInit() {
console.log("Initializing Delorme Grid");
// Build state list menu
dgDataCall("stateCodesList", function(data) {
var codes = data.list;
let storedState = GM_getValue("dg_selected_state", 0);
var selected = "";
var opt = "";
if (data.list.length > 0) {
codes.forEach(function(item) {
if (storedState === item.code) {
opt = '';
} else {
opt = '';
}
options = options + opt;
});
}
if (!_initialized) {
ctPrependToHeader('
DeLorme Grid ' + numberImg + '');
}
dgDataCall(storedState, function(data) {
let unsafeLeafletObject = ctGetUnsafeLeafletObject();
if (!data.grid) {
//alert("No grid data available");
} else {
_dgWhichGrid = data.grid;
_stateCode = data.state;
dgCreateGrid();
_initialized = true;
}
}
);
});
};
function ctDrawGrid() {
GM_addStyle(".map-label { position: absolute; bottom: 0;left: -50%; display: flex; flex-direction: column; text-align: center; ");
GM_addStyle(".map-label-content { order: 1; position: relative; left: -50%; background-color: #fff; border-radius: 5px; border-width: 2px; border-style: solid; border-color: #f00; padding: 3px; white-space: nowrap;");
let markers = [];
let numbers = [];
let unsafeLeafletObject = ctGetUnsafeLeafletObject();
clearLayers();
let firstLatLon = true;
let newView;
let grid = [];
for (var i = 0; i < _dgWhichGrid.length; i++) {
var latline = _dgWhichGrid[i];
var lat1 = parseFloat(latline[0]);
var lat2 = parseFloat(latline[1]);
var lng1 = parseFloat(latline[2]);
var lng2 = parseFloat(latline[3]);
var bounds = [
[lat1, lng1],
[lat2, lng2]
];
// capture first lat/lon for shifting map view
if (firstLatLon) {
newView = new L.LatLng(lat1, lng1);
firstLatLon = false;
}
// Black grid color for archived DeLorme challenges - menu name ends with :(
var gridColor = _dgGridColor;
if (latline[4].endsWith(":(")) {
gridColor = _dgGridArchivedColor;
}
var rect = L.rectangle(bounds, {
color: _dgGridColor,
fill: false,
weight: 1
});
var pageText = latline[4];
pageText = pageText.replace(/page /i, "");
var icon = L.divIcon({
iconSize: null,
html: ''
});
if (latline[4]) {
var pageNumber = L.marker([lat2 + .15, lng2 + .15], {
icon: icon
});
numbers.push(pageNumber);
}
grid.push(rect);
}
_gridLayer = L.layerGroup(grid);
unsafeWindow.delormeGridLayer = cloneInto(_gridLayer, unsafeWindow);
console.log("Injecting grid");
unsafeLeafletObject.addLayer(unsafeWindow.delormeGridLayer);
if(_changedState) {
unsafeLeafletObject.flyTo(newView, 6);
_changedState = false;
}
// Prepare page number layer.
_pagenumberLayer = L.layerGroup(numbers);
unsafeWindow.delormePageNumberLayer = cloneInto(_pagenumberLayer, unsafeWindow);
}
function clearLayers() {
let unsafeLeafletObject = ctGetUnsafeLeafletObject();
if (unsafeWindow.delormeGridLayer) {
unsafeLeafletObject.removeLayer(unsafeWindow.delormeGridLayer);
}
if (unsafeWindow.delormePageNumberLayer) {
unsafeLeafletObject.removeLayer(unsafeWindow.delormePageNumberLayer);
}
_pageNumbersShown = false;
}
function ctPrependToHeader(data) {
console.log("Injecting icon in menu");
let header;
if (_dgPage === "gc_map_new") header = $('.user-menu,.profile-panel');
else if (_dgPage === "gc_map") header = $('.user-menu');
if (header) {
header.prepend(data);
}
// Toggle page numbers on and off
$("#stateIcon").click(function() {
let unsafeLeafletObject = ctGetUnsafeLeafletObject();
if (_pageNumbersShown) {
_pageNumbersShown = false;
if (unsafeWindow.delormePageNumberLayer) {
unsafeLeafletObject.removeLayer(unsafeWindow.delormePageNumberLayer);
}
} else {
_pageNumbersShown = true;
console.log("showing delorme page numbers");
unsafeLeafletObject.addLayer(unsafeWindow.delormePageNumberLayer);
}
});
$("#stateSelect").change(function() {
let unsafeLeafletObject = ctGetUnsafeLeafletObject();
let id = $("#stateSelect").val();
if (id !== "XX") {
GM_setValue("dg_selected_state", id);
_changedState = true;
_initialized = true;
clearLayers();
ctInit();
} else {
GM_setValue("dg_selected_state", "");
}
});
}
function dgCreateGrid() {
setTimeout(function() {
ctDrawGrid(_dgWhichGrid);
}, 1500);
}
function ctGetUnsafeLeafletObject() {
if (_dgPage === "gc_map" && unsafeWindow.MapSettings) return unsafeWindow.MapSettings.Map;
else if (_dgPage === "gc_map_new" && unsafeWindow.delormeGCMap) return unsafeWindow.delormeGCMap;
else {
return null;
}
}
// Call for DG data load
function dgDataCall(param, callback) {
let appId = "DGOverlay " + GM_info.script.version + " - ";
var url = "https://raw.githubusercontent.com/rragan/DeLormeGridOverlay/main/gridData/" + param + ".json";
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(data) {
try {
let response = $.parseJSON(data.responseText);
if (typeof(response.error) === "undefined") {
callback(response);
} else {
callback("");
}
} catch (e) {
console.warn("Failed to verify response: " + e);
callback("");
}
},
onerror: function() {
callback("");
},
ontimeout: function() {
callback("");
}
});
}
}
wait4containers();