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"; "use strict";
// Put all the javascript code here, that you want to execute after page load. // Put all the javascript code here, that you want to execute after page load.
/**
* @typedef ("delay"|"nodep"|"abort"|"") DisruptionType
*/
/** /**
* *
* @typedef {Object} Stage * @typedef {Object} Stage
@ -11,9 +14,14 @@
*/ */
/** /**
* @type Stage * @type Stage
* @todo this can now also contain a dict<DisruptionType, Stage>
*/ */
let currentStage; let currentStage;
const settings = browser.storage.sync; 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 })); 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; let stages;
const personalDataConfigKeys = [ const personalDataConfigKeys = [
"addr__appellation", "addr__title", "addr__firstName", "addr__surName", "addr__appellation", "addr__title", "addr__firstName", "addr__surName",
@ -88,6 +80,19 @@ function processMutations(mutationList, observer) {
if (currentStage === undefined) { if (currentStage === undefined) {
currentStage = stages.shift(); 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) { for (const mutation of mutationList) {
if ('expects' in currentStage) { if ('expects' in currentStage) {
if (currentStage.expects == 'mutation') { if (currentStage.expects == 'mutation') {
@ -116,7 +121,6 @@ function processMutations(mutationList, observer) {
} }
} }
let clickThroughForms;
let hasConfiguredBankDetails, hasConfiguredPersonalData; let hasConfiguredBankDetails, hasConfiguredPersonalData;
let observer = new MutationObserver(processMutations); let observer = new MutationObserver(processMutations);
function nextStage() { function nextStage() {
@ -129,9 +133,9 @@ function nextStage() {
} }
const addObserver = () => { const addObserver = () => {
settings.get( settings.get(
['autocontinue', 'enable'].concat(personalDataConfigKeys, bankDetailConfigKeys) ['enable', 'defaultAction'].concat(personalDataConfigKeys, bankDetailConfigKeys)
).then(userSettings => { ).then(userSettings => {
clickThroughForms = !!userSettings.autocontinue; disruption = userSettings.defaultAction || "";
hasConfiguredPersonalData = Object.keys(userSettings).filter(k => personalDataConfigKeys.includes(k)).length > 0; hasConfiguredPersonalData = Object.keys(userSettings).filter(k => personalDataConfigKeys.includes(k)).length > 0;
hasConfiguredBankDetails = Object.keys(userSettings).filter(k => bankDetailConfigKeys.includes(k)).length > 0; hasConfiguredBankDetails = Object.keys(userSettings).filter(k => bankDetailConfigKeys.includes(k)).length > 0;
if (!!userSettings.enable) { 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 = { const iWasDelayed = {
name: "iWasDelayed", name: "iWasDelayed",
match: node => node.classList.contains("antrags-typ-auswahl") && clickThroughForms, match: node => node.classList.contains("antrags-typ-auswahl"),
execute: node => { execute: node => {
const delay = $$(node, 'input#antragstyp-verspaetung'); const delay = $$(node, 'input#antragstyp-verspaetung');
if (delay instanceof HTMLInputElement) { if (delay instanceof HTMLInputElement) {
@ -246,19 +262,21 @@ const iWasDelayed = {
const moreThan60Minutes = { const moreThan60Minutes = {
name: "moreThan60Minutes", name: "moreThan60Minutes",
match: node => node.classList.contains("verspaetungs-auswahl") && clickThroughForms, match: node => node.classList.contains("verspaetungs-auswahl"),
execute: node => node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change')) execute: node => {
return node.querySelector('#verspaetungstyp-mehr-als-stunde').dispatchEvent(new Event('change'));
}
} }
const continueToForm = { const continueToForm = {
name: "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()) execute: node => $$(node, 'button.fahrgastrechte-continue-button').dispatchEvent(_clickEv())
} }
const focusDepartureInput = { const focusDepartureInput = {
name: "focusDepartureInput", name: "focusDepartureInput",
match: node => node.classList.contains("fahrplan") && clickThroughForms, match: node => node.classList.contains("fahrplan"),
execute: node => { execute: node => {
const depInput = $$(node, '.fahrplan__start .fahrplan__haltestelle input'); const depInput = $$(node, '.fahrplan__start .fahrplan__haltestelle input');
const obs = new IntersectionObserver((entries, intObserver) => { const obs = new IntersectionObserver((entries, intObserver) => {
@ -274,7 +292,7 @@ const focusDepartureInput = {
const jumpToTimeInput = { const jumpToTimeInput = {
name: "jumpToTimeInput", name: "jumpToTimeInput",
match: node => node.classList.contains("ankunft-zeit") && clickThroughForms, match: node => node.classList.contains("ankunft-zeit"),
execute: node => { execute: node => {
$$(node, '#fahrgastrechte-ankunft-uhrzeit--db-web-text-input').focus(); $$(node, '#fahrgastrechte-ankunft-uhrzeit--db-web-text-input').focus();
return true; return true;
@ -426,8 +444,8 @@ const enterPaymentDetails = {
} }
const defaultStages = [ const defaultStages = [
startClaim, fillData, clickContinue, startClaim, fillData, clickContinue,
iWasDelayed, moreThan60Minutes, continueToForm, {"delay": [iWasDelayed, moreThan60Minutes, continueToForm,
focusDepartureInput, jumpToTimeInput, focusDepartureInput, jumpToTimeInput], "nodep": [], "abort": []},
activateAppellationDropdown, enterAppellationAndActivateTitleDropdown, activateAppellationDropdown, enterAppellationAndActivateTitleDropdown,
enterTitleAndActivateCountryDropdown, enterCountry, enterTitleAndActivateCountryDropdown, enterCountry,
enterTextPersonalData, /* continueToPayout, */ enterPaymentDetails enterTextPersonalData, /* continueToPayout, */ enterPaymentDetails

View File

@ -16,9 +16,13 @@
<input name="enable" id="enable" type="checkbox" /> <input name="enable" id="enable" type="checkbox" />
</label> </label>
<label class="item"> <label class="item">
<span class="fh">Skip questionnaire</span> <span class="fh">Default action</span>
<input name="autocontinue" id="autocontinue" type="checkbox" /> <select class="default_action" id="default_action">
<span class="sh">Automatically answer "I was delayed, by more than 60 minutes."</span> <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> </label>
</fieldset> </fieldset>
<button id="all-get-from-profile">Get my personal data from my profile</button> <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."); putError("Please enter a birthday.");
return; return;
} }
let autoContinue = this.querySelector('#autocontinue').checked;
let enable = this.querySelector('#enable').checked; let enable = this.querySelector('#enable').checked;
let defaultAction = this.querySelector('#default_action').value;
let title = this.querySelector('#title').value; let title = this.querySelector('#title').value;
let title_addl = this.querySelector('#title_addl').value; let title_addl = this.querySelector('#title_addl').value;
let firstName = this.querySelector('#firstname').value; let firstName = this.querySelector('#firstname').value;
@ -95,8 +95,8 @@ async function saveOptions(ev) {
let options = { let options = {
bcnum: bcNum, bcnum: bcNum,
bday: bDay, bday: bDay,
autocontinue: autoContinue,
enable: enable, enable: enable,
defaultAction: defaultAction,
addr__appellation: title, addr__appellation: title,
addr__title: title_addl, addr__title: title_addl,
addr__firstName: firstName, addr__firstName: firstName,
@ -131,7 +131,7 @@ async function restoreOptions() {
document.querySelector('#enable').checked = settings.enable || true; document.querySelector('#enable').checked = settings.enable || true;
document.querySelector("#bcnum").value = settings.bcnum || ""; document.querySelector("#bcnum").value = settings.bcnum || "";
document.querySelector("#birthday").value = settings.bday || ""; 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').value = settings.addr__appellation || "";
document.querySelector('#title_addl').value = settings.addr__title || ""; document.querySelector('#title_addl').value = settings.addr__title || "";
document.querySelector('#firstname').value = settings.addr__firstName || ""; document.querySelector('#firstname').value = settings.addr__firstName || "";