Add wikipedia box
This commit is contained in:
parent
1edf829cad
commit
8b4442c8cc
20 changed files with 401 additions and 149 deletions
|
@ -3,6 +3,7 @@ import ImageProvider, {ProvidedImage} from "./ImageProvider";
|
||||||
import BaseUIElement from "../../UI/BaseUIElement";
|
import BaseUIElement from "../../UI/BaseUIElement";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import {WikimediaImageProvider} from "./WikimediaImageProvider";
|
import {WikimediaImageProvider} from "./WikimediaImageProvider";
|
||||||
|
import Wikidata from "../Web/Wikidata";
|
||||||
|
|
||||||
export class WikidataImageProvider extends ImageProvider {
|
export class WikidataImageProvider extends ImageProvider {
|
||||||
|
|
||||||
|
@ -22,31 +23,18 @@ export class WikidataImageProvider extends ImageProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||||
const wikidataUrl = "https://www.wikidata.org/wiki/"
|
const entity = await Wikidata.LoadWikidataEntry(value)
|
||||||
if (value.startsWith(wikidataUrl)) {
|
|
||||||
value = value.substring(wikidataUrl.length)
|
const allImages : Promise<ProvidedImage>[] = []
|
||||||
}
|
|
||||||
if(value.startsWith("http")){
|
|
||||||
// Probably some random link in the image field - we skip it
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (!value.startsWith("Q")) {
|
|
||||||
value = "Q" + value
|
|
||||||
}
|
|
||||||
const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json";
|
|
||||||
const response = await Utils.downloadJson(url)
|
|
||||||
const entity = response.entities[value];
|
|
||||||
const commons = entity.sitelinks.commonswiki;
|
|
||||||
// P18 is the claim 'depicted in this image'
|
// P18 is the claim 'depicted in this image'
|
||||||
const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value;
|
for (const img of Array.from(entity.claims.get("P18") ?? [])) {
|
||||||
const allImages = []
|
const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined, img)
|
||||||
if (image !== undefined) {
|
|
||||||
// We found a 'File://'
|
|
||||||
const promises = await WikimediaImageProvider.singleton.ExtractUrls(key, image)
|
|
||||||
allImages.push(...promises)
|
allImages.push(...promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const commons =entity.wikisites.get("commons")
|
||||||
if (commons !== undefined) {
|
if (commons !== undefined) {
|
||||||
const promises = await WikimediaImageProvider.singleton.ExtractUrls(commons, image)
|
const promises = await WikimediaImageProvider.singleton.ExtractUrls(undefined , commons)
|
||||||
allImages.push(...promises)
|
allImages.push(...promises)
|
||||||
}
|
}
|
||||||
return allImages
|
return allImages
|
||||||
|
|
|
@ -44,7 +44,6 @@ export class WikimediaImageProvider extends ImageProvider {
|
||||||
if (continueParameter !== undefined) {
|
if (continueParameter !== undefined) {
|
||||||
url = `${url}&cmcontinue=${continueParameter}`;
|
url = `${url}&cmcontinue=${continueParameter}`;
|
||||||
}
|
}
|
||||||
console.debug("Loading a wikimedia category: ", url)
|
|
||||||
const response = await Utils.downloadJson(url)
|
const response = await Utils.downloadJson(url)
|
||||||
const members = response.query?.categorymembers ?? [];
|
const members = response.query?.categorymembers ?? [];
|
||||||
const imageOverview: string[] = members.map(member => member.title);
|
const imageOverview: string[] = members.map(member => member.title);
|
||||||
|
@ -135,8 +134,7 @@ export class WikimediaImageProvider extends ImageProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
public async ExtractUrls(key: string, value: string): Promise<Promise<ProvidedImage>[]> {
|
||||||
|
if(key !== undefined && key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){
|
||||||
if(key !== this.commons_key && !value.startsWith(WikimediaImageProvider.commonsPrefix)){
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
89
Logic/Web/Wikidata.ts
Normal file
89
Logic/Web/Wikidata.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
|
||||||
|
|
||||||
|
export interface WikidataResponse {
|
||||||
|
|
||||||
|
id: string,
|
||||||
|
labels: Map<string, string>,
|
||||||
|
descriptions: Map<string, string>,
|
||||||
|
claims: Map<string, Set<string>>,
|
||||||
|
wikisites: Map<string, string>
|
||||||
|
commons: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility functions around wikidata
|
||||||
|
*/
|
||||||
|
export default class Wikidata {
|
||||||
|
|
||||||
|
private static ParseResponse(entity: any): WikidataResponse {
|
||||||
|
const labels = new Map<string, string>()
|
||||||
|
for (const labelName in entity.labels) {
|
||||||
|
// The labelname is the language code
|
||||||
|
labels.set(labelName, entity.labels[labelName].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const descr = new Map<string, string>()
|
||||||
|
for (const labelName in entity.descriptions) {
|
||||||
|
// The labelname is the language code
|
||||||
|
descr.set(labelName, entity.descriptions[labelName].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const sitelinks = new Map<string, string>();
|
||||||
|
for (const labelName in entity.sitelinks) {
|
||||||
|
// labelName is `${language}wiki`
|
||||||
|
const language = labelName.substring(0, labelName.length - 4)
|
||||||
|
const title = entity.sitelinks[labelName].title
|
||||||
|
sitelinks.set(language, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
const commons = sitelinks.get("commons")
|
||||||
|
sitelinks.delete("commons")
|
||||||
|
|
||||||
|
const claims = new Map<string, Set<string>>();
|
||||||
|
for (const claimId of entity.claims) {
|
||||||
|
|
||||||
|
const claimsList: any[] = entity.claims[claimId]
|
||||||
|
const values = new Set<string>()
|
||||||
|
for (const claim of claimsList) {
|
||||||
|
const value = claim.mainsnak.datavalue.value;
|
||||||
|
values.add(value)
|
||||||
|
}
|
||||||
|
claims.set(claimId, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
claims: claims,
|
||||||
|
descriptions: descr,
|
||||||
|
id: entity.id,
|
||||||
|
labels: labels,
|
||||||
|
wikisites: sitelinks,
|
||||||
|
commons: commons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a wikidata page
|
||||||
|
* @returns the entity of the given value
|
||||||
|
*/
|
||||||
|
public static async LoadWikidataEntry(value: string | number): Promise<WikidataResponse> {
|
||||||
|
const wikidataUrl = "https://www.wikidata.org/wiki/"
|
||||||
|
if (typeof value === "number") {
|
||||||
|
value = "Q" + value
|
||||||
|
}
|
||||||
|
if (value.startsWith(wikidataUrl)) {
|
||||||
|
value = value.substring(wikidataUrl.length)
|
||||||
|
}
|
||||||
|
if (value.startsWith("http")) {
|
||||||
|
// Probably some random link in the image field - we skip it
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (!value.startsWith("Q")) {
|
||||||
|
value = "Q" + value
|
||||||
|
}
|
||||||
|
const url = "https://www.wikidata.org/wiki/Special:EntityData/" + value + ".json";
|
||||||
|
const response = await Utils.downloadJson(url)
|
||||||
|
return Wikidata.ParseResponse(response.entities[value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,8 @@
|
||||||
* Some usefull utility functions around the wikipedia API
|
* Some usefull utility functions around the wikipedia API
|
||||||
*/
|
*/
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import WikipediaBox from "../../UI/WikipediaBox";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
|
import Wikidata from "./Wikidata";
|
||||||
|
|
||||||
export default class Wikipedia {
|
export default class Wikipedia {
|
||||||
|
|
||||||
|
@ -14,22 +15,36 @@ export default class Wikipedia {
|
||||||
private static readonly classesToRemove = [
|
private static readonly classesToRemove = [
|
||||||
"shortdescription",
|
"shortdescription",
|
||||||
"sidebar",
|
"sidebar",
|
||||||
"infobox",
|
"infobox","infobox_v2",
|
||||||
|
"noprint",
|
||||||
|
"ambox",
|
||||||
"mw-editsection",
|
"mw-editsection",
|
||||||
|
"mw-selflink",
|
||||||
"hatnote" // Often redirects
|
"hatnote" // Often redirects
|
||||||
]
|
]
|
||||||
|
|
||||||
public static async GetArticle(options: {
|
private static readonly _cache = new Map<string, UIEventSource<{ success: string } | { error: any }>>()
|
||||||
|
|
||||||
|
public static GetArticle(options: {
|
||||||
pageName: string,
|
pageName: string,
|
||||||
language?: "en" | string,
|
language?: "en" | string}): UIEventSource<{ success: string } | { error: any }>{
|
||||||
section?: number,
|
const key = (options.language ?? "en")+":"+options.pageName
|
||||||
|
const cached = Wikipedia._cache.get(key)
|
||||||
|
if(cached !== undefined){
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
const v = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticleAsync(options))
|
||||||
|
Wikipedia._cache.set(key, v)
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async GetArticleAsync(options: {
|
||||||
|
pageName: string,
|
||||||
|
language?: "en" | string
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
|
|
||||||
let section = ""
|
const language = options.language ?? "en"
|
||||||
if (options.section !== undefined) {
|
const url = `https://${language}.wikipedia.org/w/api.php?action=parse&format=json&origin=*&prop=text&page=` + options.pageName
|
||||||
section = "§ion=" + options.section
|
|
||||||
}
|
|
||||||
const url = `https://${options.language ?? "en"}.wikipedia.org/w/api.php?action=parse${section}&format=json&origin=*&prop=text&page=` + options.pageName
|
|
||||||
const response = await Utils.downloadJson(url)
|
const response = await Utils.downloadJson(url)
|
||||||
const html = response["parse"]["text"]["*"];
|
const html = response["parse"]["text"]["*"];
|
||||||
|
|
||||||
|
@ -43,7 +58,19 @@ export default class Wikipedia {
|
||||||
toRemoveElement.parentElement?.removeChild(toRemoveElement)
|
toRemoveElement.parentElement?.removeChild(toRemoveElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return content.innerHTML;
|
|
||||||
|
const links = Array.from(content.getElementsByTagName("a"))
|
||||||
|
|
||||||
|
console.log("Links are", links)
|
||||||
|
// Rewrite relative links to absolute links + open them in a new tab
|
||||||
|
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 = `https://${language}.wikipedia.org${link.getAttribute("href")}`;
|
||||||
|
})
|
||||||
|
|
||||||
|
return content.innerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -9,9 +9,9 @@ export default class Loading extends Combine {
|
||||||
const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone();
|
const t = Translations.T(msg ) ?? Translations.t.general.loading.Clone();
|
||||||
t.SetClass("pl-2")
|
t.SetClass("pl-2")
|
||||||
super([
|
super([
|
||||||
Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem; margin-bottom: 4px;"),
|
Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem;"),
|
||||||
t
|
t
|
||||||
])
|
])
|
||||||
this.SetClass("flex m-1")
|
this.SetClass("flex p-1")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -225,7 +225,6 @@ export default class ShowDataLayer {
|
||||||
popup.setContent(`<div style='height: 65vh' id='${id}'>Popup for ${feature.properties.id} ${feature.geometry.type}</div>`)
|
popup.setContent(`<div style='height: 65vh' id='${id}'>Popup for ${feature.properties.id} ${feature.geometry.type}</div>`)
|
||||||
|
|
||||||
leafletLayer.on("popupopen", () => {
|
leafletLayer.on("popupopen", () => {
|
||||||
console.trace(`Opening the popup for ${feature.properties.id} ${feature.geometry.type}`)
|
|
||||||
if (infobox === undefined) {
|
if (infobox === undefined) {
|
||||||
const tags = State.state.allElements.getEventSourceById(feature.properties.id);
|
const tags = State.state.allElements.getEventSourceById(feature.properties.id);
|
||||||
infobox = new FeatureInfoBox(tags, layer);
|
infobox = new FeatureInfoBox(tags, layer);
|
||||||
|
@ -239,7 +238,9 @@ export default class ShowDataLayer {
|
||||||
}
|
}
|
||||||
infobox.AttachTo(id)
|
infobox.AttachTo(id)
|
||||||
infobox.Activate();
|
infobox.Activate();
|
||||||
State.state.selectedElement.setData(feature)
|
if (State.state?.selectedElement?.data?.properties?.id !== feature.properties.id) {
|
||||||
|
State.state.selectedElement.setData(feature)
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSou
|
||||||
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
|
import ShowDataMultiLayer from "./ShowDataLayer/ShowDataMultiLayer";
|
||||||
import Minimap from "./Base/Minimap";
|
import Minimap from "./Base/Minimap";
|
||||||
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders";
|
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders";
|
||||||
|
import WikipediaBox from "./WikipediaBox";
|
||||||
|
|
||||||
export interface SpecialVisualization {
|
export interface SpecialVisualization {
|
||||||
funcName: string,
|
funcName: string,
|
||||||
|
@ -84,6 +85,20 @@ export default class SpecialVisualizations {
|
||||||
return new ImageUploadFlow(tags, args[0])
|
return new ImageUploadFlow(tags, args[0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
funcName: "wikipedia",
|
||||||
|
docs: "A box showing the corresponding wikipedia article - based on the wikidata tag",
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "keyToShowWikipediaFor",
|
||||||
|
doc: "Use the wikidata entry from this key to show the wikipedia article for",
|
||||||
|
defaultValue: "wikidata"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
example: "`{wikipedia()}` is a basic example, `{wikipedia(name:etymology:wikidata)}` to show the wikipedia page of whom the feature was named after. Also remember that these can be styled, e.g. `{wikipedia():max-height: 10rem}` to limit the height",
|
||||||
|
constr: (_, tagsSource, args) =>
|
||||||
|
new WikipediaBox( tagsSource.map(tags => tags[args[0]]))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
funcName: "minimap",
|
funcName: "minimap",
|
||||||
docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div",
|
docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div",
|
||||||
|
@ -153,10 +168,10 @@ export default class SpecialVisualizations {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
new ShowDataMultiLayer(
|
new ShowDataMultiLayer(
|
||||||
{
|
{
|
||||||
leafletMap: minimap["leafletMap"],
|
leafletMap: minimap["leafletMap"],
|
||||||
enablePopups : false,
|
enablePopups: false,
|
||||||
zoomToFeatures: true,
|
zoomToFeatures: true,
|
||||||
layers: State.state.filteredLayers,
|
layers: State.state.filteredLayers,
|
||||||
features: new StaticFeatureSource(featuresToShow, true)
|
features: new StaticFeatureSource(featuresToShow, true)
|
||||||
|
@ -351,17 +366,17 @@ export default class SpecialVisualizations {
|
||||||
const key = args [0]
|
const key = args [0]
|
||||||
return new VariableUiElement(
|
return new VariableUiElement(
|
||||||
tagSource.map(tags => tags[key]).map(value => {
|
tagSource.map(tags => tags[key]).map(value => {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units))
|
const allUnits = [].concat(...state.layoutToUse.layers.map(lyr => lyr.units))
|
||||||
const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0]
|
const unit = allUnits.filter(unit => unit.isApplicableToKey(key))[0]
|
||||||
if (unit === undefined) {
|
if (unit === undefined) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return unit.asHumanLongValue(value);
|
return unit.asHumanLongValue(value);
|
||||||
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -411,8 +426,8 @@ There are also some technicalities in your theme to keep in mind:
|
||||||
}
|
}
|
||||||
return kv
|
return kv
|
||||||
})
|
})
|
||||||
const rewrittenTags : UIEventSource<Tag[]> = tagSource.map(tags => {
|
const rewrittenTags: UIEventSource<Tag[]> = tagSource.map(tags => {
|
||||||
const newTags : Tag [] = []
|
const newTags: Tag [] = []
|
||||||
for (const [key, value] of tgsSpec) {
|
for (const [key, value] of tgsSpec) {
|
||||||
if (value.startsWith('$')) {
|
if (value.startsWith('$')) {
|
||||||
const origKey = value.substring(1)
|
const origKey = value.substring(1)
|
||||||
|
@ -446,9 +461,9 @@ There are also some technicalities in your theme to keep in mind:
|
||||||
[
|
[
|
||||||
new Title(viz.funcName, 3),
|
new Title(viz.funcName, 3),
|
||||||
viz.docs,
|
viz.docs,
|
||||||
new Table(["name", "default", "description"],
|
viz.args.length > 0 ? new Table(["name", "default", "description"],
|
||||||
viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc])
|
viz.args.map(arg => [arg.name, arg.defaultValue ?? "undefined", arg.doc])
|
||||||
),
|
) : undefined,
|
||||||
new Title("Example usage", 4),
|
new Title("Example usage", 4),
|
||||||
new FixedUiElement(
|
new FixedUiElement(
|
||||||
viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`"
|
viz.example ?? "`{" + viz.funcName + "(" + viz.args.map(arg => arg.defaultValue).join(",") + ")}`"
|
||||||
|
|
|
@ -8,46 +8,109 @@ import BaseUIElement from "./BaseUIElement";
|
||||||
import Title from "./Base/Title";
|
import Title from "./Base/Title";
|
||||||
import Translations from "./i18n/Translations";
|
import Translations from "./i18n/Translations";
|
||||||
import Svg from "../Svg";
|
import Svg from "../Svg";
|
||||||
|
import Wikidata from "../Logic/Web/Wikidata";
|
||||||
|
import Locale from "./i18n/Locale";
|
||||||
|
|
||||||
export default class WikipediaBox extends Combine{
|
export default class WikipediaBox extends Combine {
|
||||||
|
|
||||||
constructor(options: {
|
private static async ExtractWikiPages(wikidata): Promise<Map<string, string>> {
|
||||||
pagename: string,
|
return (await Wikidata.LoadWikidataEntry(wikidata)).wikisites
|
||||||
language: string
|
|
||||||
}) {
|
|
||||||
|
|
||||||
const htmlContent = UIEventSource.FromPromiseWithErr(Wikipedia.GetArticle({
|
|
||||||
pageName: options.pagename,
|
|
||||||
language: options.language,
|
|
||||||
removeInfoBoxes: true
|
|
||||||
}))
|
|
||||||
|
|
||||||
const contents : UIEventSource<string | BaseUIElement> = htmlContent.map(htmlContent => {
|
|
||||||
if(htmlContent === undefined){
|
|
||||||
// Still loading
|
|
||||||
return new Loading("Loading wikipedia page").SetClass("p-4")
|
|
||||||
}
|
|
||||||
if(htmlContent["success"] !== undefined){
|
|
||||||
return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article")
|
|
||||||
}
|
|
||||||
if(htmlContent["error"]){
|
|
||||||
return new FixedUiElement(htmlContent["error"]).SetClass("alert p-4")
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
const scrollable = new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")])
|
|
||||||
.SetClass("block overflow-auto normal-background rounded-lg")
|
|
||||||
super([
|
|
||||||
new Combine([Svg.wikipedia_svg().SetStyle("width: 1.5rem").SetClass("mr-3"),
|
|
||||||
new Title(Translations.t.general.wikipedia.wikipediaboxTitle, 2)]).SetClass("flex"),
|
|
||||||
scrollable])
|
|
||||||
|
|
||||||
this
|
|
||||||
.SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static _cache = new Map()
|
||||||
|
|
||||||
|
constructor(wikidataId: string | UIEventSource<string>) {
|
||||||
|
const wp = Translations.t.general.wikipedia;
|
||||||
|
if(typeof wikidataId === "string"){
|
||||||
|
wikidataId = new UIEventSource(wikidataId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const knownPages = new UIEventSource<{success:Map<string, string>}|{error:any}>(undefined)
|
||||||
|
|
||||||
|
wikidataId.addCallbackAndRunD(wikidataId => {
|
||||||
|
WikipediaBox.ExtractWikiPages(wikidataId).then(pages => {
|
||||||
|
knownPages.setData({success:pages})
|
||||||
|
}).catch(err=> {
|
||||||
|
knownPages.setData({error: err})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const cachedPages = new Map<string, BaseUIElement>()
|
||||||
|
|
||||||
|
const contents = new VariableUiElement(
|
||||||
|
knownPages.map(pages => {
|
||||||
|
|
||||||
|
if (pages === undefined) {
|
||||||
|
return new Loading(wp.loading.Clone())
|
||||||
|
}
|
||||||
|
if (pages["error"] !== undefined) {
|
||||||
|
return wp.failed.Clone().SetClass("alert p-4")
|
||||||
|
}
|
||||||
|
const dict: Map<string, string> = pages["success"]
|
||||||
|
|
||||||
|
const preferredLanguage = [Locale.language.data, "en", Array.from(dict.keys())[0]]
|
||||||
|
let language
|
||||||
|
let pagetitle;
|
||||||
|
let i = 0
|
||||||
|
do {
|
||||||
|
language = preferredLanguage[i]
|
||||||
|
pagetitle = dict.get(language)
|
||||||
|
i++;
|
||||||
|
if(i >= preferredLanguage.length){
|
||||||
|
return wp.noWikipediaPage.Clone()
|
||||||
|
}
|
||||||
|
} while (pagetitle === undefined)
|
||||||
|
|
||||||
|
if(cachedPages.has(language)){
|
||||||
|
return cachedPages.get(language)
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = WikipediaBox.createContents(pagetitle, language);
|
||||||
|
cachedPages.set(language, page)
|
||||||
|
return page
|
||||||
|
}, [Locale.language])
|
||||||
|
).SetClass("overflow-auto normal-background rounded-lg")
|
||||||
|
|
||||||
|
|
||||||
|
super([
|
||||||
|
new Combine([Svg.wikipedia_ui().SetStyle("width: 1.5rem").SetClass("mr-3"),
|
||||||
|
new Title(Translations.t.general.wikipedia.wikipediaboxTitle.Clone(), 2)]).SetClass("flex"),
|
||||||
|
contents])
|
||||||
|
|
||||||
|
this
|
||||||
|
.SetClass("block rounded-xl subtle-background m-1 p-2 flex flex-col")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the actual content in a scrollable way
|
||||||
|
* @param pagename
|
||||||
|
* @param language
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private static createContents(pagename: string, language: string): BaseUIElement {
|
||||||
|
const htmlContent = Wikipedia.GetArticle({
|
||||||
|
pageName: pagename,
|
||||||
|
language: language
|
||||||
|
})
|
||||||
|
const wp = Translations.t.general.wikipedia
|
||||||
|
const contents: UIEventSource<string | BaseUIElement> = htmlContent.map(htmlContent => {
|
||||||
|
if (htmlContent === undefined) {
|
||||||
|
// Still loading
|
||||||
|
return new Loading(wp.loading.Clone())
|
||||||
|
}
|
||||||
|
if (htmlContent["success"] !== undefined) {
|
||||||
|
return new FixedUiElement(htmlContent["success"]).SetClass("wikipedia-article")
|
||||||
|
}
|
||||||
|
if (htmlContent["error"]) {
|
||||||
|
return wp.failed.Clone().SetClass("alert p-4")
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Combine([new VariableUiElement(contents).SetClass("block pl-6 pt-2")])
|
||||||
|
.SetClass("block")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -392,7 +392,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "Surface area"
|
"id": "Surface area"
|
||||||
}
|
},
|
||||||
|
"wikipedia"
|
||||||
],
|
],
|
||||||
"wayHandling": 2,
|
"wayHandling": 2,
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
|
@ -122,7 +122,8 @@
|
||||||
},
|
},
|
||||||
"id": "Payment methods"
|
"id": "Payment methods"
|
||||||
},
|
},
|
||||||
"wheelchair-access"
|
"wheelchair-access",
|
||||||
|
"wikipedia"
|
||||||
],
|
],
|
||||||
"wayHandling": 1,
|
"wayHandling": 1,
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
|
@ -5,12 +5,17 @@
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
viewBox="0 0 25 25"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
viewBox="0 0 24.022156 24.021992"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg6">
|
id="svg9"
|
||||||
|
sodipodi:docname="loading.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||||
|
width="24.022156"
|
||||||
|
height="24.021992">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata12">
|
id="metadata13">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
<cc:Work
|
<cc:Work
|
||||||
rdf:about="">
|
rdf:about="">
|
||||||
|
@ -21,18 +26,53 @@
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1043"
|
||||||
|
id="namedview11"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="19.666667"
|
||||||
|
inkscape:cx="-1.7824593"
|
||||||
|
inkscape:cy="7.7694192"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg9" />
|
||||||
<defs
|
<defs
|
||||||
id="defs10" />
|
id="defs4">
|
||||||
<circle
|
<style
|
||||||
class="opacity-25"
|
id="style2">.cls-1{fill:#000;}</style>
|
||||||
cx="12.529661"
|
</defs>
|
||||||
cy="12.529661"
|
|
||||||
r="10.441384"
|
|
||||||
id="circle2"
|
|
||||||
style="stroke:#000000;stroke-width:4.17655373;stroke-opacity:0.33976835" />
|
|
||||||
<path
|
<path
|
||||||
style="fill:currentColor;stroke-width:1.04413843"
|
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.26200151;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.26635515"
|
||||||
class="opacity-75"
|
id="path821"
|
||||||
d="M 4.1765537,12.529661 A 8.3531073,8.3531073 0 0 1 12.529661,4.1765537 V 0 C 5.6101557,0 0,5.6101557 0,12.529661 Z m 2.0882768,5.524536 A 8.3134301,8.3134301 0 0 1 4.1765537,12.529661 H 0 c 0,3.176269 1.1850971,6.081062 3.1324153,8.288371 z"
|
sodipodi:type="arc"
|
||||||
id="path4" />
|
sodipodi:cx="12.010992"
|
||||||
|
sodipodi:cy="12.010992"
|
||||||
|
sodipodi:rx="10.379992"
|
||||||
|
sodipodi:ry="10.379992"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:end="6.2828013"
|
||||||
|
sodipodi:open="true"
|
||||||
|
d="M 22.390984,12.010992 A 10.379992,10.379992 0 0 1 12.011989,22.390984 10.379992,10.379992 0 0 1 1.6310007,12.012985 10.379992,10.379992 0 0 1 12.008003,1.6310009 10.379992,10.379992 0 0 1 22.390983,12.007006" />
|
||||||
|
<path
|
||||||
|
d="m 22.390984,12.010992 a 10.379992,10.379992 0 0 1 -3.26001,7.55315 10.379992,10.379992 0 0 1 -7.732307,2.808765"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:end="1.6298215"
|
||||||
|
sodipodi:start="0"
|
||||||
|
sodipodi:ry="10.379992"
|
||||||
|
sodipodi:rx="10.379992"
|
||||||
|
sodipodi:cy="12.010992"
|
||||||
|
sodipodi:cx="12.010992"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
id="path838"
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.26200151;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -2,6 +2,9 @@
|
||||||
"images": {
|
"images": {
|
||||||
"render": "{image_carousel()}{image_upload()}"
|
"render": "{image_carousel()}{image_upload()}"
|
||||||
},
|
},
|
||||||
|
"wikipedia": {
|
||||||
|
"render": "{wikipedia():max-height:25rem}"
|
||||||
|
},
|
||||||
"reviews": {
|
"reviews": {
|
||||||
"render": "{reviews()}"
|
"render": "{reviews()}"
|
||||||
},
|
},
|
||||||
|
|
|
@ -812,10 +812,6 @@ video {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-4 {
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-2 {
|
.m-2 {
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
|
@ -824,8 +820,8 @@ video {
|
||||||
margin: 0.75rem;
|
margin: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-6 {
|
.m-4 {
|
||||||
margin: 1.5rem;
|
margin: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.my-2 {
|
.my-2 {
|
||||||
|
@ -852,8 +848,8 @@ video {
|
||||||
margin-left: 0.75rem;
|
margin-left: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml-2 {
|
.mr-3 {
|
||||||
margin-left: 0.5rem;
|
margin-right: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-2 {
|
.mb-2 {
|
||||||
|
@ -892,6 +888,10 @@ video {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ml-2 {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mt-4 {
|
.mt-4 {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
@ -912,18 +912,10 @@ video {
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-3 {
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb-4 {
|
.mb-4 {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml-4 {
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-border {
|
.box-border {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -1794,8 +1786,6 @@ svg, img {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: unset;
|
|
||||||
vertical-align: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapcontrol svg path {
|
.mapcontrol svg path {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */
|
/* This stylesheet reimplements a few classes from wikipedia to show their articles prettily */
|
||||||
|
|
||||||
.wikipedia-article {
|
.wikipedia-article {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wikipedia-article .tright {
|
.wikipedia-article .tright {
|
||||||
|
@ -9,6 +9,12 @@
|
||||||
clear: right;
|
clear: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wikipedia-article svg, img {
|
||||||
|
width: unset;
|
||||||
|
height: unset;
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.wikipedia-article .thumb {
|
.wikipedia-article .thumb {
|
||||||
background: var(--subtle-detail-color);
|
background: var(--subtle-detail-color);
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
|
@ -17,15 +23,17 @@
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wikipedia-article a {
|
.wikipedia-article a:hover a:focus {
|
||||||
color: #0645ad;
|
text-decoration: underline !important;
|
||||||
background: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wikipedia-article a:hover a:focus {
|
.wikipedia-article a {
|
||||||
text-decoration: underline;
|
color: #0645ad !important;
|
||||||
|
background: none !important;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.wikipedia-article p {
|
.wikipedia-article p {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,6 @@ svg, img {
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: unset;
|
|
||||||
vertical-align: unset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapcontrol svg path {
|
.mapcontrol svg path {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
<link href="vendor/MarkerCluster.css" rel="stylesheet"/>
|
<link href="vendor/MarkerCluster.css" rel="stylesheet"/>
|
||||||
<link href="vendor/MarkerCluster.Default.css" rel="stylesheet"/>
|
<link href="vendor/MarkerCluster.Default.css" rel="stylesheet"/>
|
||||||
<link href="./css/index-tailwind-output.css" rel="stylesheet" />
|
<link href="./css/index-tailwind-output.css" rel="stylesheet" />
|
||||||
|
<link href="./css/wikipedia.css" rel="stylesheet" />
|
||||||
<meta content="website" property="og:type">
|
<meta content="website" property="og:type">
|
||||||
|
|
||||||
<!-- THEME-SPECIFIC -->
|
<!-- THEME-SPECIFIC -->
|
||||||
|
|
|
@ -218,7 +218,10 @@
|
||||||
"error_loading": "Could not load the histogram"
|
"error_loading": "Could not load the histogram"
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"wikipediaboxTitle": "Wikipedia"
|
"wikipediaboxTitle": "Wikipedia",
|
||||||
|
"failed":"Loading the wikipedia entry failed",
|
||||||
|
"loading": "Loading Wikipedia...",
|
||||||
|
"noWikipediaPage": "This wikidata item has no corresponding wikipedia page yet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"favourite": {
|
"favourite": {
|
||||||
|
|
|
@ -743,6 +743,22 @@
|
||||||
"description": "On this map, publicly accessible drinking water spots are shown and can be easily added",
|
"description": "On this map, publicly accessible drinking water spots are shown and can be easily added",
|
||||||
"title": "Drinking Water"
|
"title": "Drinking Water"
|
||||||
},
|
},
|
||||||
|
"etymology": {
|
||||||
|
"description": "On this map, you can see what an object is named after. The streets, buildings, ... come from OpenStreetMap which got linked with Wikidata. The information comes from Wpikipedia.",
|
||||||
|
"layers": {
|
||||||
|
"0": {
|
||||||
|
"description": "All objects which have an etymology known",
|
||||||
|
"name": "Has etymolgy",
|
||||||
|
"tagRenderings": {
|
||||||
|
"simple etymology": {
|
||||||
|
"render": "Named after {name:etymology}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shortDescription": "What is the origin of a toponym?",
|
||||||
|
"title": "Open Etymology Map"
|
||||||
|
},
|
||||||
"facadegardens": {
|
"facadegardens": {
|
||||||
"description": "<a href='https://nl.wikipedia.org/wiki/Geveltuin' target=_blank>Facade gardens</a>, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality. <br/> Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.<br/>More info about the project at <a href='https://klimaan.be/' target=_blank>klimaan.be</a>.",
|
"description": "<a href='https://nl.wikipedia.org/wiki/Geveltuin' target=_blank>Facade gardens</a>, green facades and trees in the city not only bring peace and quiet, but also a more beautiful city, greater biodiversity, a cooling effect and better air quality. <br/> Klimaan VZW and Mechelen Klimaatneutraal want to map existing and new facade gardens as an example for people who want to build their own garden or for city walkers who love nature.<br/>More info about the project at <a href='https://klimaan.be/' target=_blank>klimaan.be</a>.",
|
||||||
"layers": {
|
"layers": {
|
||||||
|
|
|
@ -624,6 +624,22 @@
|
||||||
"description": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen",
|
"description": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen",
|
||||||
"title": "Drinkwaterpunten"
|
"title": "Drinkwaterpunten"
|
||||||
},
|
},
|
||||||
|
"etymology": {
|
||||||
|
"description": "Op deze kaart zie je waar een plaats naar is vernoemd. De straten, gebouwen, ... komen uit OpenStreetMap, waar een link naar Wikidata werd gelegd. De informatie komt uit wikipedia.",
|
||||||
|
"layers": {
|
||||||
|
"0": {
|
||||||
|
"description": "Alle lagen met een gelinkt etymology",
|
||||||
|
"name": "Heeft etymology info",
|
||||||
|
"tagRenderings": {
|
||||||
|
"simple etymology": {
|
||||||
|
"render": "Vernoemd naar {name:etymology}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shortDescription": "Wat is de oorsprong van een plaatsnaam?",
|
||||||
|
"title": "Open Etymology-kaart"
|
||||||
|
},
|
||||||
"facadegardens": {
|
"facadegardens": {
|
||||||
"description": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit. <br/> Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur. <br/>Meer info over het project op <a href='https://klimaan.be/' target=_blank>klimaan.be</a>.",
|
"description": "Ontharde voortuintjes, groene gevels en bomen ín de stad brengen naast rust ook een mooiere stad, een grotere biodiversiteit, een verkoelend effect en een betere luchtkwaliteit. <br/> Klimaan VZW en 'Mechelen Klimaatneutraal' willen met het project Klim(t)aan je Gevel bestaande en nieuwe geveltuintjes in kaart brengen als voorbeeld voor mensen zelf een tuintje willen aanleggen of voor stadwandelaars die houden van de natuur. <br/>Meer info over het project op <a href='https://klimaan.be/' target=_blank>klimaan.be</a>.",
|
||||||
"layers": {
|
"layers": {
|
||||||
|
|
16
test.ts
16
test.ts
|
@ -1,14 +1,8 @@
|
||||||
import Wikipedia from "./Logic/Web/Wikipedia";
|
import Wikidata from "./Logic/Web/Wikidata";
|
||||||
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
|
||||||
import WikipediaBox from "./UI/WikipediaBox";
|
import WikipediaBox from "./UI/WikipediaBox";
|
||||||
import Loading from "./UI/Base/Loading";
|
import Locale from "./UI/i18n/Locale";
|
||||||
|
import LanguagePicker from "./UI/LanguagePicker";
|
||||||
|
|
||||||
|
new WikipediaBox("Q177").SetStyle("max-height: 25rem")
|
||||||
new WikipediaBox({
|
|
||||||
pagename: "Poertoren",
|
|
||||||
language: "nl"
|
|
||||||
})
|
|
||||||
.SetStyle("max-height: 20rem;")
|
|
||||||
.AttachTo("maindiv")
|
.AttachTo("maindiv")
|
||||||
|
LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv")
|
||||||
|
|
Loading…
Reference in a new issue