forked from MapComplete/MapComplete
		
	Styling tweak
Add mapillary link to nearby_images Fix licenses Add missing assets First version of nearby-images
This commit is contained in:
		
							parent
							
								
									a4f2fa63a5
								
							
						
					
					
						commit
						7559f9259b
					
				
					 52 changed files with 674 additions and 207 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								Docs/Misc/TreesWithWikidata.gif
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Docs/Misc/TreesWithWikidata.gif
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 MiB | 
|  | @ -167,6 +167,7 @@ export default class DetermineLayout { | |||
|         const raw = json; | ||||
| 
 | ||||
|         json = new FixImages(DetermineLayout._knownImages).convertStrict(json, "While fixing the images") | ||||
|         json.enableNoteImports = json.enableNoteImports ?? false; | ||||
|         json = new PrepareTheme(converState).convertStrict(json, "While preparing a dynamic theme") | ||||
|         console.log("The layoutconfig is ", json) | ||||
|          | ||||
|  |  | |||
|  | @ -19,9 +19,19 @@ export default class AllImageProviders { | |||
|         new GenericImageProvider( | ||||
|             [].concat(...Imgur.defaultValuePrefix, ...WikimediaImageProvider.commonsPrefixes, ...Mapillary.valuePrefixes) | ||||
|         ) | ||||
| 
 | ||||
|     ] | ||||
| 
 | ||||
|     private static providersByName= { | ||||
|         "imgur": Imgur.singleton, | ||||
| "mapillary":        Mapillary.singleton, | ||||
|      "wikidata":  WikidataImageProvider.singleton, | ||||
|        "wikimedia": WikimediaImageProvider.singleton | ||||
|     } | ||||
|      | ||||
|     public static byName(name: string){ | ||||
|         return AllImageProviders.providersByName[name.toLowerCase()] | ||||
|     } | ||||
| 
 | ||||
|     public static defaultKeys = [].concat(AllImageProviders.ImageAttributionSource.map(provider => provider.defaultKeyPrefixes)) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import Svg from "../../Svg"; | |||
| import {Utils} from "../../Utils"; | ||||
| import {LicenseInfo} from "./LicenseInfo"; | ||||
| import Constants from "../../Models/Constants"; | ||||
| import * as Console from "console"; | ||||
| 
 | ||||
| export class Mapillary extends ImageProvider { | ||||
| 
 | ||||
|  | @ -12,11 +13,49 @@ export class Mapillary extends ImageProvider { | |||
|     public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com", "https://mapillary.com", "http://www.mapillary.com", "https://www.mapillary.com"] | ||||
|     defaultKeyPrefixes = ["mapillary", "image"] | ||||
| 
 | ||||
|     /** | ||||
|      * Indicates that this is the same URL | ||||
|      * Ignores 'stp' parameter | ||||
|      *  | ||||
|      * const a = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s1024x768&ccb=10-5&oh=00_AT-ZGTXHzihoaQYBILmEiAEKR64z_IWiTlcAYq_D7Ka0-Q&oe=6278C456&_nc_sid=122ab1" | ||||
|      * const b = "https://scontent-bru2-1.xx.fbcdn.net/m1/v/t6/An8xm5SGLt20ETziNqzhhBd8b8S5GHLiIu8N6BbyqHFohFAQoaJJPG8i5yQiSwjYmEqXSfVeoCmpiyBJICEkQK98JOB21kkJoBS8VdhYa-Ty93lBnznQyesJBtKcb32foGut2Hgt10hEMWJbE3dDgA?stp=s256x192&ccb=10-5&oh=00_AT9BZ1Rpc9zbY_uNu92A_4gj1joiy1b6VtgtLIu_7wh9Bg&oe=6278C456&_nc_sid=122ab1" | ||||
|      * Mapillary.sameUrl(a, b) => true | ||||
|      */ | ||||
|     static sameUrl(a: string, b: string): boolean { | ||||
|         if (a === b) { | ||||
|             return true | ||||
|         } | ||||
|         try { | ||||
| console.log("COmparing",a,b) | ||||
|             const aUrl = new URL(a) | ||||
|             const bUrl = new URL(b) | ||||
|             if (aUrl.host !== bUrl.host || aUrl.pathname !== bUrl.pathname) { | ||||
|                 return false; | ||||
|             } | ||||
|             let allSame = true; | ||||
|             aUrl.searchParams.forEach((value, key) => { | ||||
|                 if (key === "stp") { | ||||
|                     // This is the key indicating the image size on mapillary; we ignore it
 | ||||
|                     return | ||||
|                 } | ||||
|                 if (value !== bUrl.searchParams.get(key)) { | ||||
|                     allSame = false | ||||
|                     return | ||||
|                 } | ||||
|             }) | ||||
|             return allSame; | ||||
| 
 | ||||
|         } catch (e) { | ||||
|             Console.debug("Could not compare ", a, "and", b, "due to", e) | ||||
|         } | ||||
|         return false; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the correct key for API v4.0 | ||||
|      */ | ||||
|     private static ExtractKeyFromURL(value: string): number { | ||||
| 
 | ||||
|         let key: string; | ||||
| 
 | ||||
|         const newApiFormat = value.match(/https?:\/\/www.mapillary.com\/app\/\?pKey=([0-9]*)/) | ||||
|  | @ -24,6 +63,8 @@ export class Mapillary extends ImageProvider { | |||
|             key = newApiFormat[1] | ||||
|         } else if (value.startsWith(Mapillary.valuePrefix)) { | ||||
|             key = value.substring(0, value.lastIndexOf("?")).substring(value.lastIndexOf("/") + 1) | ||||
|         } else if (value.match("[0-9]*")) { | ||||
|             key = value; | ||||
|         } | ||||
| 
 | ||||
|         const keyAsNumber = Number(key) | ||||
|  |  | |||
|  | @ -9,7 +9,8 @@ export default class ChangeTagAction extends OsmChangeAction { | |||
|     private readonly _currentTags: any; | ||||
|     private readonly _meta: { theme: string, changeType: string }; | ||||
| 
 | ||||
|     constructor(elementId: string, tagsFilter: TagsFilter, currentTags: any, meta: { | ||||
|     constructor(elementId: string,  | ||||
|                 tagsFilter: TagsFilter, currentTags: any, meta: { | ||||
|         theme: string, | ||||
|         changeType: "answer" | "soft-delete" | "add-image" | string | ||||
|     }) { | ||||
|  |  | |||
|  | @ -53,6 +53,10 @@ export default class UserRelatedState extends ElementsState { | |||
|             osmConfiguration: <'osm' | 'osm-test'>this.featureSwitchApiURL.data, | ||||
|             attemptLogin: options?.attemptLogin | ||||
|         }) | ||||
|         const translationMode  = this.osmConnection.GetPreference("translation-mode").map(str => str === undefined ? undefined : str === "true", [], b => b === undefined ? undefined : b+"") | ||||
|              | ||||
|         translationMode.syncWith(Locale.showLinkToWeblate) | ||||
|          | ||||
|         this.isTranslator = this.osmConnection.userDetails.map(ud => { | ||||
|             if(!ud.loggedIn){ | ||||
|                 return false; | ||||
|  | @ -60,6 +64,7 @@ export default class UserRelatedState extends ElementsState { | |||
|             const name= ud.name.toLowerCase().replace(/\s+/g, '') | ||||
|             return translators.contributors.some(c => c.contributor.toLowerCase().replace(/\s+/g, '') === name) | ||||
|         }) | ||||
|          | ||||
|         this.isTranslator.addCallbackAndRunD(ud => { | ||||
|             if(ud){ | ||||
|                 Locale.showLinkToWeblate.setData(true) | ||||
|  |  | |||
|  | @ -163,6 +163,11 @@ export default class CreateNoteImportLayer extends Conversion<LayerConfigJson, L | |||
|                 { | ||||
|                     "id": "add_image", | ||||
|                     "render": "{add_image_to_note()}" | ||||
|                 }, | ||||
|                 { | ||||
|                     "id":"nearby_images", | ||||
|                     render: tr(t.nearbyImagesIntro) | ||||
|                          | ||||
|                 } | ||||
|             ], | ||||
|             "mapRendering": [ | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import Translations from "../../../UI/i18n/Translations"; | |||
| import {Translation} from "../../../UI/i18n/Translation"; | ||||
| import * as tagrenderingconfigmeta from "../../../assets/tagrenderingconfigmeta.json" | ||||
| import {AddContextToTranslations} from "./AddContextToTranslations"; | ||||
| import spec = Mocha.reporters.spec; | ||||
| 
 | ||||
| class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> { | ||||
|     private readonly _state: DesugaringContext; | ||||
|  | @ -302,11 +303,6 @@ export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[ | |||
| 
 | ||||
| /** | ||||
|  * Converts a 'special' translation into a regular translation which uses parameters | ||||
|  * E.g. | ||||
|  *  | ||||
|  * const tr = <TagRenderingJson> { | ||||
|  *     "special":  | ||||
|  * } | ||||
|  */ | ||||
| export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | ||||
|     constructor() { | ||||
|  | @ -330,6 +326,11 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|      * const r = RewriteSpecial.convertIfNeeded(spec, [], "test") | ||||
|      * r // => {"en": "{image_upload(,Add a picture to this object)}", "nl": "{image_upload(,Voeg een afbeelding toe)}" }
 | ||||
|      *  | ||||
|      * // should handle special case with a prefix and postfix
 | ||||
|      * const spec = {"special": {"type":"image_upload" }, before: {"en": "PREFIX "}, after: {"en": " POSTFIX", nl: " Achtervoegsel"} } | ||||
|      * const r = RewriteSpecial.convertIfNeeded(spec, [], "test") | ||||
|      * r // => {"en": "PREFIX {image_upload(,)} POSTFIX", "nl": "PREFIX {image_upload(,)} Achtervoegsel" }
 | ||||
|      *  | ||||
|      * // should warn for unexpected keys
 | ||||
|      * const errors = [] | ||||
|      * RewriteSpecial.convertIfNeeded({"special": {type: "image_carousel"}, "en": "xyz"}, errors, "test") // =>  {'*': "{image_carousel()}"}
 | ||||
|  | @ -352,7 +353,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|             return input | ||||
|         } | ||||
| 
 | ||||
|         for (const wrongKey of Object.keys(input).filter(k => k !== "special")) { | ||||
|         for (const wrongKey of Object.keys(input).filter(k => k !== "special" && k !== "before" && k !== "after")) { | ||||
|             errors.push(`At ${context}: Unexpected key in a special block: ${wrongKey}`) | ||||
|         } | ||||
| 
 | ||||
|  | @ -400,6 +401,16 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|             }   | ||||
|         } | ||||
|          | ||||
|         const before = Translations.T(input.before) | ||||
|         const after = Translations.T(input.after) | ||||
| 
 | ||||
|         for (const ln of Object.keys(before?.translations??{})) { | ||||
|             foundLanguages.add(ln) | ||||
|         } | ||||
|         for (const ln of Object.keys(after?.translations??{})) { | ||||
|             foundLanguages.add(ln) | ||||
|         } | ||||
| 
 | ||||
|         if(foundLanguages.size === 0){ | ||||
|            const args=   argNamesList.map(nm => special[nm] ?? "").join(",") | ||||
|             return {'*': `{${type}(${args})}` | ||||
|  | @ -419,7 +430,9 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|                     args.push(v) | ||||
|                 } | ||||
|             } | ||||
|             result[ln] = `{${type}(${args.join(",")})}` | ||||
|             const beforeText = before?.textFor(ln) ?? "" | ||||
|             const afterText = after?.textFor(ln) ?? "" | ||||
|             result[ln] = `${beforeText}{${type}(${args.join(",")})}${afterText}` | ||||
|         } | ||||
|         return result | ||||
|     } | ||||
|  | @ -437,6 +450,13 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|      * const result = new RewriteSpecial().convert(tr,"test").result | ||||
|      * const expected = {render:  {'*': "{image_carousel(image)}"}, mappings: [{if: "other_image_key", then:  {'*': "{image_carousel(other_image_key)}"}} ]} | ||||
|      * result // => expected
 | ||||
|      *  | ||||
|      * const tr = { | ||||
|      *     render: {special: {type: "image_carousel", image_key: "image"}, before: {en: "Some introduction"} }, | ||||
|      * } | ||||
|      * const result = new RewriteSpecial().convert(tr,"test").result | ||||
|      * const expected = {render:  {'en': "Some introduction{image_carousel(image)}"}} | ||||
|      * result // => expected
 | ||||
|      */ | ||||
|     convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { | ||||
|         const errors = [] | ||||
|  |  | |||
|  | @ -170,7 +170,13 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> { | |||
|         super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"], "AddImportLayers"); | ||||
|     } | ||||
| 
 | ||||
|     convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[] } { | ||||
|     convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[], warnings?: string[] } { | ||||
|         if (!(json.enableNoteImports ?? true)) { | ||||
|             return { | ||||
|                 warnings: ["Not creating a note import layers for theme "+json.id+" as they are disabled"], | ||||
|                 result: json | ||||
|             }; | ||||
|         } | ||||
|         const errors = [] | ||||
| 
 | ||||
|         json = {...json} | ||||
|  | @ -178,39 +184,37 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> { | |||
|         json.layers = [...json.layers] | ||||
| 
 | ||||
| 
 | ||||
|         if (json.enableNoteImports ?? true) { | ||||
|             const creator = new CreateNoteImportLayer() | ||||
|             for (let i1 = 0; i1 < allLayers.length; i1++) { | ||||
|                 const layer = allLayers[i1]; | ||||
|                 if (Constants.priviliged_layers.indexOf(layer.id) >= 0) { | ||||
|                     // Priviliged layers are skipped
 | ||||
|                     continue | ||||
|                 } | ||||
|         const creator = new CreateNoteImportLayer() | ||||
|         for (let i1 = 0; i1 < allLayers.length; i1++) { | ||||
|             const layer = allLayers[i1]; | ||||
|             if (Constants.priviliged_layers.indexOf(layer.id) >= 0) { | ||||
|                 // Priviliged layers are skipped
 | ||||
|                 continue | ||||
|             } | ||||
| 
 | ||||
|                 if (layer.source["geoJson"] !== undefined) { | ||||
|                     // Layer which don't get their data from OSM are skipped
 | ||||
|                     continue | ||||
|                 } | ||||
|             if (layer.source["geoJson"] !== undefined) { | ||||
|                 // Layer which don't get their data from OSM are skipped
 | ||||
|                 continue | ||||
|             } | ||||
| 
 | ||||
|                 if (layer.title === undefined || layer.name === undefined) { | ||||
|                     // Anonymous layers and layers without popup are skipped
 | ||||
|                     continue | ||||
|                 } | ||||
|             if (layer.title === undefined || layer.name === undefined) { | ||||
|                 // Anonymous layers and layers without popup are skipped
 | ||||
|                 continue | ||||
|             } | ||||
| 
 | ||||
|                 if (layer.presets === undefined || layer.presets.length == 0) { | ||||
|                     // A preset is needed to be able to generate a new point
 | ||||
|                     continue; | ||||
|                 } | ||||
|             if (layer.presets === undefined || layer.presets.length == 0) { | ||||
|                 // A preset is needed to be able to generate a new point
 | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|                 try { | ||||
|             try { | ||||
| 
 | ||||
|                     const importLayerResult = creator.convert(layer, context + ".(noteimportlayer)[" + i1 + "]") | ||||
|                     if (importLayerResult.result !== undefined) { | ||||
|                         json.layers.push(importLayerResult.result) | ||||
|                     } | ||||
|                 } catch (e) { | ||||
|                     errors.push("Could not generate an import-layer for " + layer.id + " due to " + e) | ||||
|                 const importLayerResult = creator.convert(layer, context + ".(noteimportlayer)[" + i1 + "]") | ||||
|                 if (importLayerResult.result !== undefined) { | ||||
|                     json.layers.push(importLayerResult.result) | ||||
|                 } | ||||
|             } catch (e) { | ||||
|                 errors.push("Could not generate an import-layer for " + layer.id + " due to " + e) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -255,6 +259,7 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> { | |||
|                 if (!translation.hasOwnProperty(key)) { | ||||
|                     continue | ||||
|                 } | ||||
|                  | ||||
|                 const template = translation[key] | ||||
|                 const parts = SubstitutedTranslation.ExtractSpecialComponents(template) | ||||
|                 const hasMiniMap = parts.filter(part => part.special !== undefined).some(special => special.special.func.funcName === "minimap") | ||||
|  |  | |||
|  | @ -308,6 +308,8 @@ export interface LayoutConfigJson { | |||
|     /** | ||||
|      * If true, notes will be loaded and parsed. If a note is an import (as created by the import_helper.html-tool from mapcomplete), | ||||
|      * these notes will be shown if a relevant layer is present. | ||||
|      *  | ||||
|      * Default is true for official layers and false for unofficial (sideloaded) layers | ||||
|      */ | ||||
|     enableNoteImports?: true | boolean; | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ import ContributorCount from "../../Logic/ContributorCount"; | |||
| import Img from "../Base/Img"; | ||||
| import {TypedTranslation} from "../i18n/Translation"; | ||||
| import TranslatorsPanel from "./TranslatorsPanel"; | ||||
| import {MapillaryLink} from "./MapillaryLink"; | ||||
| 
 | ||||
| export class OpenIdEditor extends VariableUiElement { | ||||
|     constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string, objectId?: string) { | ||||
|  | @ -44,19 +45,6 @@ export class OpenIdEditor extends VariableUiElement { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class OpenMapillary extends VariableUiElement { | ||||
|     constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string) { | ||||
|         const t = Translations.t.general.attribution | ||||
|         super(state.locationControl.map(location => { | ||||
|             const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` | ||||
|             return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), t.openMapillary, { | ||||
|                 url: mapillaryLink, | ||||
|                 newTab: true | ||||
|             }) | ||||
|         })) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class OpenJosm extends Combine { | ||||
| 
 | ||||
|     constructor(state: { osmConnection: OsmConnection, currentBounds: UIEventSource<BBox>, }, iconStyle?: string) { | ||||
|  | @ -132,7 +120,7 @@ export default class CopyrightPanel extends Combine { | |||
|                 newTab: true | ||||
|             }), | ||||
|             new OpenIdEditor(state, iconStyle), | ||||
|             new OpenMapillary(state, iconStyle), | ||||
|             new MapillaryLink(state, iconStyle), | ||||
|             new OpenJosm(state, iconStyle), | ||||
|             new TranslatorsPanel(state, iconStyle) | ||||
|            | ||||
|  |  | |||
							
								
								
									
										24
									
								
								UI/BigComponents/MapillaryLink.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								UI/BigComponents/MapillaryLink.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Svg from "../../Svg"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Title from "../Base/Title"; | ||||
| 
 | ||||
| export class MapillaryLink extends VariableUiElement { | ||||
|     constructor(state: { locationControl: UIEventSource<Loc> }, iconStyle?: string) { | ||||
|         const t = Translations.t.general.attribution | ||||
|         super(state.locationControl.map(location => { | ||||
|             const mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}` | ||||
|             return new SubtleButton(Svg.mapillary_black_ui().SetStyle(iconStyle), | ||||
|                 new Combine([ | ||||
|                     new Title(t.openMapillary,3), | ||||
|                     t.mapillaryHelp]), { | ||||
|                     url: mapillaryLink, | ||||
|                     newTab: true | ||||
|                 }).SetClass("flex flex-col link-no-underline") | ||||
|         })) | ||||
|     } | ||||
| } | ||||
|  | @ -1,22 +1,31 @@ | |||
| import Combine from "../Base/Combine"; | ||||
| import Attribution from "./Attribution"; | ||||
| import Img from "../Base/Img"; | ||||
| import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; | ||||
| import ImageProvider, {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; | ||||
| 
 | ||||
| 
 | ||||
| export class AttributedImage extends Combine { | ||||
| 
 | ||||
|     constructor(imageInfo: ProvidedImage) { | ||||
|      constructor(imageInfo: { | ||||
|                     url: string, | ||||
|                     provider?: ImageProvider, | ||||
|                     date?: Date | ||||
|                 } | ||||
|     ) { | ||||
|         let img: BaseUIElement; | ||||
|         let attr: BaseUIElement | ||||
|         img = new Img(imageInfo.url, false, { | ||||
|             fallbackImage: imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined | ||||
|         }); | ||||
|         attr = new Attribution(imageInfo.provider.GetAttributionFor(imageInfo.url), | ||||
|             imageInfo.provider.SourceIcon(), | ||||
|         ) | ||||
|          | ||||
|         let attr: BaseUIElement = undefined | ||||
|         if(imageInfo.provider !== undefined){ | ||||
|             attr = new Attribution(imageInfo.provider?.GetAttributionFor(imageInfo.url), | ||||
|                 imageInfo.provider?.SourceIcon(), | ||||
|                 imageInfo.date | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         super([img, attr]); | ||||
|  |  | |||
|  | @ -4,10 +4,11 @@ import BaseUIElement from "../BaseUIElement"; | |||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {LicenseInfo} from "../../Logic/ImageProviders/LicenseInfo"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| 
 | ||||
| export default class Attribution extends VariableUiElement { | ||||
| 
 | ||||
|     constructor(license: UIEventSource<LicenseInfo>, icon: BaseUIElement) { | ||||
|     constructor(license: UIEventSource<LicenseInfo>, icon: BaseUIElement, date?: Date) { | ||||
|         if (license === undefined) { | ||||
|             throw "No license source given in the attribution element" | ||||
|         } | ||||
|  | @ -23,7 +24,8 @@ export default class Attribution extends VariableUiElement { | |||
|                     new Combine([ | ||||
|                         Translations.W(license?.title).SetClass("block"), | ||||
|                         Translations.W(license?.artist ?? "").SetClass("block font-bold"), | ||||
|                         Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? "")) | ||||
|                         Translations.W((license?.license ?? "") === "" ? "CC0" : (license?.license ?? "")), | ||||
|                         date === undefined ? undefined : new FixedUiElement(date.toLocaleDateString()) | ||||
|                     ]).SetClass("flex flex-col") | ||||
|                 ]).SetClass("flex flex-row bg-black text-white text-sm absolute bottom-0 left-0 p-0.5 pl-5 pr-3 rounded-lg no-images") | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										133
									
								
								UI/Popup/NearbyImages.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								UI/Popup/NearbyImages.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,133 @@ | |||
| import Combine from "../Base/Combine"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {SlideShow} from "../Image/SlideShow"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import Loading from "../Base/Loading"; | ||||
| import {AttributedImage} from "../Image/AttributedImage"; | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"; | ||||
| import Svg from "../../Svg"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import {InputElement} from "../Input/InputElement"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {Mapillary} from "../../Logic/ImageProviders/Mapillary"; | ||||
| 
 | ||||
| export interface P4CPicture { | ||||
|     pictureUrl: string, | ||||
|     date: number, | ||||
|     coordinates: { lat: number, lng: number }, | ||||
|     provider: "Mapillary" | string, | ||||
|     author, | ||||
|     license, | ||||
|     detailsUrl: string, | ||||
|     direction, | ||||
|     osmTags: object /*To copy straight into OSM!*/ | ||||
|     , | ||||
|     thumbUrl: string, | ||||
|     details: { | ||||
|         isSpherical: boolean, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| interface NearbyImageOptions { | ||||
|     lon: number, | ||||
|     lat: number, | ||||
|     radius: number, | ||||
|     maxDaysOld?: 1095, | ||||
|     blacklist: UIEventSource<{url: string}[]> | ||||
| } | ||||
| 
 | ||||
| export default class NearbyImages extends VariableUiElement { | ||||
| 
 | ||||
|     constructor(options: NearbyImageOptions) { | ||||
|         const t = Translations.t.image.nearbyPictures | ||||
|         const P4C = require("../../vendor/P4C.min") | ||||
|         const picManager = new P4C.PicturesManager({}); | ||||
| 
 | ||||
|         const loadedPictures = UIEventSource.FromPromise<P4CPicture[]>( | ||||
|             picManager.startPicsRetrievalAround(new P4C.LatLng(options.lat, options.lon), options.radius, { | ||||
|                 mindate: new Date().getTime() - (options.maxDaysOld ?? 1095) * 24 * 60 * 60 * 1000 | ||||
|             }) | ||||
|         ).map(images => { | ||||
|             console.log("Images are" ,images, "blacklisted is", options.blacklist.data) | ||||
|             images?.sort((a, b) => b.date - a.date) | ||||
|             return images ?.filter(i => !(options.blacklist?.data?.some(blacklisted =>  | ||||
|                     Mapillary.sameUrl(i.pictureUrl, blacklisted.url))) | ||||
|                 && i.details.isSpherical === false); | ||||
|         }, [options.blacklist]) | ||||
| 
 | ||||
|         super(loadedPictures.map(images => { | ||||
|             if(images === undefined){ | ||||
|               return  new Loading(t.loading); | ||||
|             } | ||||
|             if(images.length === 0){ | ||||
|                 return t.nothingFound.SetClass("alert block") | ||||
|             } | ||||
|             return new SlideShow(loadedPictures.map(imgs => (imgs ?? []).slice(0, 25).map(i => this.prepareElement(i)))) | ||||
|         })); | ||||
|                 | ||||
|     } | ||||
| 
 | ||||
|     protected prepareElement(info: P4CPicture): BaseUIElement { | ||||
|         const provider = AllImageProviders.byName(info.provider); | ||||
|         return new AttributedImage({url: info.pictureUrl, provider}) | ||||
|     } | ||||
| 
 | ||||
|     private asAttributedImage(info: P4CPicture): AttributedImage { | ||||
|         const provider = AllImageProviders.byName(info.provider); | ||||
|         return new AttributedImage({url: info.thumbUrl, provider, date: new Date(info.date)}) | ||||
|     } | ||||
|      | ||||
|     protected asToggle(info:P4CPicture): Toggle { | ||||
|         const imgNonSelected = this.asAttributedImage(info); | ||||
|         const imageSelected = this.asAttributedImage(info); | ||||
| 
 | ||||
|         const nonSelected = new Combine([imgNonSelected]).SetClass("relative block") | ||||
|         const hoveringCheckmark = | ||||
|             new Combine([Svg.confirm_svg().SetClass("block w-24 h-24 -ml-12 -mt-12")]).SetClass("absolute left-1/2 top-1/2 w-0") | ||||
|         const selected = new Combine([ | ||||
|             imageSelected, | ||||
|             hoveringCheckmark, | ||||
|         ]).SetClass("relative block") | ||||
| 
 | ||||
|         return new Toggle(selected, nonSelected).SetClass("").ToggleOnClick(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class SelectOneNearbyImage extends NearbyImages implements InputElement<P4CPicture> { | ||||
|     private readonly value: UIEventSource<P4CPicture>; | ||||
| 
 | ||||
|     constructor(options: NearbyImageOptions & {value?: UIEventSource<P4CPicture>}) { | ||||
|         super(options) | ||||
|         this.value = options.value ?? new UIEventSource<P4CPicture>(undefined); | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<P4CPicture> { | ||||
|         return this.value; | ||||
|     } | ||||
| 
 | ||||
|     IsValid(t: P4CPicture): boolean { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     protected prepareElement(info: P4CPicture): BaseUIElement { | ||||
|         const toggle = super.asToggle(info) | ||||
|         toggle.isEnabled.addCallback(enabled => { | ||||
|             if (enabled) { | ||||
|                 this.value.setData(info) | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         this.value.addCallback(inf => { | ||||
|             if(inf !== info){ | ||||
|                 toggle.isEnabled.setData(false) | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         return toggle | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -2,10 +2,11 @@ import {UIEventSource} from "../../Logic/UIEventSource"; | |||
| import Translations from "../i18n/Translations"; | ||||
| import {OsmConnection} from "../../Logic/Osm/OsmConnection"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| 
 | ||||
| export class SaveButton extends Toggle { | ||||
| 
 | ||||
|     constructor(value: UIEventSource<any>, osmConnection: OsmConnection) { | ||||
|     constructor(value: UIEventSource<any>, osmConnection: OsmConnection, textEnabled ?: BaseUIElement, textDisabled ?: BaseUIElement) { | ||||
|         if (value === undefined) { | ||||
|             throw "No event source for savebutton, something is wrong" | ||||
|         } | ||||
|  | @ -17,9 +18,9 @@ export class SaveButton extends Toggle { | |||
| 
 | ||||
|         const isSaveable = value.map(v => v !== false && (v ?? "") !== "") | ||||
| 
 | ||||
|         const text = Translations.t.general.save | ||||
|         const saveEnabled = text.Clone().SetClass(`btn`); | ||||
|         const saveDisabled = text.Clone().SetClass(`btn btn-disabled`); | ||||
|         const saveEnabled = (textEnabled ?? Translations.t.general.save.Clone()).SetClass(`btn`); | ||||
|         const saveDisabled = (textDisabled ?? Translations.t.general.save.Clone()).SetClass(`btn btn-disabled`); | ||||
|          | ||||
|         const save = new Toggle( | ||||
|             saveEnabled, | ||||
|             saveDisabled, | ||||
|  |  | |||
|  | @ -48,6 +48,13 @@ import {TextField} from "./Input/TextField"; | |||
| import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"; | ||||
| import {Translation} from "./i18n/Translation"; | ||||
| import {AllTagsPanel} from "./AllTagsPanel"; | ||||
| import NearbyImages, {P4CPicture, SelectOneNearbyImage} from "./Popup/NearbyImages"; | ||||
| import Lazy from "./Base/Lazy"; | ||||
| import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"; | ||||
| import {Tag} from "../Logic/Tags/Tag"; | ||||
| import {And} from "../Logic/Tags/And"; | ||||
| import {SaveButton} from "./Popup/SaveButton"; | ||||
| import {MapillaryLink} from "./BigComponents/MapillaryLink"; | ||||
| 
 | ||||
| export interface SpecialVisualization { | ||||
|     funcName: string, | ||||
|  | @ -141,6 +148,116 @@ class CloseNoteButton implements SpecialVisualization { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| class NearbyImageVis implements SpecialVisualization { | ||||
|     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ | ||||
| 
 | ||||
|         { | ||||
|             name: "mode", | ||||
|             defaultValue: "expandable", | ||||
|             doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them." | ||||
|         }, | ||||
|         { | ||||
|             name: "mapillary", | ||||
|             defaultValue: "true", | ||||
|             doc: "If 'true', includes a link to mapillary on this location." | ||||
|         } | ||||
|     ] | ||||
|     docs = "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature"; | ||||
|     funcName = "nearby_images"; | ||||
| 
 | ||||
|     constr(state: FeaturePipelineState, tagSource: UIEventSource<any>, args: string[], guistate: DefaultGuiState): BaseUIElement { | ||||
|         const t = Translations.t.image.nearbyPictures | ||||
|         const mode: "open" | "expandable" | "collapsable" = <any>args[0] | ||||
|         const feature = state.allElements.ContainingFeatures.get(tagSource.data.id) | ||||
|         const [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||
|         const id: string = tagSource.data["id"] | ||||
|         const canBeEdited: boolean = !!(id?.match("(node|way|relation)/-?[0-9]+")) | ||||
|         const selectedImage = new UIEventSource<P4CPicture>(undefined); | ||||
| 
 | ||||
|         const nearby = new Lazy(() => { | ||||
|             const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource) | ||||
|             const options = { | ||||
|                 lon, lat, radius: 50, | ||||
|                 value: selectedImage, | ||||
|                 blacklist: alreadyInTheImage | ||||
|             }; | ||||
|             const slideshow = canBeEdited ? new SelectOneNearbyImage(options) : new NearbyImages(options); | ||||
|             return new Combine([slideshow, new MapillaryLinkVis().constr(state, tagSource, [])]) | ||||
|         }); | ||||
| 
 | ||||
|         let withEdit: BaseUIElement = nearby; | ||||
| 
 | ||||
|         if (canBeEdited) { | ||||
|             const confirmText: BaseUIElement = new SubstitutedTranslation(t.confirm, tagSource, state) | ||||
| 
 | ||||
|             const onSave = async () => { | ||||
|                 console.log("Selected a picture...", selectedImage.data) | ||||
|                 const osmTags = selectedImage.data.osmTags | ||||
|                 const tags: Tag[] = [] | ||||
|                 for (const key in osmTags) { | ||||
|                     tags.push(new Tag(key, osmTags[key])) | ||||
|                 } | ||||
|                 await state?.changes?.applyAction( | ||||
|                     new ChangeTagAction( | ||||
|                         id, | ||||
|                         new And(tags), | ||||
|                         tagSource, | ||||
|                         { | ||||
|                             theme: state?.layoutToUse.id, | ||||
|                             changeType: "link-image" | ||||
|                         } | ||||
|                     ) | ||||
|                 ) | ||||
|             }; | ||||
| 
 | ||||
|             const saveButton = new SaveButton(selectedImage, state.osmConnection, confirmText, t.noImageSelected) | ||||
|                 .onClick(onSave) | ||||
| 
 | ||||
|             withEdit = new Combine([ | ||||
|                 t.hasMatchingPicture, | ||||
|                 nearby, | ||||
|                 saveButton | ||||
|                     .SetClass("flex justify-end") | ||||
|             ]).SetClass("flex flex-col") | ||||
|         } | ||||
| 
 | ||||
|         if (mode === 'open') { | ||||
|             return withEdit | ||||
|         } | ||||
|         const toggleState = new UIEventSource<boolean>(mode === 'collapsable') | ||||
|         return new Toggle( | ||||
|             new Combine([new Title(t.title), withEdit]), | ||||
|             new Title(t.browseNearby).onClick(() => toggleState.setData(true)), | ||||
|             toggleState | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class MapillaryLinkVis implements SpecialVisualization { | ||||
|     funcName = "mapillary_link" | ||||
|     docs = "Adds a button to open mapillary on the specified location" | ||||
|     args = [{ | ||||
|         name: "zoom", | ||||
|         doc: "The startzoom of mapillary", | ||||
|         defaultValue: "18" | ||||
|     }]; | ||||
| 
 | ||||
|     public constr(state, tagsSource, args) { | ||||
|         const feat = state.allElements.ContainingFeatures.get(tagsSource.data.id); | ||||
|         const [lon, lat] = GeoOperations.centerpointCoordinates(feat); | ||||
|         let zoom = Number(args[0]) | ||||
|         if (isNaN(zoom)) { | ||||
|             zoom = 18 | ||||
|         } | ||||
|         return new MapillaryLink({ | ||||
|             locationControl: new UIEventSource<Loc>({ | ||||
|                 lat, lon, zoom | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default class SpecialVisualizations { | ||||
| 
 | ||||
|     public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.init() | ||||
|  | @ -309,7 +426,7 @@ export default class SpecialVisualizations { | |||
|                     example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`", | ||||
|                     constr: (state, tagSource, args, _) => { | ||||
| 
 | ||||
|                         if(state === undefined){ | ||||
|                         if (state === undefined) { | ||||
|                             return undefined | ||||
|                         } | ||||
|                         const keys = [...args] | ||||
|  | @ -940,7 +1057,9 @@ export default class SpecialVisualizations { | |||
|                             } | ||||
|                             return new SubstitutedTranslation(title, tagsSource, state) | ||||
|                         })) | ||||
|                 } | ||||
|                 }, | ||||
|                 new NearbyImageVis(), | ||||
|                 new MapillaryLinkVis() | ||||
|             ] | ||||
| 
 | ||||
|         specialVisualizations.push(new AutoApplyButton(specialVisualizations)) | ||||
|  |  | |||
|  | @ -59,6 +59,18 @@ | |||
|       "id": "comment", | ||||
|       "render": "{add_note_comment()}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "nearby-images", | ||||
|       "render": { | ||||
|         "before": { | ||||
|           "en": "<h3>Nearby images</h3>The pictures below are nearby geotagged images and might be helpful to handle this note." | ||||
|         }, | ||||
|         "special": { | ||||
|           "type": "nearby_images", | ||||
|           "mode": "open" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "report-contributor", | ||||
|       "render": { | ||||
|  |  | |||
|  | @ -1,89 +0,0 @@ | |||
| { | ||||
|   "id": "note_import", | ||||
|   "name": { | ||||
|     "en": "Possible bookcases", | ||||
|     "nl": "Mogelijke publieke boekenkastjes", | ||||
|     "de": "Mögliche Bücherschränke" | ||||
|   }, | ||||
|   "description": "Template for note note imports.", | ||||
|   "source": { | ||||
|     "osmTags": { | ||||
|       "and": [ | ||||
|         "id~*" | ||||
|       ] | ||||
|     }, | ||||
|     "geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?closed=0&bbox={x_min},{y_min},{x_max},{y_max}", | ||||
|     "geoJsonZoomLevel": 12, | ||||
|     "maxCacheAge": 0 | ||||
|   }, | ||||
|   "minzoom": 10, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Possible feature", | ||||
|       "nl": "Mogelijk object", | ||||
|       "de": "Mögliches Objekt" | ||||
|     } | ||||
|   }, | ||||
|   "calculatedTags": [ | ||||
|     "_first_comment:=feat.get('comments')[0].text.toLowerCase()", | ||||
|     "_trigger_index:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); const matchesMapCompleteURL = lines.map(l => l.match(\".*https://mapcomplete.osm.be/\\([a-zA-Z_-]+\\)\\(.html\\).*#import\")); const matchedIndexes = matchesMapCompleteURL.map((doesMatch, i) => [doesMatch !== null, i]).filter(v => v[0]).map(v => v[1]); return matchedIndexes[0] })()", | ||||
|     "_intro:=(() => {const lines = feat.properties['_first_comment'].split('\\n'); lines.splice(feat.get('_trigger_index')-1, lines.length); return lines.map(l => l == '' ? '<br/>' : l).join('');})()", | ||||
|     "_tags:=(() => {let lines = feat.properties['_first_comment'].split('\\n').map(l => l.trim()); lines.splice(0, feat.get('_trigger_index') + 1); lines = lines.filter(l => l != ''); return lines.join(';');})()" | ||||
|   ], | ||||
|   "isShown": { | ||||
|     "render": "yes", | ||||
|     "mappings": [ | ||||
|       { | ||||
|         "if": "_trigger_index=", | ||||
|         "then": "no" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "titleIcons": [ | ||||
|     { | ||||
|       "render": "<a href='https://openstreetmap.org/note/{id}' target='_blank'><img src='./assets/svg/osm-logo-us.svg'></a>" | ||||
|     } | ||||
|   ], | ||||
|   "tagRenderings": [ | ||||
|     { | ||||
|       "id": "conversation", | ||||
|       "render": "{visualize_note_comments(comments,1)}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "Intro", | ||||
|       "render": "{_intro}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "import", | ||||
|       "render": "{import_button(public_bookcase, _tags, There might be a public bookcase here,./assets/svg/addSmall.svg,,,id)}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "close_note_", | ||||
|       "render": "{close_note(Does not exist<br/>, ./assets/svg/close.svg, id, This feature does not exist)}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "close_note_mapped", | ||||
|       "render": "{close_note(Already mapped, ./assets/svg/checkmark.svg, id, Already mapped)}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "comment", | ||||
|       "render": "{add_note_comment()}" | ||||
|     }, | ||||
|     { | ||||
|       "id": "add_image", | ||||
|       "render": "{add_image_to_note()}" | ||||
|     } | ||||
|   ], | ||||
|   "mapRendering": [ | ||||
|     { | ||||
|       "location": [ | ||||
|         "point", | ||||
|         "centroid" | ||||
|       ], | ||||
|       "icon": { | ||||
|         "render": "teardrop:#3333cc" | ||||
|       }, | ||||
|       "iconSize": "40,40,bottom" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										37
									
								
								assets/svg/confirm.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								assets/svg/confirm.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    width="157.33984" | ||||
|    height="157.33984" | ||||
|    viewBox="0 0 157.33984 157.33984" | ||||
|    version="1.1" | ||||
|    id="svg9" | ||||
|    sodipodi:docname="confirm.svg" | ||||
|    inkscape:version="1.1.2 (1:1.1+202202050950+0a00cf5339)" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg"> | ||||
|   <defs | ||||
|      id="defs13" /> | ||||
|   <sodipodi:namedview | ||||
|      id="namedview11" | ||||
|      pagecolor="#505050" | ||||
|      bordercolor="#eeeeee" | ||||
|      borderopacity="1" | ||||
|      inkscape:pageshadow="0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pagecheckerboard="0" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="2.312" | ||||
|      inkscape:cx="-18.598616" | ||||
|      inkscape:cy="57.093426" | ||||
|      inkscape:current-layer="svg9" /> | ||||
|   <path | ||||
|      style="fill:#35d447;fill-opacity:1;fill-rule:nonzero;stroke:none" | ||||
|      d="m 157.33984,78.66796 c 0,43.44922 -35.21875,78.67188 -78.66796,78.67188 C 35.22266,157.33984 0,122.11718 0,78.66796 0,35.22265 35.22266,0 78.67188,0 c 43.44921,0 78.66796,35.22265 78.66796,78.66796 z m 0,0" | ||||
|      id="path4" /> | ||||
|   <path | ||||
|      style="fill:none;stroke:#ffffff;stroke-width:19.7495;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" | ||||
|      d="m 37.69921,75.49609 35.55078,39.5 47.39844,-63.19922" | ||||
|      id="path6" /> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 1.4 KiB | 
|  | @ -239,6 +239,14 @@ | |||
|     "authors": [], | ||||
|     "sources": [] | ||||
|   }, | ||||
|   { | ||||
|     "path": "confirm.svg", | ||||
|     "license": "CC0", | ||||
|     "authors": [ | ||||
|       "Pieter Vander Vennet" | ||||
|     ], | ||||
|     "sources": [] | ||||
|   }, | ||||
|   { | ||||
|     "path": "copyright.svg", | ||||
|     "license": "CC0", | ||||
|  | @ -1115,11 +1123,15 @@ | |||
|   }, | ||||
|   { | ||||
|     "path": "search_disable.svg", | ||||
|     "license": "CC0", | ||||
|     "license": "MIT", | ||||
|     "authors": [ | ||||
|       "OOjs UI Team and other contributors", | ||||
|       "Pieter Vander Vennet" | ||||
|     ], | ||||
|     "sources": [] | ||||
|     "sources": [ | ||||
|       "https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg", | ||||
|       "https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/AUTHORS.txt" | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "path": "send_email.svg", | ||||
|  |  | |||
|  | @ -4,7 +4,10 @@ | |||
|     "id": "questions" | ||||
|   }, | ||||
|   "images": { | ||||
|     "render": "{image_carousel()}{image_upload()}" | ||||
|     "render": "{image_carousel()}{image_upload()}{nearby_images(expandable)}" | ||||
|   }, | ||||
|   "mapillary": { | ||||
|     "render": "{mapillary()}" | ||||
|   }, | ||||
|   "export_as_gpx": { | ||||
|     "render": "{export_as_gpx()}" | ||||
|  | @ -36,7 +39,25 @@ | |||
|       { | ||||
|         "if": "wikipedia~*", | ||||
|         "then": { | ||||
|           "*": "{wikipedia():max-height:25rem}" | ||||
|           "*": "{wikipedia():max-height:25rem}", | ||||
|           "ca": "No hi ha cap enllaça a Viquipèdia encara", | ||||
|           "da": "Der er endnu ikke linket til nogen Wikipedia-side", | ||||
|           "de": "Es wurde noch keine Wikipedia-Seite verlinkt", | ||||
|           "en": "No Wikipedia page has been linked yet", | ||||
|           "es": "Todavía no se ha enlazado una página de wikipedia", | ||||
|           "fil": "Wala pang kawing ng Wikipedia page", | ||||
|           "fr": "Pas encore de lien vers une page Wikipedia", | ||||
|           "hu": "Még nincs Wikipédia-oldal belinkelve", | ||||
|           "it": "Nessuna pagina Wikipedia è ancora stata collegata", | ||||
|           "ja": "ウィキペディアのページはまだリンクされていません", | ||||
|           "nb_NO": "Ingen Wikipedia-side lenket enda", | ||||
|           "nl": "Er werd nog geen Wikipedia-pagina gekoppeld", | ||||
|           "pl": "Link do strony Wikipedii nie został jeszcze określony", | ||||
|           "pt": "Ainda não foi vinculada nenhuma página da Wikipédia", | ||||
|           "ru": "Никакой страницы на Википедии не было прикреплено", | ||||
|           "sv": "Ingen Wikipedia-sida har länkats än", | ||||
|           "zh_Hans": "尚未有连接到的维基百科页面", | ||||
|           "zh_Hant": "還沒有連結到維基百科頁面" | ||||
|         }, | ||||
|         "hideInAnswer": true | ||||
|       }, | ||||
|  |  | |||
|  | @ -1,13 +1,19 @@ | |||
| { | ||||
|   "id": "mapcomplete-changes", | ||||
|   "title": { | ||||
|     "en": "Changes made with MapComplete" | ||||
|     "en": "Changes made with MapComplete", | ||||
|     "de": "Änderungen mit MapComplete", | ||||
|     "es": "Cambios hechos con MapComplete" | ||||
|   }, | ||||
|   "shortDescription": { | ||||
|     "en": "Shows changes made by MapComplete" | ||||
|     "en": "Shows changes made by MapComplete", | ||||
|     "de": "Zeigt Änderungen von MapComplete", | ||||
|     "es": "Muestra los cambios hechos por MapComplete" | ||||
|   }, | ||||
|   "description": { | ||||
|     "en": "This maps shows all the changes made with MapComplete" | ||||
|     "en": "This maps shows all the changes made with MapComplete", | ||||
|     "de": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden", | ||||
|     "es": "Este mapa muestra todos los cambios hechos con MapComplete" | ||||
|   }, | ||||
|   "maintainer": "", | ||||
|   "icon": "./assets/svg/logo.svg", | ||||
|  | @ -22,7 +28,9 @@ | |||
|     { | ||||
|       "id": "mapcomplete-changes", | ||||
|       "name": { | ||||
|         "en": "Changeset centers" | ||||
|         "en": "Changeset centers", | ||||
|         "de": "Schwerpunkte von Änderungssätzen", | ||||
|         "es": "Centros de conjuntos de cambios" | ||||
|       }, | ||||
|       "minzoom": 0, | ||||
|       "source": { | ||||
|  | @ -36,35 +44,45 @@ | |||
|       ], | ||||
|       "title": { | ||||
|         "render": { | ||||
|           "en": "Changeset for {theme}" | ||||
|           "en": "Changeset for {theme}", | ||||
|           "de": "Änderungen für {theme}", | ||||
|           "es": "Conjunto de cambios para {theme}" | ||||
|         } | ||||
|       }, | ||||
|       "description": { | ||||
|         "en": "Shows all MapComplete changes" | ||||
|         "en": "Shows all MapComplete changes", | ||||
|         "de": "Zeigt alle MapComplete Änderungen", | ||||
|         "es": "Muestra todos los cambios de MapComplete" | ||||
|       }, | ||||
|       "tagRenderings": [ | ||||
|         { | ||||
|           "id": "render_id", | ||||
|           "render": { | ||||
|             "en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>" | ||||
|             "en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>", | ||||
|             "de": "Änderung <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>", | ||||
|             "es": "Conjunto de cambios <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": "contributor", | ||||
|           "render": { | ||||
|             "en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>" | ||||
|             "en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>", | ||||
|             "de": "Änderung wurde von <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a> gemacht", | ||||
|             "es": "Cambio hecho por <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>" | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "id": "theme", | ||||
|           "render": { | ||||
|             "en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>" | ||||
|             "en": "Change with theme <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>", | ||||
|             "de": "Änderung mit Thema <a href='https://mapcomplete.osm.be/{theme}'>{theme}</a>" | ||||
|           }, | ||||
|           "mappings": [ | ||||
|             { | ||||
|               "if": "theme~http.*", | ||||
|               "then": { | ||||
|                 "en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>" | ||||
|                 "en": "Change with <b>unofficial</b> theme <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>", | ||||
|                 "de": "Änderung mit <b>inoffiziellem</b> Thema <a href='https://mapcomplete.osm.be/theme.html?userlayout={theme}'>{theme}</a>" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|  | @ -332,7 +350,8 @@ | |||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "Themename contains {search}" | ||||
|                 "en": "Themename contains {search}", | ||||
|                 "de": "Themenname enthält {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|  | @ -348,7 +367,9 @@ | |||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "Made by contributor {search}" | ||||
|                 "en": "Made by contributor {search}", | ||||
|                 "de": "Erstellt von {search}", | ||||
|                 "es": "Hecho por contributor/a {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|  | @ -364,7 +385,9 @@ | |||
|                 } | ||||
|               ], | ||||
|               "question": { | ||||
|                 "en": "<b>Not</b> made by contributor {search}" | ||||
|                 "en": "<b>Not</b> made by contributor {search}", | ||||
|                 "de": "<b>Nicht</b> erstellt von {search}", | ||||
|                 "es": "<b>No</b> hecho por contributor/a {search}" | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|  | @ -379,7 +402,9 @@ | |||
|           { | ||||
|             "id": "link_to_more", | ||||
|             "render": { | ||||
|               "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>" | ||||
|               "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>", | ||||
|               "de": "Weitere Statistiken finden Sie <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>", | ||||
|               "es": "Se pueden encontrar más estadísticas <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>aquí</a>" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|  |  | |||
|  | @ -773,6 +773,14 @@ video { | |||
|   left: 0px; | ||||
| } | ||||
| 
 | ||||
| .left-1\/2 { | ||||
|   left: 50%; | ||||
| } | ||||
| 
 | ||||
| .top-1\/2 { | ||||
|   top: 50%; | ||||
| } | ||||
| 
 | ||||
| .isolate { | ||||
|   isolation: isolate; | ||||
| } | ||||
|  | @ -932,6 +940,14 @@ video { | |||
|   margin-right: 0.75rem; | ||||
| } | ||||
| 
 | ||||
| .-ml-12 { | ||||
|   margin-left: -3rem; | ||||
| } | ||||
| 
 | ||||
| .-mt-12 { | ||||
|   margin-top: -3rem; | ||||
| } | ||||
| 
 | ||||
| .mb-0 { | ||||
|   margin-bottom: 0px; | ||||
| } | ||||
|  | @ -1436,6 +1452,11 @@ video { | |||
|   border-color: rgba(229, 231, 235, var(--tw-border-opacity)); | ||||
| } | ||||
| 
 | ||||
| .border-green-500 { | ||||
|   --tw-border-opacity: 1; | ||||
|   border-color: rgba(16, 185, 129, var(--tw-border-opacity)); | ||||
| } | ||||
| 
 | ||||
| .border-opacity-50 { | ||||
|   --tw-border-opacity: 0.5; | ||||
| } | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ | |||
|             "josmOpened": "JOSM is opened", | ||||
|             "mapContributionsBy": "The current visible data has edits made by {contributors}", | ||||
|             "mapContributionsByAndHidden": "The current visible data has edits made by {contributors} and {hiddenCount} more contributors", | ||||
|             "mapillaryHelp": "<b>Mapillary</b> is an online service which gathers street-level pictures and offers them under a free license. Contributors are allowed to use these pictures to improve OpenStreetMap", | ||||
|             "openIssueTracker": "File a bug", | ||||
|             "openMapillary": "Open Mapillary here", | ||||
|             "openOsmcha": "See latest edits made with {theme}", | ||||
|  | @ -275,6 +276,15 @@ | |||
|         "doDelete": "Remove image", | ||||
|         "dontDelete": "Cancel", | ||||
|         "isDeleted": "Deleted", | ||||
|         "nearbyPictures": { | ||||
|             "browseNearby": "Browse nearby images...", | ||||
|             "confirm": "The selected image shows {title()}", | ||||
|             "hasMatchingPicture": "Does a picture match the object? Select it below", | ||||
|             "loading": "Loading nearby images...", | ||||
|             "noImageSelected": "Select an image to link it to the object", | ||||
|             "nothingFound": "No nearby images found...", | ||||
|             "title": "Nearby pictures" | ||||
|         }, | ||||
|         "pleaseLogin": "Please log in to add a picture", | ||||
|         "respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.", | ||||
|         "toBig": "Your image is too large as it is {actual_size}. Please use images of at most {max_size}", | ||||
|  | @ -417,6 +427,7 @@ | |||
|         "importButton": "import_button({layerId}, _tags, I have found a {title} here - add it to the map,./assets/svg/addSmall.svg,,,id)", | ||||
|         "importHandled": "<div class='thanks'>This feature has been handled! Thanks for your effort</div>", | ||||
|         "layerName": "Possible {title}", | ||||
|         "nearbyImagesIntro": "<h3>Nearby pictures</h3>The following pictures are nearby geotagged pictures from various online services. They might help you to resolve this note.{nearby_images(open)}", | ||||
|         "notFound": "I could not find {title} - remove it", | ||||
|         "popupTitle": "There might be {title} here" | ||||
|     }, | ||||
|  |  | |||
|  | @ -3353,12 +3353,6 @@ | |||
|             "render": "Notiz" | ||||
|         } | ||||
|     }, | ||||
|     "note_import": { | ||||
|         "name": "Mögliche Bücherschränke", | ||||
|         "title": { | ||||
|             "render": "Mögliches Objekt" | ||||
|         } | ||||
|     }, | ||||
|     "observation_tower": { | ||||
|         "description": "Türme zur Aussicht auf die umgebende Landschaft", | ||||
|         "name": "Aussichtstürme", | ||||
|  |  | |||
|  | @ -4252,6 +4252,11 @@ | |||
|         }, | ||||
|         "name": "OpenStreetMap notes", | ||||
|         "tagRenderings": { | ||||
|             "nearby-images": { | ||||
|                 "render": { | ||||
|                     "before": "<h3>Nearby images</h3>The pictures below are nearby geotagged images and might be helpful to handle this note." | ||||
|                 } | ||||
|             }, | ||||
|             "report-contributor": { | ||||
|                 "render": "<a href='https://www.openstreetmap.org/reports/new?reportable_id={_first_user_id}&reportable_type=User' target='_blank' class='subtle'>Report {_first_user} as spam</a>" | ||||
|             }, | ||||
|  | @ -4268,12 +4273,6 @@ | |||
|             "render": "Note" | ||||
|         } | ||||
|     }, | ||||
|     "note_import": { | ||||
|         "name": "Possible bookcases", | ||||
|         "title": { | ||||
|             "render": "Possible feature" | ||||
|         } | ||||
|     }, | ||||
|     "observation_tower": { | ||||
|         "description": "Towers with a panoramic view", | ||||
|         "name": "Observation towers", | ||||
|  |  | |||
|  | @ -4122,12 +4122,6 @@ | |||
|             "render": "Note" | ||||
|         } | ||||
|     }, | ||||
|     "note_import": { | ||||
|         "name": "Mogelijke publieke boekenkastjes", | ||||
|         "title": { | ||||
|             "render": "Mogelijk object" | ||||
|         } | ||||
|     }, | ||||
|     "observation_tower": { | ||||
|         "description": "Torens om van het uitzicht te genieten", | ||||
|         "name": "Uitkijktorens", | ||||
|  |  | |||
|  | @ -333,6 +333,7 @@ | |||
|         "importButton": "import_button({layerId}, _tags, Ik heb hier een {title} gevonden - voeg deze toe aan de kaart...,./assets/svg/addSmall.svg,,,id)", | ||||
|         "importHandled": "<div class='thanks'>Dit punt is afgehandeld. Bedankt om mee te helpen!</div>", | ||||
|         "layerName": "Hier is misschien een {title}", | ||||
|         "nearbyImagesIntro": "<h3>Afbeeldingen in de buurt</h3>De volgende afbeeldingen zijn in de buurt gemaakt en kunnen mogelijks helpen. {nearby_images(open)}", | ||||
|         "notFound": "Ik kon hier g{title} vinden - verwijder deze van de kaart", | ||||
|         "popupTitle": "Is hier {title}?" | ||||
|     }, | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "No hi ha cap enllaça a Viquipèdia encara" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "No hi ha cap enllaça a Viquipèdia encara" | ||||
|                 } | ||||
|  |  | |||
|  | @ -94,6 +94,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Der er endnu ikke linket til nogen Wikipedia-side" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Der er endnu ikke linket til nogen Wikipedia-side" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Es wurde noch keine Wikipedia-Seite verlinkt" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Es wurde noch keine Wikipedia-Seite verlinkt" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "No Wikipedia page has been linked yet" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "No Wikipedia page has been linked yet" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Todavía no se ha enlazado una página de wikipedia" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Todavía no se ha enlazado una página de wikipedia" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Wala pang kawing ng Wikipedia page" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Wala pang kawing ng Wikipedia page" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Pas encore de lien vers une page Wikipedia" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Pas encore de lien vers une page Wikipedia" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Még nincs Wikipédia-oldal belinkelve" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Még nincs Wikipédia-oldal belinkelve" | ||||
|                 } | ||||
|  |  | |||
|  | @ -81,6 +81,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Nessuna pagina Wikipedia è ancora stata collegata" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Nessuna pagina Wikipedia è ancora stata collegata" | ||||
|                 } | ||||
|  |  | |||
|  | @ -98,6 +98,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "ウィキペディアのページはまだリンクされていません" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "ウィキペディアのページはまだリンクされていません" | ||||
|                 } | ||||
|  |  | |||
|  | @ -96,6 +96,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Ingen Wikipedia-side lenket enda" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Ingen Wikipedia-side lenket enda" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Er werd nog geen Wikipedia-pagina gekoppeld" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Er werd nog geen Wikipedia-pagina gekoppeld" | ||||
|                 } | ||||
|  |  | |||
|  | @ -98,6 +98,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Link do strony Wikipedii nie został jeszcze określony" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Link do strony Wikipedii nie został jeszcze określony" | ||||
|                 } | ||||
|  |  | |||
|  | @ -110,6 +110,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Ainda não foi vinculada nenhuma página da Wikipédia" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Ainda não foi vinculada nenhuma página da Wikipédia" | ||||
|                 } | ||||
|  |  | |||
|  | @ -77,6 +77,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Никакой страницы на Википедии не было прикреплено" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Никакой страницы на Википедии не было прикреплено" | ||||
|                 } | ||||
|  |  | |||
|  | @ -98,6 +98,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "Ingen Wikipedia-sida har länkats än" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "Ingen Wikipedia-sida har länkats än" | ||||
|                 } | ||||
|  |  | |||
|  | @ -55,6 +55,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "尚未有连接到的维基百科页面" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "尚未有连接到的维基百科页面" | ||||
|                 } | ||||
|  |  | |||
|  | @ -113,6 +113,9 @@ | |||
|         }, | ||||
|         "wikipedia": { | ||||
|             "mappings": { | ||||
|                 "1": { | ||||
|                     "then": "還沒有連結到維基百科頁面" | ||||
|                 }, | ||||
|                 "1": { | ||||
|                     "then": "還沒有連結到維基百科頁面" | ||||
|                 } | ||||
|  |  | |||
							
								
								
									
										28
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,17 +1,19 @@ | |||
| import {SelectOneNearbyImage} from "./UI/Popup/NearbyImages"; | ||||
| import Minimap from "./UI/Base/Minimap"; | ||||
| import MinimapImplementation from "./UI/Base/MinimapImplementation"; | ||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| import Loc from "./Models/Loc"; | ||||
| import {UIEventSource} from "./Logic/UIEventSource"; | ||||
| import Wikidata from "./Logic/Web/Wikidata"; | ||||
| import Combine from "./UI/Base/Combine"; | ||||
| import {FixedUiElement} from "./UI/Base/FixedUiElement"; | ||||
| 
 | ||||
| const result = UIEventSource.FromPromise( | ||||
|     Wikidata.searchAdvanced("WOlf", { | ||||
|         lang: "nl", | ||||
|         maxCount: 100, | ||||
|         instanceOf: 5 | ||||
| MinimapImplementation.initialize() | ||||
| const map = Minimap.createMiniMap({ | ||||
|     location: new UIEventSource<Loc>({ | ||||
|         lon: 3.22457, | ||||
|         lat: 51.20876, | ||||
|         zoom: 18 | ||||
|     }) | ||||
| ) | ||||
| result.addCallbackAndRunD(r => console.log(r)) | ||||
| new VariableUiElement(result.map(items =>new Combine( (items??[])?.map(i =>  | ||||
|     new FixedUiElement(JSON.stringify(i, null, "  ")).SetClass("p-4 block") | ||||
| ))  )).SetClass("flex flex-col").AttachTo("maindiv") | ||||
| }) | ||||
| map.AttachTo("extradiv") | ||||
| map.SetStyle("height: 500px") | ||||
| 
 | ||||
| new VariableUiElement(map.location.map( loc =>  new SelectOneNearbyImage( {...loc, radius: 50}))).AttachTo("maindiv") | ||||
|  | @ -9,7 +9,7 @@ import {exec} from "child_process"; | |||
|  */ | ||||
| function detectInCode(forbidden: string, reason: string) { | ||||
| 
 | ||||
|     const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets"] | ||||
|     const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets", "vendor"] | ||||
| 
 | ||||
|     exec("grep -n \"" + forbidden + "\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => { | ||||
|         if (error?.message?.startsWith("Command failed: grep")) { | ||||
|  |  | |||
							
								
								
									
										7
									
								
								vendor/P4C.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/P4C.min.js
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue