import {ImagesInCategory, Wikidata, Wikimedia} from "../Web/Wikimedia"; import {UIEventSource} from "../UIEventSource"; /** * There are multiple way to fetch images for an object * 1) There is an image tag * 2) There is an image tag, the image tag contains multiple ';'-seperated URLS * 3) there are multiple image tags, e.g. 'image', 'image:0', 'image:1', and 'image_0', 'image_1' - however, these are pretty rare so we are gonna ignore them * 4) There is a wikimedia_commons-tag, which either has a 'File': or a 'category:' containing images * 5) There is a wikidata-tag, and the wikidata item either has an 'image' attribute or has 'a link to a wikimedia commons category' * 6) There is a wikipedia article, from which we can deduct the wikidata item * * For some images, author and license should be shown */ /** * Class which search for all the possible locations for images and which builds a list of UI-elements for it. * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel. * */ export class ImageSearcher extends UIEventSource<{ key: string, url: string }[]>{ private readonly _wdItem = new UIEventSource(""); private readonly _commons = new UIEventSource(""); constructor(tags: UIEventSource, imagePrefix = "image", loadSpecial = true) { super([]) const self = this; function AddImages(images: { key: string, url: string }[]) { const oldUrls = self.data.map(kurl => kurl.url); let somethingChanged = false; for (const image of images) { const url = image.url; const key = image.key; if (url === undefined || url === null || url === "") { continue; } if (oldUrls.indexOf(url) >= 0) { // Already exists continue; } self.data.push(image); somethingChanged = true; } if (somethingChanged) { self.ping(); } } // By wrapping this in a UIEventSource, we prevent multiple queries of loadWikiData this._wdItem.addCallback(wdItemContents => { const images = ImageSearcher.loadWikidata(wdItemContents).map(url => { return {url: url, key: undefined} }); AddImages(images); }); this._commons.addCallback(commonsData => { const images = ImageSearcher.LoadCommons(commonsData).map(url => { return {url: url, key: undefined} }); AddImages(images); }); tags.addCallbackAndRun(tags => { const images = ImageSearcher.LoadImages(tags, imagePrefix, loadSpecial); AddImages(images); }); if (loadSpecial) { tags.addCallbackAndRun(tags => { const wdItem = tags.wikidata; if (wdItem !== undefined) { self._wdItem.setData(wdItem); } const commons = tags.wikimedia_commons; if (commons !== undefined) { self._commons.setData(commons); } if (tags.mapillary) { let mapillary = tags.mapillary; const prefix = "https://www.mapillary.com/map/im/"; let regex = /https?:\/\/www.mapillary.com\/app\/.*&pKey=([^&]*)/ let match = mapillary.match(regex); if (match) { mapillary = match[1]; } if (mapillary.indexOf(prefix) < 0) { mapillary = prefix + mapillary; } AddImages([{url: mapillary, key: undefined}]); } }) } } private static loadWikidata(wikidataItem): string[] { // Load the wikidata item, then detect usage on 'commons' let allWikidataId = wikidataItem.split(";"); const imageURLS: string[] = []; for (let wikidataId of allWikidataId) { // @ts-ignore if (wikidataId.startsWith("Q")) { wikidataId = wikidataId.substr(1); } Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { imageURLS.push(wd.image); Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { for (const image of images.images) { // @ts-ignore if (image.startsWith("File:")) { imageURLS.push(image); } } }) }) } return imageURLS; } private static LoadCommons(commonsData: string): string[] { const imageUrls = []; const allCommons: string[] = commonsData.split(";"); for (const commons of allCommons) { // @ts-ignore if (commons.startsWith("Category:")) { Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => { for (const image of images.images) { // @ts-ignore if (image.startsWith("File:")) { imageUrls.push(image); } } }) } else { // @ts-ignore if (commons.startsWith("File:")) { imageUrls.push(commons); } } } return imageUrls; } private static LoadImages(tags: any, imagePrefix: string, loadAdditional: boolean): { key: string, url: string }[] { const imageTag = tags[imagePrefix]; const images: { key: string, url: string }[] = []; if (imageTag !== undefined) { const bareImages = imageTag.split(";"); for (const bareImage of bareImages) { images.push({key: imagePrefix, url: bareImage}) } } for (const key in tags) { if (key.startsWith(imagePrefix + ":")) { const url = tags[key] images.push({key: key, url: url}) } } return images; } }