choose your own adventure

This commit is contained in:
iw0 2024-05-01 14:53:27 +02:00
parent cc7a1ceda5
commit f796c6fd04
3 changed files with 55 additions and 33 deletions

View File

@ -1,6 +1,9 @@
"use strict";
// Put all the javascript code here, that you want to execute after page load.
/**
* @typedef ("delay"|"nodep"|"abort"|"") DisruptionType
*/
/**
*
* @typedef {Object} Stage
@ -11,9 +14,14 @@
*/
/**
* @type Stage
* @todo this can now also contain a dict<DisruptionType, Stage>
*/
let currentStage;
const settings = browser.storage.sync;
/**
* @type ("delay"|"nodep"|"abort"|"")
*/
let disruption = "";
/**
*
@ -44,22 +52,6 @@ const pressKey = (...keys) => {
d(new KeyboardEvent('keyup', { bubbles: true, key: k }));
})
}
/** @param {Node} node */
function executeStage(node) {
if (node.nodeType === 1) {
if (currentStage === undefined) {
currentStage = stages.shift();
}
if (currentStage.match(node)) {
console.log(currentStage.name, "matched: ", node);
console.log(currentStage.name, currentStage.execute(node) ? "executed" : "execution failed");
nextStage();
} else {
console.log(currentStage.name, "did not match: ", mutation);
}
}
}
let stages;
const personalDataConfigKeys = [
"addr__appellation", "addr__title", "addr__firstName", "addr__surName",
@ -88,6 +80,19 @@ function processMutations(mutationList, observer) {
if (currentStage === undefined) {
currentStage = stages.shift();
}
if (!("name" in currentStage)){
// stages always have a `name`, so this must be the disruption type split
if (disruption in currentStage){
stages.unshift(...currentStage[disruption]);
currentStage = stages.shift();
} else if (disruption == "") {
// no default action - skip to personal data stuff
currentStage = stages.shift();
//TODO sniff action and provide field jumps instead
} else {
console.log("this Stage is invalid, aborting", currentStage);
}
}
for (const mutation of mutationList) {
if ('expects' in currentStage) {
if (currentStage.expects == 'mutation') {
@ -116,7 +121,6 @@ function processMutations(mutationList, observer) {
}
}
let clickThroughForms;
let hasConfiguredBankDetails, hasConfiguredPersonalData;
let observer = new MutationObserver(processMutations);
function nextStage() {
@ -129,9 +133,9 @@ function nextStage() {
}
const addObserver = () => {
settings.get(
['autocontinue', 'enable'].concat(personalDataConfigKeys, bankDetailConfigKeys)
['enable', 'defaultAction'].concat(personalDataConfigKeys, bankDetailConfigKeys)
).then(userSettings => {
clickThroughForms = !!userSettings.autocontinue;
disruption = userSettings.defaultAction || "";
hasConfiguredPersonalData = Object.keys(userSettings).filter(k => personalDataConfigKeys.includes(k)).length > 0;
hasConfiguredBankDetails = Object.keys(userSettings).filter(k => bankDetailConfigKeys.includes(k)).length > 0;
if (!!userSettings.enable) {
@ -231,9 +235,21 @@ const clickContinue = {
}
//TODO construction zone
const sniffDisruption = {
name: "sniffDisruption",
match: node => {
//delay -> .verspaetungs-auswahl.fahrgastrechte-page__section.test-current-section
//nodep -> .fahrplan...., .antrags-typ-auswahl:children("input[name=radioAntragsTyp]"):id(#antragstyp-nicht-angetreten):has-siblings(.db-web-radio-button-container__radio-button-icon--checked)
//abort -> .fahrplan...., ...#antragstyp-abgebrochen, An welchem Bahnhof? -> .fahrgastrechte-page__section:has-child(.fahrgastrechte-editable.fahrgastrechte-bahnhof.abbruchbahnhof)
// Weitere Kosten? (.kosten-abfrage.f-p__s.t-c-s:has_child(.db-web-radio-button-container.kosten-abfrage__radio))
},
execute: () => true
}
const iWasDelayed = {
name: "iWasDelayed",
match: node => node.classList.contains("antrags-typ-auswahl") && clickThroughForms,
match: node => node.classList.contains("antrags-typ-auswahl"),
execute: node => {
const delay = $$(node, 'input#antragstyp-verspaetung');
if (delay instanceof HTMLInputElement) {
@ -246,19 +262,21 @@ const iWasDelayed = {
const moreThan60Minutes = {
name: "moreThan60Minutes",
match: node => node.classList.contains("verspaetungs-auswahl") && clickThroughForms,
execute: node => node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change'))
match: node => node.classList.contains("verspaetungs-auswahl"),
execute: node => {
return node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change'));
}
}
const continueToForm = {
name: "continueToForm",
match: node => node.classList.contains("verspaetung-bestaetigung") && clickThroughForms,
match: node => node.classList.contains("verspaetung-bestaetigung"),
execute: node => $$(node, 'button.fahrgastrechte-continue-button').dispatchEvent(_clickEv())
}
const focusDepartureInput = {
name: "focusDepartureInput",
match: node => node.classList.contains("fahrplan") && clickThroughForms,
match: node => node.classList.contains("fahrplan"),
execute: node => {
const depInput = $$(node, '.fahrplan__start .fahrplan__haltestelle input');
const obs = new IntersectionObserver((entries, intObserver) => {
@ -274,7 +292,7 @@ const focusDepartureInput = {
const jumpToTimeInput = {
name: "jumpToTimeInput",
match: node => node.classList.contains("ankunft-zeit") && clickThroughForms,
match: node => node.classList.contains("ankunft-zeit"),
execute: node => {
$$(node, '#fahrgastrechte-ankunft-uhrzeit--db-web-text-input').focus();
return true;
@ -426,8 +444,8 @@ const enterPaymentDetails = {
}
const defaultStages = [
startClaim, fillData, clickContinue,
iWasDelayed, moreThan60Minutes, continueToForm,
focusDepartureInput, jumpToTimeInput,
{"delay": [iWasDelayed, moreThan60Minutes, continueToForm,
focusDepartureInput, jumpToTimeInput], "nodep": [], "abort": []},
activateAppellationDropdown, enterAppellationAndActivateTitleDropdown,
enterTitleAndActivateCountryDropdown, enterCountry,
enterTextPersonalData, /* continueToPayout, */ enterPaymentDetails

View File

@ -16,9 +16,13 @@
<input name="enable" id="enable" type="checkbox" />
</label>
<label class="item">
<span class="fh">Skip questionnaire</span>
<input name="autocontinue" id="autocontinue" type="checkbox" />
<span class="sh">Automatically answer "I was delayed, by more than 60 minutes."</span>
<span class="fh">Default action</span>
<select class="default_action" id="default_action">
<option value="">None</option>
<option value="delay">I arrived >60 min late</option>
<option value="nodep">I did not start the trip</option>
<option value="abort">I returned home</option>
</select>
</label>
</fieldset>
<button id="all-get-from-profile">Get my personal data from my profile</button>

View File

@ -75,8 +75,8 @@ async function saveOptions(ev) {
putError("Please enter a birthday.");
return;
}
let autoContinue = this.querySelector('#autocontinue').checked;
let enable = this.querySelector('#enable').checked;
let defaultAction = this.querySelector('#default_action').value;
let title = this.querySelector('#title').value;
let title_addl = this.querySelector('#title_addl').value;
let firstName = this.querySelector('#firstname').value;
@ -95,8 +95,8 @@ async function saveOptions(ev) {
let options = {
bcnum: bcNum,
bday: bDay,
autocontinue: autoContinue,
enable: enable,
defaultAction: defaultAction,
addr__appellation: title,
addr__title: title_addl,
addr__firstName: firstName,
@ -131,7 +131,7 @@ async function restoreOptions() {
document.querySelector('#enable').checked = settings.enable || true;
document.querySelector("#bcnum").value = settings.bcnum || "";
document.querySelector("#birthday").value = settings.bday || "";
document.querySelector("#autocontinue").checked = settings.autocontinue || false;
document.querySelector("#default_action").value = settings.defaultAction || "";
document.querySelector('#title').value = settings.addr__appellation || "";
document.querySelector('#title_addl').value = settings.addr__title || "";
document.querySelector('#firstname').value = settings.addr__firstName || "";