// ==UserScript==
// @name Check PQ Date Ranges
// @namespace http://tampermonkey.net/
// @version 0.5.1
// @description Find sequences in list of PQs
// @author Gary Turner
// @match https://www.geocaching.com/pocket/
// @match https://www.geocaching.com/pocket/gcquery.aspx?guid=*
// @match https://www.geocaching.com/pocket/default.aspx?pq=*
// @icon https://www.google.com/s2/favicons?sz=64&domain=geocaching.com
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_openInTab
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_addStyle
// ==/UserScript==
let doneListener = null;
let tab = null;
let seqs = null;
function modifyTabs() {
let PQtabs = document.getElementById("Tabs");
let TabList = PQtabs.getElementsByTagName("li");
let lastTab = TabList[TabList.length-1];
let newTab = document.createElement('li');
newTab.id="GTPQDR";
let nta = document.createElement('a');
nta.href = "#DateRangePQs";
nta.text = "Date ranges";
newTab.appendChild(nta);
let dpqDiv = document.getElementById("DownloadablePQs");
let dtrngDiv = dpqDiv.cloneNode(false);
dtrngDiv.id="DateRangePQs";
let para = document.createElement("P");
para.textContent = "Date Ranges found among PQs. These are sets of 2 or more PQs that have the same name and end in a number";
dtrngDiv.appendChild(para);
PQtabs.appendChild(dtrngDiv);
lastTab.parentElement.appendChild(newTab);
$("#Tabs").tabs("refresh");
return dtrngDiv;
}
function tableRow(row,type,data){
for ( const cell of data ){
let c = document.createElement(type); c.appendChild(document.createTextNode(cell)); row.appendChild(c); }
}
function showResults(seqs,seq){
let previous = 0;
let diff = 0;
let total = 0;
var c;
const firstCache = new Date(2000,4,3);
const errColour = "#ffd0d0";
var haveCoords = 0;
var haveState = 0;
var haveCountry = 0;
for ( const aseq of seqs[seq]){
if (aseq[3][8] == "rbOriginWpt") { haveCoords = 1 };
if (aseq[3][5] == "rbStates" ) { haveState = 1 };
if (aseq[3][5] == "rbCountries") { haveCountry = 1 };
}
let sp20s = document.getElementsByClassName("span-20"); //make page wider
for (const sp20 of sp20s){
sp20.style.width = '100%';
}
let rtable = document.getElementById("PQDateRangeResultsTable");
while(rtable.firstChild){ // get rid of previous results
rtable.removeChild(rtable.firstChild);
}
let sthr = rtable.createTHead().insertRow();
let headers = ["Name", "Max Res", "Ret Res", "Distance"];
if (haveState ) {headers.push("State")};
if (haveCountry) {headers.push("Country")};
if (haveCoords ) {headers.push("Coords")};
headers = headers.concat(["Start Date", "End Date", "Difference"]);
tableRow(sthr,"th", headers);
let rtb = rtable.createTBody();
let str = null;
for (const num of seqs[seq]){
if (previous) {
diff = (num[3][3] - previous)/(1000*60*60*24);
}
str = rtb.insertRow();
let link = document.createElement("A");
link.href=num[2][2];
link.text=num[0]+num[1];
str.insertCell().appendChild(link);
str.insertCell().appendChild(document.createTextNode(num[2][1])); //max res
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][0])); //res
if (num[3][0]==num[2][1]) { c.style.backgroundColor=errColour; c.title = "Results limited"; };
if ( num[3][0]==0) { c.style.backgroundColor=errColour; c.title = "No results"; };
total += parseInt(num[3][0]);
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][1])); // distance
if (num[3][1]!=seqs[seq][0][3][1]) { c.style.backgroundColor=errColour; c.title = "Distance is different"; };
if (haveState) {
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][6])); // state
if (num[3][6]!=seqs[seq][0][3][6]) { c.style.backgroundColor=errColour; c.title = "State is different"; };
}
if (haveCountry) {
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][7])); // state
if (num[3][7]!=seqs[seq][0][3][7]) { c.style.backgroundColor=errColour; c.title = "Country is different"; };
}
if (haveCoords) {
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][2])); // coords
if (num[3][2]!=seqs[seq][0][3][2]) { c.style.backgroundColor=errColour; c.title = "Coordinates are different"; };
}
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][3].toDateString())); // start date
if ((num[1]==seqs[seq][0][1]) && (num[3][3]>firstCache)) { c.style.backgroundColor=errColour; c.title = "Date is after first cache laid"; }; // date is first range and date is after first cache
c = str.insertCell(); c.appendChild(document.createTextNode(num[3][4].toDateString())); // end date
if ((num[1]==seqs[seq][seqs[seq].length-1][1])&& num[3][4] < Date.now()) { c.style.backgroundColor=errColour; c.title = "Last date is before now"; }; // date is first range and date is after first cache
if ( previous ) { // no cell for first
c = str.insertCell();
let cTxt = diff+" day";
if ( diff != 1 ) {
cTxt += "s";
c.style.backgroundColor=errColour; c.title = "Start date is not the day after previous end date"; };
c.appendChild(document.createTextNode(cTxt));
}
previous = num[3][4];
}
str = rtable.insertRow();
str.insertCell().appendChild(document.createTextNode("Total"));
str.insertCell();
str.insertCell().appendChild(document.createTextNode(total));
}
function oneOfSeq(seq,num){
if ( typeof seq == "object" ){ //initial call is event handler
seq = seq.currentTarget.idx; // get which line was clicked
num = 0;
}
if ( seq > seqs.length ) { return };
GM_setValue("PQRRunning",1);
if (num > 0){
let CoStType = GM_getValue("PQRCoStT");
let CoStName = GM_getValue("PQRCoStV");
seqs[seq][num-1].push([
GM_getValue("PQRRes"), //0
GM_getValue("PQRRad"), //1
GM_getValue("PQRCoords"), //2
new Date(GM_getValue("PQRStD")), //3
new Date(GM_getValue("PQREnD")), //4
CoStType, //5
CoStType == 'rbStates' ? CoStName : "", //6
CoStType == 'rbCountries' ? CoStName : "", //7
GM_getValue("PQROrig"), //8
]);
}
if (tab) { tab.close() };
if (doneListener) { GM_removeValueChangeListener( doneListener )};
if (num >= seqs[seq].length){
showResults(seqs,seq);
GM_setValue("PQRRunning",0);
return;
}
GM_setValue("PQRDone",0);
tab = GM_openInTab(seqs[seq][num][2][2],{active: true, setParent: true});
doneListener = GM_addValueChangeListener("PQRDone",function(){oneOfSeq(seq,num+1)});
}
function getPQDate(which){
var thisd = new Date();
thisd.setTime(0);
thisd.setFullYear(document.getElementById("ctl00_ContentBody_DateTimeXXX_Year".replace("XXX", which )).value);
thisd.setMonth(document.getElementById("ctl00_ContentBody_DateTimeXXX_Month".replace("XXX", which )).value-1);
thisd.setDate(document.getElementById("ctl00_ContentBody_DateTimeXXX_Day".replace("XXX", which )).value);
//console.log("Start Date "+thisd.toDateString());
return thisd;
}
function getLatLon(which,fmt){
var i;
let sels = ["ctl00_ContentBody_LatLong__inputLatDegs",
"ctl00_ContentBody_LatLong__inputLatMins",
"ctl00_ContentBody_LatLong__inputLatSecs",
"ctl00_ContentBody_LatLong:_selectNorthSouth"];
let hStr = ["S","N"];
if (which == "Long"){
hStr = ["E","W"];
sels[3].replace("NorthSouth","EastWest");
for ( i = 0; i <= fmt; i++ ){
sels[i] = sels[i].replace("tLat","tLong"); //weird - can't do inplace editing
}
}
let e = document.getElementById(sels[3]);
let hemiV = parseInt(e.options[e.selectedIndex].value);
let hemiT = hStr[(hemiV+1)/2]; // map -1,1 to 0,1 to use as array indices
let str = hemiT;
for ( i = 0; i <= fmt; i++ ){
let v = document.getElementById(sels[i]).value;
if ( i > 0 ){ str += " " }
str += v;
}
return str;
}
function doOnePQ(){
let e = null;
let countryStateVal = "";
let countryStateID = null;
let resstr = document.querySelector("p.Success");
let rescnt = -1;
if (resstr){
let resmatch = resstr.textContent.match(/results in (\d+) caches/);
if (resmatch) {
rescnt = resmatch[1];
console.log("results in "+rescnt+ " caches");
GM_setValue("PQRRes",rescnt);
}
}
let countryState = document.querySelector('input[name="ctl00$ContentBody$CountryState"]:checked').value;
if ( countryState == "rbStates" ){
countryStateID = "ctl00$ContentBody$lbStates";
}
if ( countryState == "rbCountries" ){
countryStateID = "ctl00$ContentBody$lbCountries";
}
if ( countryState != "rbNone"){
e = document.querySelector("select[name='"+countryStateID+"']");
countryStateVal = e.options[e.selectedIndex].text;
}
console.log("countryStateVal: "+countryStateVal);
GM_setValue("PQRCoStV",countryStateVal);
GM_setValue("PQRCoStT",countryState);
let origin = document.querySelector('input[name="ctl00$ContentBody$Origin"]:checked').value;
console.log("origin: "+origin);
GM_setValue("PQROrig",origin);
e = document.getElementById("ctl00_ContentBody_rbUnitType_1");
let radunit = e.checked ? "km" : "mi";
let radius = document.getElementById("ctl00_ContentBody_tbRadius").value;
radius = radius + radunit;
GM_setValue("PQRRad",radius);
let startd = getPQDate("Begin")
GM_setValue("PQRStD",startd.toJSON());
let endd = getPQDate("End");
GM_setValue("PQREnD",endd.toJSON());
e = document.querySelector("select[name='ctl00$ContentBody$LatLong']");
let degFmt = e.options[e.selectedIndex].value;
let LatT = getLatLon("Lat",degFmt);
let LonT = getLatLon("Long",degFmt);
console.log("Coords "+LatT+" "+LonT);
GM_setValue("PQRCoords",LatT+" "+LonT);
GM_setValue("PQRDone",1);
// console.log("PQReport Done");
}
function getRangePQList(ntab){
var c;
let pqtable = document.querySelector("#pqRepeater");
let cntregex = / \((\d+)\) /;
let pqs = new Array();
let seqregex = /(.*)(\d+)$/;
for (var i = 0, row; row = pqtable.rows[i]; i++) { // go through pqs, put matching ones into pqs
let rowdata = row.cells[3]; //the cell with the link and info
let cntmatch = rowdata.innerHTML.match(cntregex);
let count = "xxx"; //maybe not needed any more
let title = "xxx";
let href = "xxx";
if (cntmatch) { // not the first or last line
count = cntmatch[1];
let link = rowdata.getElementsByTagName("a")[0];
if (link) {
title = link.title;
href = link.href;
}
pqs.push([title,count,href]);
// console.log ("count = " + count + " title= " + title + " link= " + href);
}
}
let cursname = ""; // go through pqs - group into seqs
seqs = new Array();
let thisseq = null;
for (const pq of pqs){
let aseq = pq[0].match(seqregex);
if (aseq) {
let sname = aseq[1];
let snum = aseq[2];
// console.log ("sequence - name = " + sname + " num= " + snum + " count = " + pq[1] + " title= " + pq[0] + " link= " + pq[2]);
if (sname != cursname) { // new sequence
if ( thisseq && thisseq.length <= 1){ // '<' just in case?
seqs.pop();
}
thisseq = new Array();
seqs.push(thisseq);
cursname = sname;
}
thisseq.push([sname,snum,pq]);
}
}
let stable = document.createElement("table");
ntab.appendChild(stable);
stable.id = "PQDateRangeTable";
stable.className = "PocketQueryListTable";
let sthr = stable.createTHead().insertRow();
tableRow(sthr,"th",["Name", "Count", "Start", "End"]);
i = 0;
let str = null;
let stb = stable.createTBody();
for (const seqn of seqs){
// console.log ("sequence " + seqn[0][0]);
str = stb.insertRow();
str.idx = i; // store index as a property of the row, which can be later read by oneOfSeq
str.className = "PQDateRangeEntry";
let link = document.createElement("A");
link.href="#";
link.text=seqn[0][0];
str.insertCell().appendChild(link);
str.insertCell().appendChild(document.createTextNode(seqn.length));
str.insertCell().appendChild(document.createTextNode(seqn[0][1]));
str.insertCell().appendChild(document.createTextNode(seqn[seqn.length-1][1]));
str.addEventListener('click', oneOfSeq); // oneOfSeq gets passed an event
i++;
}
let rtable = document.createElement("table");
ntab.appendChild(rtable);
rtable.id = "PQDateRangeResultsTable";
rtable.className = "PocketQueryListTable Table";
}
(function() {
'use strict';
var style = ".PQDateRangeEntry:hover { background-color: #93c732;}"
GM_addStyle(style);
const url = document.URL;
if (url.match(/gcquery\.aspx\?guid=/)){
if ( GM_getValue("PQRRunning")){
if ( document.querySelector("p.Success")) { // Is there a 'this query results in...
doOnePQ();
} else {
document.getElementById("ctl00_ContentBody_btnSubmit").click();
}
}
} else {
let ntab = modifyTabs();
getRangePQList(ntab);
}
})();