diff --git a/Logic/Web/Wikipedia.ts b/Logic/Web/Wikipedia.ts index 3b72024621..69b3a67daa 100644 --- a/Logic/Web/Wikipedia.ts +++ b/Logic/Web/Wikipedia.ts @@ -31,10 +31,10 @@ export default class Wikipedia { private static readonly _cache = new Map>() - private readonly _backend: string; + public readonly backend: string; constructor(options?: ({ language?: "en" | string } | { backend?: string })) { - this._backend = Wikipedia.getBackendUrl(options ?? {}); + this.backend = Wikipedia.getBackendUrl(options ?? {}); } /** @@ -61,10 +61,10 @@ export default class Wikipedia { * new Wikipedia().extractPageName("https://wiki.openstreetmap.org/wiki/NL:Speelbos") // => undefined */ public extractPageName(input: string):string | undefined{ - if(!input.startsWith(this._backend)){ + if(!input.startsWith(this.backend)){ return undefined } - input = input.substring(this._backend.length); + input = input.substring(this.backend.length); const matched = input.match("/?wiki/\(.+\)") if (matched === undefined || matched === null) { @@ -88,7 +88,7 @@ export default class Wikipedia { } public GetArticle(pageName: string, options: WikipediaBoxOptions): UIEventSource<{ success: string } | { error: any }> { - const key = this._backend + ":" + pageName + ":" + (options.firstParagraphOnly ?? false) + const key = this.backend + ":" + pageName + ":" + (options.firstParagraphOnly ?? false) const cached = Wikipedia._cache.get(key) if (cached !== undefined) { return cached @@ -99,11 +99,11 @@ export default class Wikipedia { } public getDataUrl(pageName: string): string { - return `${this._backend}/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + pageName + return `${this.backend}/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + pageName } public getPageUrl(pageName: string): string { - return `${this._backend}/wiki/${pageName}` + return `${this.backend}/wiki/${pageName}` } /** @@ -111,7 +111,7 @@ export default class Wikipedia { * @param searchTerm */ public async search(searchTerm: string): Promise<{ title: string, snippet: string }[]> { - const url = this._backend + "/w/api.php?action=query&format=json&list=search&srsearch=" + encodeURIComponent(searchTerm); + const url = this.backend + "/w/api.php?action=query&format=json&list=search&srsearch=" + encodeURIComponent(searchTerm); return (await Utils.downloadJson(url))["query"]["search"]; } @@ -121,23 +121,28 @@ export default class Wikipedia { * @param searchTerm */ public async searchViaIndex(searchTerm: string): Promise<{ title: string, snippet: string, url: string } []> { - const url = `${this._backend}/w/index.php?search=${encodeURIComponent(searchTerm)}` + const url = `${this.backend}/w/index.php?search=${encodeURIComponent(searchTerm)}` const result = await Utils.downloadAdvanced(url); if(result["redirect"] ){ // This is an exact match return [{ - title: this.extractPageName(result["redirect"]), + title: this.extractPageName(result["redirect"]).trim(), url: result["redirect"], snippet: "" }] } const el = document.createElement('html'); - el.innerHTML = result["content"].replace(/href="\//g, "href=\""+this._backend+"/"); + el.innerHTML = result["content"].replace(/href="\//g, "href=\""+this.backend+"/"); const searchResults = el.getElementsByClassName("mw-search-results") const individualResults = Array.from(searchResults[0]?.getElementsByClassName("mw-search-result") ?? []) return individualResults.map(result => { + const toRemove = Array.from(result.getElementsByClassName("searchalttitle")) + for (const toRm of toRemove) { + toRm.parentElement.removeChild(toRm) + } + return { - title: result.getElementsByClassName("mw-search-result-heading")[0].textContent, + title: result.getElementsByClassName("mw-search-result-heading")[0].textContent.trim(), url: result.getElementsByTagName("a")[0].href, snippet: result.getElementsByClassName("searchresult")[0].textContent } @@ -180,7 +185,7 @@ export default class Wikipedia { links.filter(link => link.getAttribute("href")?.startsWith("/") ?? false).forEach(link => { link.target = '_blank' // note: link.getAttribute("href") gets the textual value, link.href is the rewritten version which'll contain the host for relative paths - link.href = `${this._backend}${link.getAttribute("href")}`; + link.href = `${this.backend}${link.getAttribute("href")}`; }) if (options?.firstParagraphOnly) { diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 1391241dd7..dc969deeb7 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -17,7 +17,8 @@ export class SubstitutedTranslation extends VariableUiElement { translation: Translation, tagsSource: UIEventSource, state: FeaturePipelineState, - mapping: Map = undefined) { + mapping: Map, argument: string[], guistate: DefaultGuiState) => BaseUIElement)> = undefined) { const extraMappings: SpecialVisualization[] = []; @@ -25,9 +26,7 @@ export class SubstitutedTranslation extends VariableUiElement { extraMappings.push( { funcName: key, - constr: (() => { - return value - }), + constr: typeof value === "function" ? value : () => value, docs: "Dynamically injected input element", args: [], example: "" diff --git a/UI/Wikipedia/WikidataPreviewBox.ts b/UI/Wikipedia/WikidataPreviewBox.ts index ca86a70a93..ebda722a60 100644 --- a/UI/Wikipedia/WikidataPreviewBox.ts +++ b/UI/Wikipedia/WikidataPreviewBox.ts @@ -22,7 +22,8 @@ export default class WikidataPreviewBox extends VariableUiElement { private static extraProperties: { requires?: { p: number, q?: number }[], property: string, - display: TypedTranslation<{value}> | Map BaseUIElement) /*If translation: Subs({value: * }) */> + display: TypedTranslation<{value}> | Map BaseUIElement) /*If translation: Subs({value: * }) */>, + textMode?: Map }[] = [ { requires: WikidataPreviewBox.isHuman, @@ -34,6 +35,14 @@ export default class WikidataPreviewBox extends VariableUiElement { ['Q1052281', () => Svg.gender_trans_svg().SetStyle("width: 1rem; height: auto")/*'transwomen'*/], ['Q2449503', () => Svg.gender_trans_svg().SetStyle("width: 1rem; height: auto")/*'transmen'*/], ['Q48270', () => Svg.gender_queer_svg().SetStyle("width: 1rem; height: auto")] + ]), + textMode: new Map([ + ['Q6581097', "♂️"], + ['Q6581072', "♀️"], + ['Q1097630', "⚥️"], + ['Q1052281', "🏳️‍⚧️"/*'transwomen'*/], + ['Q2449503', "🏳️‍⚧️"/*'transmen'*/], + ['Q48270', "🏳️‍🌈 ⚧"] ]) }, { @@ -48,7 +57,7 @@ export default class WikidataPreviewBox extends VariableUiElement { } ] - constructor(wikidataId: UIEventSource) { + constructor(wikidataId: UIEventSource, options?: {noImages?: boolean}) { let inited = false; const wikidata = wikidataId .stabilized(250) @@ -73,18 +82,16 @@ export default class WikidataPreviewBox extends VariableUiElement { return new FixedUiElement(maybeWikidata["error"]).SetClass("alert") } const wikidata = maybeWikidata["success"] - return WikidataPreviewBox.WikidataResponsePreview(wikidata) + return WikidataPreviewBox.WikidataResponsePreview(wikidata, options) })) } - // @ts-ignore - - public static WikidataResponsePreview(wikidata: WikidataResponse): BaseUIElement { + public static WikidataResponsePreview(wikidata: WikidataResponse, options?: {noImages?: boolean}): BaseUIElement { let link = new Link( new Combine([ wikidata.id, - Svg.wikidata_svg().SetStyle("width: 2.5rem").SetClass("block") + options.noImages ? wikidata.id : Svg.wikidata_svg().SetStyle("width: 2.5rem").SetClass("block") ]).SetClass("flex"), Wikidata.IdToArticle(wikidata.id), true)?.SetClass("must-link") @@ -93,7 +100,7 @@ export default class WikidataPreviewBox extends VariableUiElement { [Translation.fromMap(wikidata.labels)?.SetClass("font-bold"), link]).SetClass("flex justify-between"), Translation.fromMap(wikidata.descriptions), - WikidataPreviewBox.QuickFacts(wikidata) + WikidataPreviewBox.QuickFacts(wikidata, options) ]).SetClass("flex flex-col link-underline") @@ -103,7 +110,7 @@ export default class WikidataPreviewBox extends VariableUiElement { } - if (imageUrl) { + if (imageUrl && !options?.noImages) { imageUrl = WikimediaImageProvider.singleton.PrepUrl(imageUrl).url info = new Combine([new Img(imageUrl).SetStyle("max-width: 5rem; width: unset; height: 4rem").SetClass("rounded-xl mr-2"), info.SetClass("w-full")]).SetClass("flex") @@ -114,7 +121,7 @@ export default class WikidataPreviewBox extends VariableUiElement { return info } - public static QuickFacts(wikidata: WikidataResponse): BaseUIElement { + public static QuickFacts(wikidata: WikidataResponse, options?: {noImages?: boolean}): BaseUIElement { const els: BaseUIElement[] = [] for (const extraProperty of WikidataPreviewBox.extraProperties) { @@ -134,7 +141,7 @@ export default class WikidataPreviewBox extends VariableUiElement { } const key = extraProperty.property - const display = extraProperty.display + const display = (options?.noImages ? extraProperty.textMode: extraProperty.display) ?? extraProperty.display if (wikidata.claims?.get(key) === undefined) { continue } diff --git a/UI/Wikipedia/WikipediaBox.ts b/UI/Wikipedia/WikipediaBox.ts index ec9722fdca..0b65c18420 100644 --- a/UI/Wikipedia/WikipediaBox.ts +++ b/UI/Wikipedia/WikipediaBox.ts @@ -18,21 +18,23 @@ import {Paragraph} from "../Base/Paragraph"; export interface WikipediaBoxOptions { addHeader: boolean, - firstParagraphOnly: boolean + firstParagraphOnly: boolean, + noImages: boolean + currentState?: UIEventSource<"loading" | "loaded" | "error"> } export default class WikipediaBox extends Combine { - + constructor(wikidataIds: string[], options?: WikipediaBoxOptions) { const mainContents = [] - options = options??{addHeader: false, firstParagraphOnly: true}; + options = options??{addHeader: false, firstParagraphOnly: true, noImages: false}; const pages = wikidataIds.map(entry => WikipediaBox.createLinkedContent(entry.trim(), options)) if (wikidataIds.length == 1) { const page = pages[0] mainContents.push( new Combine([ new Combine([ - Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("inline-block mr-3"), + options.noImages ? undefined : Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("inline-block mr-3"), page.titleElement]).SetClass("flex"), page.linkElement ]).SetClass("flex justify-between align-middle"), @@ -56,7 +58,7 @@ export default class WikipediaBox extends Combine { }), 0, { - leftOfHeader: Svg.wikipedia_svg().SetStyle("width: 1.5rem; align-self: center;").SetClass("mr-4"), + leftOfHeader: options.noImages ? undefined : Svg.wikipedia_svg().SetStyle("width: 1.5rem; align-self: center;").SetClass("mr-4"), styleHeader: header => header.SetClass("subtle-background").SetStyle("height: 3.3rem") } ) @@ -81,7 +83,6 @@ export default class WikipediaBox extends Combine { if (entry.match("[qQ][0-9]+")) { return WikipediaBox.createWikidatabox(entry, options) } else { - console.log("Creating wikipedia box for ", entry) return WikipediaBox.createWikipediabox(entry, options) } } @@ -116,7 +117,8 @@ export default class WikipediaBox extends Combine { } /** - * Given a `Q1234`, constructs a wikipedia box or wikidata box + * Given a `Q1234`, constructs a wikipedia box (if a wikipedia page is available) or wikidata box as fallback. + * */ private static createWikidatabox(wikidataId: string, options: WikipediaBoxOptions): { titleElement: BaseUIElement, @@ -168,6 +170,7 @@ export default class WikipediaBox extends Combine { } if (status[0] == "no page") { const [_, wd] = <[string, WikidataResponse]>status + options.currentState?.setData("loaded") return new Combine([ WikidataPreviewBox.WikidataResponsePreview(wd), wp.noWikipediaPage.Clone().SetClass("subtle")]).SetClass("flex flex-col p-4") @@ -197,15 +200,16 @@ export default class WikipediaBox extends Combine { const linkElement = new VariableUiElement(wikiLink.map(state => { if (typeof state !== "string") { const [pagetitle, language] = state + const popout = options.noImages ? "Source" : Svg.pop_out_svg().SetStyle("width: 1.2rem").SetClass("block") if (pagetitle === "no page") { const wd = state[1] - return new Link(Svg.pop_out_svg().SetStyle("width: 1.2rem").SetClass("block "), + return new Link(popout, "https://www.wikidata.org/wiki/" + wd.id , true) } const url = `https://${language}.wikipedia.org/wiki/${pagetitle}` - return new Link(Svg.pop_out_svg().SetStyle("width: 1.2rem").SetClass("block "), url, true) + return new Link(popout, url, true) } return undefined })) @@ -220,13 +224,14 @@ export default class WikipediaBox extends Combine { /** - * Returns the actual content in a scrollable way + * Returns the actual content in a scrollable way for the given wikipedia page */ private static createContents(pagename: string, wikipedia: Wikipedia, options:{ topBar?: BaseUIElement} & WikipediaBoxOptions): BaseUIElement { const htmlContent = wikipedia.GetArticle(pagename, options) const wp = Translations.t.general.wikipedia - const contents: UIEventSource = htmlContent.map(htmlContent => { + const contents: VariableUiElement =new VariableUiElement( + htmlContent.map(htmlContent => { if (htmlContent === undefined) { // Still loading return new Loading(wp.loading.Clone()) @@ -253,12 +258,21 @@ export default class WikipediaBox extends Combine { } return undefined - }) + })) + htmlContent.addCallbackAndRunD(c => { + if(c["success"] !== undefined){ + options.currentState?.setData("loaded") + }else if (c["error"] !== undefined){ + options.currentState?.setData("error") + }else { + options.currentState?.setData("loading") + } + }) + return new Combine([ options?.topBar?.SetClass("border-2 border-grey rounded-lg m-1 mb-0"), - new VariableUiElement(contents) - .SetClass("block pl-6 pt-2")]) + contents .SetClass("block pl-6 pt-2")]) } } \ No newline at end of file