// ==UserScript==
// @name Add log signature
// @version 0.0.1
// @namespace https://github.com/todeit02/geocaching-com_add_log_signature
// @description Automatically adds your signature when creating logs on geocaching.com. With configurable content and desired log types.
// @grant GM_registerMenuCommand
// @require https://openuserjs.org/src/libs/sizzle/GM_config.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.notification
// @include /^https:\/\/www\.geocaching\.com\/seek\/log\.aspx.*$/
// @include /^https:\/\/www\.geocaching\.com\/play\/geocache\/.+\/log$/
// @run-at document-end
// @connect *
// ==/UserScript==
"use strict";
const OLD_LOG_PAGE_PATHNAME = "/seek/log.aspx"
const NEW_LOG_PAGE_PATHNAME_REGEX = /\/play\/geocache\/.+\/log/;
const LOG_TYPES = [
{
id: 2,
name: "Found it"
},
{
id: 3,
name: "Didn't find it"
},
{
id: 4,
name: "Write note"
},
{
id: 7,
name: "Needs Archived"
},
{
id: 9,
name: "Will attend"
},
{
id: 45,
name: "Needs Maintenance"
}
];
const LOG_TYPE_CONFIG_FIELD_PREFIX = "logType";
(async () =>
{
const isNewLogPage = NEW_LOG_PAGE_PATHNAME_REGEX.test(window.location.pathname);
const isOldLogPage = (window.location.pathname === OLD_LOG_PAGE_PATHNAME);
if(!isNewLogPage && !isOldLogPage) throw new Error("Unrecognized log page.");
let logInputElement = null;
let logTypeSelect = null;
if(isNewLogPage)
{
logInputElement = document.querySelector("#LogText");
logTypeSelect = await waitForNewLogTypeSelect();
}
else if(isOldLogPage)
{
logInputElement = document.querySelector("#ctl00_ContentBody_LogBookPanel1_uxLogInfo");
logTypeSelect = document.querySelector("#ctl00_ContentBody_LogBookPanel1_ddLogType");
}
const configPanel = await initializeConfigurability(LOG_TYPES);
GM_registerMenuCommand("Configuration", () =>
{
configPanel.open();
});
if(!configPanel.get("configHasBeenOpen"))
{
showMissingConfigurationNotification(configPanel);
return;
}
const signature = configPanel.get("signature");
const onlyInNewLogs = configPanel.get("onlyInNewLogs");
const logInput = LogInput(logInputElement, signature);
logTypeSelect.addEventListener("change", handleLogTypeChange);
const selectedLogTypeId = parseInt(logTypeSelect.value);
if(isNaN(selectedLogTypeId)) throw new UnrecognizedLogTypeError(logTypeSelect.value);
const currentText = logInput.domElement.value;
if(shouldSignLog(selectedLogTypeId, currentText, onlyInNewLogs)) logInput.appendSignature();
function shouldSignLog(currentLogTypeId, currentText, onlyInNewLogs)
{
return shouldSignLogType(currentLogTypeId) && shouldSignLogText(currentText, onlyInNewLogs);
}
function shouldSignLogType(logTypeId)
{
const logTypeIsUnselected = logTypeId < 0;
if(logTypeIsUnselected) return true;
return configPanel.get(LOG_TYPE_CONFIG_FIELD_PREFIX + logTypeId);
}
function shouldSignLogText(logText, onlyInNewLogs)
{
return !logInput.logIsSigned() && (!onlyInNewLogs || (logText.length === 0));
}
function handleLogTypeChange()
{
const logTypeId = parseInt(logTypeSelect.value);
if(isNaN(logTypeId)) throw new UnrecognizedLogTypeError(logTypeSelect.value);
if(logInput.containsOnlySignature()) logInput.domElement.value = ''
const currentText = logInput.domElement.value;
if(shouldSignLog(logTypeId, currentText, onlyInNewLogs)) logInput.appendSignature();
}
function showMissingConfigurationNotification(configPanel)
{
const notificationOptions = Object.freeze(
{
title: "Missing Signature Text",
text: "You haven't specified a signature yet.\n\nClick here to configure.",
silent: true,
onclick: () => void configPanel.open(),
});
GM_notification(notificationOptions);
}
function LogInput(inputElement, signature)
{
function containsOnlySignature()
{
return (inputElement.value === signature);
}
function appendSignature()
{
const currentText = inputElement.value;
const texts = [currentText, signature].filter(text => !!text);
inputElement.value = texts.join("\n");
}
function logIsSigned()
{
const currentText = inputElement.value;
currentText.endsWith(signature);
}
return {
appendSignature,
logIsSigned,
containsOnlySignature,
domElement: inputElement,
};
}
function waitForNewLogTypeSelect()
{
return new Promise(resolve =>
{
const intervalId = window.setInterval(() =>
{
const element = document.querySelector("select.log-types");
if(element)
{
window.clearInterval(intervalId);
resolve(element);
}
}, 100);
});
}
function initializeConfigurability(logTypes)
{
const logTypeFieldEntries = logTypes.map((logType, i) =>
{
const fieldName = LOG_TYPE_CONFIG_FIELD_PREFIX + logType.id;
const fieldEntry =
{
section: (i === 0) ? ["Log types to sign"] : undefined,
label: logType.name,
type: "checkbox",
default: true,
};
return [fieldName, fieldEntry];
});
const logTypeFields = Object.fromEntries(logTypeFieldEntries);
const fields = Object.freeze(
{
"signature":
{
"label": 'Signature',
"type": 'textarea',
"section": ["General settings"],
},
"onlyInNewLogs":
{
"label": 'Insert only for new logs',
"type": 'checkbox',
"default": true,
},
...logTypeFields,
"configHasBeenOpen":
{
'type': 'hidden',
'value': '',
},
});
return new Promise(resolve =>
{
const gmConfigConfig =
{
id: 'geocaching-log-singature',
title: "Log Signature Settings",
fields,
events:
{
open: () => configPanel.set("configHasBeenOpen", "yes"),
init: () => resolve(configPanel),
},
};
const configPanel = new GM_config(gmConfigConfig);
});
}
class UnrecognizedLogTypeError extends Error
{
constructor(logType)
{
super(`Unrecognized log type ID "${logType}".`);
}
}
})();