import { type InProgressGeneratedState, RolledValues, RollSelections, type RollTableAuthor, RollTableDatabase, type RollTableDetailsAndResults, type RollTableDetailsNoResults, type RollTableLimited, type RollTableResult, type RollTableResultFull, type RollTableResultSet } from '../common/rolltable'; import { authorIdKey } from '../common/template'; export function asBoolean(s: string|undefined): boolean|undefined { if (typeof s === "undefined") { return } switch (s.toLowerCase()) { case 'true': return true case 'false': return false default: return } } export function asInteger(s: string|undefined): number|undefined { if (typeof s === "undefined") { return } const result = parseInt(s) if (Number.isNaN(result)) { return } return result } export function asTimestamp(s: string|undefined): Date|undefined { const i = asInteger(s) if (typeof i === "undefined") { return } const date = new Date(i) if (Number.isNaN(date.valueOf())) { return } return date } export function textFrom(e: HTMLElement|null): string|undefined { if (!e) { return } return e.innerText.trim() } export function hrefFrom(e: HTMLAnchorElement|null): string | null { if (!e) { return null } return e.href } export function checkedFrom(e: HTMLInputElement|null): boolean | null { if (!e) { return null } return e.checked } // element to find here is .author export function scrapeAuthor(author: HTMLElement|null): RollTableAuthor|null|undefined { if (!author) { return null } const id = asInteger(author.dataset[authorIdKey]) const name = textFrom(author.querySelector(`.authorName`)) const url = hrefFrom(author.querySelector("a[href]")) const relation = textFrom(author.querySelector(`.authorRelation`)) if (typeof id === "undefined" || typeof name === "undefined" || typeof relation === 'undefined') { return } return { id, name, url, relation } } // element to find here is .resultSet export function scrapeResultSet(set: HTMLElement|null): RollTableResultSet|null|undefined { if (!set) { return null } const id = asInteger(set.dataset["id"]) const name = textFrom(set.querySelector(`.setName`)) const global = asBoolean(set.dataset["global"]) if (typeof id === "undefined" || typeof global === "undefined") { return } return { id, name: name ?? null, description: null, global, } } // element to find here is .tableHeader export function scrapeTableHeader(head: HTMLElement|null): RollTableLimited|RollTableDetailsNoResults|null|undefined { if (!head) { return null } const emoji = textFrom(head.querySelector(".tableEmoji")) const title = textFrom(head.querySelector(".tableTitle")) const ordinal = asInteger(head.dataset["ordinal"]) const id = asInteger(head.dataset["id"]) const identifier = head.dataset["identifier"] const name = head.dataset["name"] if (typeof emoji === 'undefined' || typeof title === 'undefined' || typeof ordinal === 'undefined') { return } const header = `${emoji} ${title}` if (typeof id === 'undefined' || typeof identifier === 'undefined' || typeof name === 'undefined') { return { full: false, emoji, title, header, ordinal, } } return { full: 'details', id, identifier, emoji, title, header, ordinal, name, } } export function scrapeGeneratedHead(head: HTMLElement|null): {table: RollTableLimited|RollTableDetailsNoResults, selected: boolean|null}|null|undefined { if (!head) { return null } const table = scrapeTableHeader(head.querySelector(`.tableHeader`)) if (!table) { return } const selected = checkedFrom(head.querySelector(`input[type=checkbox].generatedSelect`)) return { table, selected, } } // element to find here is .resultText export function scrapeResultText(result: HTMLElement|null): {full: false, text: string}|{full: true, mappingId: number, textId: number, updated: Date, text: string}|undefined|null { if (!result) { return null } const text = textFrom(result) const mappingId = asInteger(result.dataset["mappingid"]) const textId = asInteger(result.dataset["textid"]) const updated = asTimestamp(result.dataset["updated"]) if (typeof text === 'undefined') { return } if (typeof mappingId === 'undefined' || typeof textId === 'undefined' || typeof updated == 'undefined') { return { full: false, text, } } return { full: true, text, textId, mappingId, updated: new Date(updated) } } // element to find here is .generatedElement export function scrapeGeneratedElement(generated: HTMLElement|null): {result: RollTableResult, selected: boolean|null}|null|undefined { if (!generated) { return null } const result = scrapeResultText(generated.querySelector(`.resultText`)) const author = scrapeAuthor(generated.querySelector(`.author`)) const set = scrapeResultSet(generated.querySelector(`.resultSet`)) const header = scrapeGeneratedHead(generated.querySelector(`.generatedHead`)) if (!header || !result) { return } const {table, selected} = header if (!set || typeof author === "undefined" || !result.full) { return { result: { full: false, table, text: result.text, }, selected } } return { result: { ...result, author, set, table, }, selected, } } export function scrapeGeneratedScenario(scenario: HTMLElement): InProgressGeneratedState|undefined export function scrapeGeneratedScenario(scenario: null): null // element to find here is #generatedScenario export function scrapeGeneratedScenario(scenario: HTMLElement|null): InProgressGeneratedState|null|undefined { if (!scenario) { return null } const rolls = new RolledValues() const selection = new RollSelections() for (const item of scenario.querySelectorAll(`.generatedElement`)) { const element = scrapeGeneratedElement(item) if (!element) { return } const {result, selected} = element rolls.add(result) if (selected) { selection.add(result.table) } } return { final: false, rolled: rolls, selected: selection, } } export function scrapeResponseList(responseTypeElement: HTMLElement|null, db: RollTableDatabase): [table: RollTableDetailsAndResults, active: RollTableResultFull|null]|null|undefined { if (!responseTypeElement) { return null } const table = scrapeTableHeader(responseTypeElement.querySelector(`.tableHeader`)) if (!table || !table.full) { return } const resultTable = db.addTable(table) let activeResult: RollTableResultFull|null = null for (const resultElement of responseTypeElement.querySelectorAll(`.response`)) { const partialResult = scrapeResultText(resultElement.querySelector(`.resultText`)) const author = scrapeAuthor(resultElement.querySelector(`.author`)) const set = scrapeResultSet(resultElement.querySelector(`.resultSet`)) const active = resultElement.classList.contains("active") if (!partialResult || !partialResult.full || typeof author === "undefined" || !set) { return } const result = db.addResult({ ...partialResult, set, author, table: resultTable, }) if (active) { activeResult = result } } return [resultTable, activeResult] } export function scrapeResponseLists(lists: HTMLElement): {db: RollTableDatabase, active: ReadonlyMap|null>}|undefined export function scrapeResponseLists(lists: null): null export function scrapeResponseLists(lists: HTMLElement|null): {db: RollTableDatabase, active: ReadonlyMap|null>}|null|undefined { if (!lists) { return null } const db = new RollTableDatabase() const active = new Map|null> for (const responseTypeElement of lists.querySelectorAll(`.responseType`)) { const responseType = scrapeResponseList(responseTypeElement, db) if (!responseType) { return } const [table, activeResult] = responseType active.set(table, activeResult) } return { db, active, } }