From 9faac532b505aa885bafc1b810f01d1a1c4dd2f6 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 9 Oct 2021 22:40:52 +0200 Subject: [PATCH] Support for lexemes, decent etymology layer and theme with rudimentary icon --- .gitignore | 3 +- Logic/Web/Wikidata.ts | 315 ++++++++++++++-------- UI/Input/ValidatedTextField.ts | 17 +- UI/Popup/TagRenderingQuestion.ts | 2 +- UI/Wikipedia/WikidataPreviewBox.ts | 3 +- UI/Wikipedia/WikidataSearchBox.ts | 4 +- UI/Wikipedia/WikipediaBox.ts | 7 +- assets/layers/etymology/etymology.json | 138 ++++++++++ assets/layers/etymology/license_info.json | 10 + assets/layers/etymology/logo.svg | 107 ++++++++ assets/tagRenderings/questions.json | 18 +- assets/themes/etymology.json | 146 ++-------- css/index-tailwind-output.css | 25 +- css/tagrendering.css | 6 - css/wikipedia.css | 6 + index.css | 21 ++ test.ts | 7 +- test/Wikidata.spec.test.ts | 46 ++++ 18 files changed, 611 insertions(+), 270 deletions(-) create mode 100644 assets/layers/etymology/etymology.json create mode 100644 assets/layers/etymology/license_info.json create mode 100644 assets/layers/etymology/logo.svg diff --git a/.gitignore b/.gitignore index 5d39c4885..f2ff425f8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ Docs/Tools/stats.csv missing_translations.txt *.swp .DS_Store -Svg.ts \ No newline at end of file +Svg.ts +data/ diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts index e77dcc2ee..c23a5347c 100644 --- a/Logic/Web/Wikidata.ts +++ b/Logic/Web/Wikidata.ts @@ -2,27 +2,33 @@ import {Utils} from "../../Utils"; import {UIEventSource} from "../UIEventSource"; -export interface WikidataResponse { +export class WikidataResponse { + public readonly id: string + public readonly labels: Map + public readonly descriptions: Map + public readonly claims: Map> + public readonly wikisites: Map + public readonly commons: string - id: string, - labels: Map, - descriptions: Map, - claims: Map>, - wikisites: Map - commons: string -} + constructor( + id: string, + labels: Map, + descriptions: Map, + claims: Map>, + wikisites: Map, + commons: string + ) { -export interface WikidataSearchoptions { - lang?: "en" | string, - maxCount?: 20 | number -} + this.id = id + this.labels = labels + this.descriptions = descriptions + this.claims = claims + this.wikisites = wikisites + this.commons = commons -/** - * Utility functions around wikidata - */ -export default class Wikidata { + } - private static ParseResponse(entity: any): WikidataResponse { + public static fromJson(entity: any): WikidataResponse { const labels = new Map() for (const labelName in entity.labels) { // The labelname is the language code @@ -42,163 +48,252 @@ export default class Wikidata { const title = entity.sitelinks[labelName].title sitelinks.set(language, title) } - + const commons = sitelinks.get("commons") sitelinks.delete("commons") + const claims = WikidataResponse.extractClaims(entity.claims); + return new WikidataResponse( + entity.id, + labels, + descr, + claims, + sitelinks, + commons + ) + + } + + static extractClaims(claimsJson: any): Map> { const claims = new Map>(); - for (const claimId in entity.claims) { + for (const claimId in claimsJson) { - const claimsList: any[] = entity.claims[claimId] + const claimsList: any[] = claimsJson[claimId] const values = new Set() for (const claim of claimsList) { let value = claim.mainsnak?.datavalue?.value; if (value === undefined) { continue; } - if(value.id !== undefined){ + if (value.id !== undefined) { value = value.id } values.add(value) } claims.set(claimId, values); } + return claims + } +} - return { - claims: claims, - descriptions: descr, - id: entity.id, - labels: labels, - wikisites: sitelinks, - commons: commons +export class WikidataLexeme { + id: string + lemma: Map + senses: Map + claims: Map> + + + constructor(json) { + this.id = json.id + this.claims = WikidataResponse.extractClaims(json.claims) + this.lemma = new Map() + for (const language in json.lemmas) { + this.lemma.set(language, json.lemmas[language].value) + } + + this.senses = new Map() + + for (const sense of json.senses) { + const glosses = sense.glosses + for (const language in glosses) { + let previousSenses = this.senses.get(language) + if(previousSenses === undefined){ + previousSenses = "" + }else{ + previousSenses = previousSenses+"; " + } + this.senses.set(language, previousSenses + glosses[language].value ?? "") + } } } - private static readonly _cache = new Map>() - public static LoadWikidataEntry(value: string | number): UIEventSource<{success: WikidataResponse} | {error: any}> { + asWikidataResponse() { + return new WikidataResponse( + this.id, + this.lemma, + this.senses, + this.claims, + new Map(), + undefined + ); + } +} + +export interface WikidataSearchoptions { + lang?: "en" | string, + maxCount?: 20 | number +} + +/** + * Utility functions around wikidata + */ +export default class Wikidata { + + private static readonly _identifierPrefixes = ["Q", "L"].map(str => str.toLowerCase()) + private static readonly _prefixesToRemove = ["https://www.wikidata.org/wiki/Lexeme:", "https://www.wikidata.org/wiki/", "Lexeme:"].map(str => str.toLowerCase()) + + + private static readonly _cache = new Map>() + + public static LoadWikidataEntry(value: string | number): UIEventSource<{ success: WikidataResponse } | { error: any }> { const key = this.ExtractKey(value) const cached = Wikidata._cache.get(key) - if(cached !== undefined){ + if (cached !== undefined) { return cached } const src = UIEventSource.FromPromiseWithErr(Wikidata.LoadWikidataEntryAsync(key)) Wikidata._cache.set(key, src) return src; } - + public static async search( - search: string, - options?:WikidataSearchoptions, - page = 1 - ): Promise<{ + search: string, + options?: WikidataSearchoptions, + page = 1 + ): Promise<{ id: string, label: string, description: string }[]> { - const maxCount = options?.maxCount ?? 20 - let pageCount = Math.min(maxCount,50) - const start = page * pageCount - pageCount; - const lang = (options?.lang ?? "en") - const url = - "https://www.wikidata.org/w/api.php?action=wbsearchentities&search=" + - search + - "&language=" + - lang + - "&limit="+pageCount+"&continue=" + - start + - "&format=json&uselang=" + - lang + - "&type=item&origin=*"+ - "&props=" ;// props= removes some unused values in the result - const response = await Utils.downloadJson(url) - - const result : any[] = response.search - - if(result.length < pageCount){ - // No next page - return result; - } - if(result.length < maxCount){ - const newOptions = {...options} - newOptions.maxCount = maxCount - result.length - result.push(...await Wikidata.search(search, - newOptions, - page + 1 - )) - } - + const maxCount = options?.maxCount ?? 20 + let pageCount = Math.min(maxCount, 50) + const start = page * pageCount - pageCount; + const lang = (options?.lang ?? "en") + const url = + "https://www.wikidata.org/w/api.php?action=wbsearchentities&search=" + + search + + "&language=" + + lang + + "&limit=" + pageCount + "&continue=" + + start + + "&format=json&uselang=" + + lang + + "&type=item&origin=*" + + "&props=";// props= removes some unused values in the result + const response = await Utils.downloadJson(url) + + const result: any[] = response.search + + if (result.length < pageCount) { + // No next page return result; + } + if (result.length < maxCount) { + const newOptions = {...options} + newOptions.maxCount = maxCount - result.length + result.push(...await Wikidata.search(search, + newOptions, + page + 1 + )) + } + + return result; } - + public static async searchAndFetch( - search: string, - options?:WikidataSearchoptions -) : Promise - { + search: string, + options?: WikidataSearchoptions + ): Promise { const maxCount = options.maxCount // We provide some padding to filter away invalid values options.maxCount = Math.ceil((options.maxCount ?? 20) * 1.5) const searchResults = await Wikidata.search(search, options) - const maybeResponses = await Promise.all(searchResults.map(async r => { - try{ - return await Wikidata.LoadWikidataEntry(r.id).AsPromise() - }catch(e){ - console.error(e) - return undefined; - } + const maybeResponses = await Promise.all(searchResults.map(async r => { + try { + return await Wikidata.LoadWikidataEntry(r.id).AsPromise() + } catch (e) { + console.error(e) + return undefined; + } })) const responses = maybeResponses - .map(r => r["success"]) + .map(r => r["success"]) .filter(wd => { - if(wd === undefined){ - return false; - } - if(wd.claims.get("P31" /*Instance of*/)?.has("Q4167410"/* Wikimedia Disambiguation page*/)){ - return false; - } - return true; - }) + if (wd === undefined) { + return false; + } + if (wd.claims.get("P31" /*Instance of*/)?.has("Q4167410"/* Wikimedia Disambiguation page*/)) { + return false; + } + return true; + }) responses.splice(maxCount, responses.length - maxCount) - return responses + return responses } - - private static ExtractKey(value: string | number) : number{ + + public static ExtractKey(value: string | number): string { if (typeof value === "number") { - return value + return "Q" + value } - const wikidataUrl = "https://www.wikidata.org/wiki/" - if (value.startsWith(wikidataUrl)) { - value = value.substring(wikidataUrl.length) + if (value === undefined) { + console.error("ExtractKey: value is undefined") + return undefined; } - if (value.startsWith("http")) { + value = value.trim().toLowerCase() + + for (const prefix of Wikidata._prefixesToRemove) { + if (value.startsWith(prefix)) { + value = value.substring(prefix.length) + } + } + + if (value.startsWith("http") && value === "") { // Probably some random link in the image field - we skip it return undefined } - if (value.startsWith("Q")) { - value = value.substring(1) + + for (const identifierPrefix of Wikidata._identifierPrefixes) { + if (value.startsWith(identifierPrefix)) { + const trimmed = value.substring(identifierPrefix.length); + if(trimmed === ""){ + return undefined + } + const n = Number(trimmed) + if (isNaN(n)) { + return undefined + } + return value.toUpperCase(); + } } - const n = Number(value) - if(isNaN(n)){ - return undefined + + if (value !== "" && !isNaN(Number(value))) { + return "Q" + value } - return n; + + return undefined; } - + /** * Loads a wikidata page * @returns the entity of the given value */ public static async LoadWikidataEntryAsync(value: string | number): Promise { const id = Wikidata.ExtractKey(value) - if(id === undefined){ + if (id === undefined) { console.warn("Could not extract a wikidata entry from", value) - return undefined; + throw "Could not extract a wikidata entry from " + value } - - const url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; - const response = await Utils.downloadJson(url) - return Wikidata.ParseResponse(response.entities["Q" + id]) + + const url = "https://www.wikidata.org/wiki/Special:EntityData/" + id + ".json"; + const response = (await Utils.downloadJson(url)).entities[id] + + if (id.startsWith("L")) { + // This is a lexeme: + return new WikidataLexeme(response).asWikidataResponse() + } + + return WikidataResponse.fromJson(response) } } \ No newline at end of file diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 7218d590e..9840071aa 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -17,6 +17,7 @@ import {GeoOperations} from "../../Logic/GeoOperations"; import {Unit} from "../../Models/Unit"; import {FixedInputElement} from "./FixedInputElement"; import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"; +import Wikidata from "../../Logic/Web/Wikidata"; interface TextFieldDef { name: string, @@ -153,20 +154,23 @@ export default class ValidatedTextField { if (str === undefined) { return false; } - return (str.length > 1 && (str.startsWith("q") || str.startsWith("Q")) || str.startsWith("https://www.wikidata.org/wiki/Q")) + if(str.length <= 2){ + return false; + } + return !str.split(";").some(str => Wikidata.ExtractKey(str) === undefined) }, (str) => { if (str === undefined) { return undefined; } - const wd = "https://www.wikidata.org/wiki/"; - if (str.startsWith(wd)) { - str = str.substr(wd.length) + let out = str.split(";").map(str => Wikidata.ExtractKey(str)).join("; ") + if(str.endsWith(";")){ + out = out + ";" } - return str.toUpperCase(); + return out; }, (currentValue, inputHelperOptions) => { - const args = inputHelperOptions.args + const args = inputHelperOptions.args ?? [] const searchKey = args[0] ?? "name" let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() @@ -175,7 +179,6 @@ export default class ValidatedTextField { if (searchFor !== undefined && options !== undefined) { const prefixes = options["removePrefixes"] const postfixes = options["removePostfixes"] - for (const postfix of postfixes ?? []) { if (searchFor.endsWith(postfix)) { searchFor = searchFor.substring(0, searchFor.length - postfix.length) diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 16811a3b1..556635c57 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -136,7 +136,7 @@ export default class TagRenderingQuestion extends Combine { options.cancelButton, saveButton, bottomTags]) - this.SetClass("question") + this.SetClass("question disable-links") } diff --git a/UI/Wikipedia/WikidataPreviewBox.ts b/UI/Wikipedia/WikidataPreviewBox.ts index 650b76327..3fe01aab8 100644 --- a/UI/Wikipedia/WikidataPreviewBox.ts +++ b/UI/Wikipedia/WikidataPreviewBox.ts @@ -51,8 +51,9 @@ export default class WikidataPreviewBox extends VariableUiElement { wikidata.id, Svg.wikidata_ui().SetStyle("width: 2.5rem").SetClass("block") ]).SetClass("flex"), - "https://wikidata.org/wiki/"+wikidata.id ,true) + "https://wikidata.org/wiki/"+wikidata.id ,true).SetClass("must-link") + console.log(wikidata) let info = new Combine( [ new Combine([Translation.fromMap(wikidata.labels).SetClass("font-bold"), link]).SetClass("flex justify-between"), diff --git a/UI/Wikipedia/WikidataSearchBox.ts b/UI/Wikipedia/WikidataSearchBox.ts index dce60c02e..da0c90ebc 100644 --- a/UI/Wikipedia/WikidataSearchBox.ts +++ b/UI/Wikipedia/WikidataSearchBox.ts @@ -6,12 +6,10 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import Wikidata, {WikidataResponse} from "../../Logic/Web/Wikidata"; import Locale from "../i18n/Locale"; import {VariableUiElement} from "../Base/VariableUIElement"; -import {FixedUiElement} from "../Base/FixedUiElement"; import WikidataPreviewBox from "./WikidataPreviewBox"; import Title from "../Base/Title"; import WikipediaBox from "./WikipediaBox"; import Svg from "../../Svg"; -import Link from "../Base/Link"; export default class WikidataSearchBox extends InputElement { @@ -104,7 +102,7 @@ export default class WikidataSearchBox extends InputElement { if (wid === undefined) { return undefined } - return new WikipediaBox([wid]); + return new WikipediaBox(wid.split(";")); })).SetStyle("max-height:12.5rem"), full ]).ConstructElement(); diff --git a/UI/Wikipedia/WikipediaBox.ts b/UI/Wikipedia/WikipediaBox.ts index 0dc059ac0..4c7ba9235 100644 --- a/UI/Wikipedia/WikipediaBox.ts +++ b/UI/Wikipedia/WikipediaBox.ts @@ -22,7 +22,7 @@ export default class WikipediaBox extends Combine { const mainContents = [] - const pages = wikidataIds.map(wdId => WikipediaBox.createLinkedContent(wdId)) + const pages = wikidataIds.map(wdId => WikipediaBox.createLinkedContent(wdId.trim())) if (wikidataIds.length == 1) { const page = pages[0] mainContents.push( @@ -88,6 +88,9 @@ export default class WikipediaBox extends Combine { } const wikidata = maybewikidata["success"] + if(wikidata === undefined){ + return "failed" + } if (wikidata.wikisites.size === 0) { return ["no page", wikidata] } @@ -157,7 +160,7 @@ export default class WikipediaBox extends Combine { } return undefined })) - .SetClass("flex items-center") + .SetClass("flex items-center enable-links") return { contents: contents, diff --git a/assets/layers/etymology/etymology.json b/assets/layers/etymology/etymology.json new file mode 100644 index 000000000..5182f5380 --- /dev/null +++ b/assets/layers/etymology/etymology.json @@ -0,0 +1,138 @@ +{ + "id": "etymology", + "#": "A layer showing all objects having etymology info (either via `name:etymology:wikidata` or `name:etymology`. The intention is that this layer is reused for a certain category to also _ask_ for information", + "name": { + "en": "Has etymolgy", + "nl": "Heeft etymology info" + }, + "minzoom": 12, + "source": { + "osmTags": { + "or": [ + "name:etymology:wikidata~*", + "name:etymology~*" + ] + } + }, + "title": { + "render": { + "*": "{name}" + } + }, + "description": { + "en": "All objects which have an etymology known", + "nl": "Alle lagen met een gelinkt etymology" + }, + "tagRenderings": [ + { + "id": "wikipedia-etymology", + "question": { + "en": "What is the Wikidata-item that this object is named after?", + "nl": "Wat is het Wikidata-item van hetgeen dit object is naar vernoemd?" + }, + "freeform": { + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "steenweg", + "heirbaan", + "baan", + "straat", + "street", + "weg", + "dreef", + "laan", + "boulevard", + "pad", + "path", + "plein", + "square", + "plaza", + "wegel", + "kerk", + "church", + "kaai" + ] + } + ] + }, + "render": { + "en": "

Wikipedia article of the name giver

{wikipedia(name:etymology:wikidata):max-height:20rem}", + "nl": "

Wikipedia artikel van de naamgever

{wikipedia(name:etymology:wikidata):max-height:20rem}" + } + }, + { + "id": "zoeken op inventaris onroerend erfgoed", + "render": { + "nl": "Zoeken op inventaris onroerend erfgoed", + "en": "Search on inventaris onroerend erfgoed" + }, + "conditions": "_country=be" + }, + { + "id": "simple etymology", + "question": { + "en": "What is this object named after?
This might be written on the street name sign", + "nl": "Naar wat is dit object vernoemd?
Dit staat mogelijks vermeld op het straatnaambordje" + }, + "render": { + "en": "Named after {name:etymology}", + "nl": "Vernoemd naar {name:etymology}" + }, + "freeform": { + "key": "name:etymology" + }, + "condition": { + "or": [ + "name:etymology~*", + "name:etymology:wikidata=" + ] + } + }, + { + "id": "street-name-sign-image", + "render": { + "en": "{image_carousel(image:streetsign)}
{image_upload(image:streetsign, Add image of a street name sign)}", + "nl": "{image_carousel(image:streetsign)}
{image_upload(image:streetsign, Voeg afbeelding van straatnaambordje toe)}" + } + }, + "wikipedia" + ], + "icon": { + "render": "pin:#05d7fcaa;./assets/layers/etymology/logo.svg", + "mappings": [ + { + "if": { + "and": [ + "name:etymology=", + "name:etymology:wikidata=" + ] + }, + "then": "pin:#fcca05aa;./assets/layers/etymology/logo.svg" + } + ] + }, + "width": { + "render": "8" + }, + "iconSize": { + "render": "40,40,center" + }, + "color": { + "render": "#05d7fcaa", + "mappings": [ + { + "if": { + "and": [ + "name:etymology=", + "name:etymology:wikidata=" + ] + }, + "then": "#fcca05aa" + } + ] + } +} \ No newline at end of file diff --git a/assets/layers/etymology/license_info.json b/assets/layers/etymology/license_info.json new file mode 100644 index 000000000..d186e73a2 --- /dev/null +++ b/assets/layers/etymology/license_info.json @@ -0,0 +1,10 @@ +[ + { + "path": "logo.svg", + "license": "CC0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + } +] \ No newline at end of file diff --git a/assets/layers/etymology/logo.svg b/assets/layers/etymology/logo.svg new file mode 100644 index 000000000..6d6c9a0f6 --- /dev/null +++ b/assets/layers/etymology/logo.svg @@ -0,0 +1,107 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index fc7756492..22edf9199 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -45,7 +45,23 @@ }, "wikipedialink": { "render": "WP", - "condition": "wikipedia~*" + "freeform": { + "key": "wikidata", + "type": "wikidata" + }, + "question": { + "en": "What is the corresponding item on Wikipedia?", + "nl": "Welk Wikipedia-artikel beschrijft dit object?" + }, + "mappings": [ + { + "if": "wikidata=", + "then": { + "en": "Not linked with Wikipedia", + "nl": "Nog geen Wikipedia-artikel bekend" + } + } + ] }, "email": { "render": "{email}", diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json index e71445814..3aa346dc3 100644 --- a/assets/themes/etymology.json +++ b/assets/themes/etymology.json @@ -17,7 +17,7 @@ "nl" ], "maintainer": "", - "icon": "./assets/svg/bug.svg", + "icon": "./assets/layers/etymology/logo.svg", "version": "0", "startLat": 0, "startLon": 0, @@ -25,138 +25,26 @@ "widenFactor": 2, "socialImage": "", "layers": [ + "etymology", { - "id": "has_etymology", - "name": { - "en": "Has etymolgy", - "nl": "Heeft etymology info" - }, - "minzoom": 12, - "source": { - "osmTags": { - "or": [ - "name:etymology:wikidata~*", - "name:etymology~*" - ] - } - }, - "title": { - "render": { - "*": "{name}" - } - }, - "description": { - "en": "All objects which have an etymology known", - "nl": "Alle lagen met een gelinkt etymology" - }, - "tagRenderings": [ - { - "id": "etymology_wikidata_image", - "render": { - "*": "{image_carousel(name:etymology:wikidata)}" - } + "builtin": "etymology", + "override": { + "id": "streets_without_etymology", + "name": { + "en": "Streets without etymology information", + "nl": "Straten zonder etymologische informatie" }, - { - "id": "simple etymology", - "render": { - "en": "Named after {name:etymology}", - "nl": "Vernoemd naar {name:etymology}" - }, - "freeform": { - "key": "name:etymology" - } - }, - { - "id": "wikipedia-etymology", - "render": { - "*": "{wikipedia(name:etymology:wikidata):max-height:30rem}" - } - }, - { - "id": "wikidata-embed", - "render": { - "*": "" - }, - "condition": "name:etymology:wikidata~*" - }, - "wikipedia", - { - "id": "street-name-sign-image", - "render": { - "en": "{image_carousel(image:streetsign)}
{image_upload(image:streetsign, Add image of a street name sign)}", - "nl": "{image_carousel(image:streetsign)}
{image_upload(image:streetsign, Voeg afbeelding van straatnaambordje toe)}" + "minzoom": 18, + "source": { + "osmTags": { + "and": [ + "name~*", + "highway~*", + "highway!=bus_stop" + ] } } - ], - "icon": { - "render": "./assets/svg/bug.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f" - }, - "presets": [] - }, - { - "id": "has_a_name", - "name": { - "en": "Has etymolgy", - "nl": "Heeft etymology info" - }, - "minzoom": 12, - "source": { - "osmTags": { - "or": [ - "name:etymology:wikidata~*", - "name:etymology~*" - ] - } - }, - "title": { - "render": { - "*": "{name}" - } - }, - "description": { - "en": "All objects which have an etymology known", - "nl": "Alle lagen met een gelinkt etymology" - }, - "tagRenderings": [ - { - "id": "simple etymology", - "render": { - "en": "Named after {name:etymology}", - "nl": "Vernoemd naar {name:etymology}" - }, - "freeform": { - "key": "name:etymology" - } - }, - { - "id": "wikipedia-etymology", - "render": { - "*": "{wikipedia(name:etymology:wikidata):max-height:20rem}" - } - } - ], - "icon": { - "render": "./assets/svg/bug.svg" - }, - "width": { - "render": "8" - }, - "iconSize": { - "render": "40,40,center" - }, - "color": { - "render": "#00f" - }, - "presets": [] + } } ], "hideFromOverview": true diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index d2b2ec440..69508ef54 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -1958,6 +1958,27 @@ li::marker { max-width: 2em !important; } +.block-ruby { + display: block ruby; +} + +.disable-links a { + pointer-events: none; + text-decoration: none !important; + color: var(--subtle-detail-color-contrast) !important; +} + +.enable-links a { + pointer-events: unset; + text-decoration: underline !important; + color: unset !important; +} + +.disable-links a.must-link, .disable-links .must-link a { + /* Hide links if they are disabled */ + display: none; +} + /**************** GENERIC ****************/ .alert { @@ -2237,10 +2258,6 @@ li::marker { } @media (min-width: 640px) { - .sm\:m-1 { - margin: 0.25rem; - } - .sm\:mx-auto { margin-left: auto; margin-right: auto; diff --git a/css/tagrendering.css b/css/tagrendering.css index a87f67a47..6e154e6c1 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -20,12 +20,6 @@ height: 100%; } -.question a { - pointer-events: none; - text-decoration: none; - color: var(--subtle-detail-color-contrast) -} - .question-text { font-size: larger; font-weight: bold; diff --git a/css/wikipedia.css b/css/wikipedia.css index e636fb8dc..e261ffbde 100644 --- a/css/wikipedia.css +++ b/css/wikipedia.css @@ -33,6 +33,12 @@ text-decoration: none; } +.disable-links .wikipedia-article a { + color: black !important; + background: none !important; + text-decoration: none; +} + .wikipedia-article p { margin-bottom: 0.5rem; diff --git a/index.css b/index.css index f4e51240a..9111b847d 100644 --- a/index.css +++ b/index.css @@ -243,6 +243,27 @@ li::marker { } +.block-ruby { + display: block ruby; +} + +.disable-links a { + pointer-events: none; + text-decoration: none !important; + color: var(--subtle-detail-color-contrast) !important; +} + +.enable-links a { + pointer-events: unset; + text-decoration: underline !important; + color: unset !important; +} + +.disable-links a.must-link, .disable-links .must-link a { + /* Hide links if they are disabled */ + display: none; +} + /**************** GENERIC ****************/ diff --git a/test.ts b/test.ts index 631d1e061..e2bb93acd 100644 --- a/test.ts +++ b/test.ts @@ -1,6 +1,3 @@ -import WikidataPreviewBox from "./UI/Wikipedia/WikidataPreviewBox"; -import {UIEventSource} from "./Logic/UIEventSource"; -import Wikidata from "./Logic/Web/Wikidata"; -import WikidataSearchBox from "./UI/Wikipedia/WikidataSearchBox"; +import WikipediaBox from "./UI/Wikipedia/WikipediaBox"; -new WikidataSearchBox({searchText: new UIEventSource("Brugge")}).AttachTo("maindiv") \ No newline at end of file +new WikipediaBox(["L614072"]).AttachTo("maindiv") \ No newline at end of file diff --git a/test/Wikidata.spec.test.ts b/test/Wikidata.spec.test.ts index 1bc6a8558..f2a7bb25a 100644 --- a/test/Wikidata.spec.test.ts +++ b/test/Wikidata.spec.test.ts @@ -1881,6 +1881,52 @@ export default class WikidataSpecTest extends T { ) const wikidata = await Wikidata.LoadWikidataEntryAsync("2747456") + }], + ["Extract key from a lexeme", () => { + Utils.injectJsonDownloadForTests( + "https://www.wikidata.org/wiki/Special:EntityData/L614072.json" , + { + "entities": { + "L614072": { + "pageid": 104085278, + "ns": 146, + "title": "Lexeme:L614072", + "lastrevid": 1509989280, + "modified": "2021-10-09T18:43:52Z", + "type": "lexeme", + "id": "L614072", + "lemmas": {"nl": {"language": "nl", "value": "Groen"}}, + "lexicalCategory": "Q34698", + "language": "Q7411", + "claims": {}, + "forms": [], + "senses": [{ + "id": "L614072-S1", + "glosses": {"nl": {"language": "nl", "value": "Nieuw"}}, + "claims": {} + }, { + "id": "L614072-S2", + "glosses": {"nl": {"language": "nl", "value": "Jong"}}, + "claims": {} + }, { + "id": "L614072-S3", + "glosses": {"nl": {"language": "nl", "value": "Pril"}}, + "claims": {} + }] + } + } + } + ) + + const key = Wikidata.ExtractKey("https://www.wikidata.org/wiki/Lexeme:L614072") + T.equals("L614072", key) + + }], + ["Download a lexeme", async () => { + + const response = await Wikidata.LoadWikidataEntryAsync("https://www.wikidata.org/wiki/Lexeme:L614072") + T.isTrue(response !== undefined, "Response is undefined") + }] ]);