forked from MapComplete/MapComplete
		
	Optimize rendering
This commit is contained in:
		
							parent
							
								
									8babafaadb
								
							
						
					
					
						commit
						a721d3137a
					
				
					 21 changed files with 361 additions and 362 deletions
				
			
		|  | @ -23,8 +23,6 @@ import * as bike_shops from "../../assets/layers/bike_shop/bike_shop.json" | |||
| import * as maps from "../../assets/layers/maps/maps.json" | ||||
| import * as information_boards from "../../assets/layers/information_board/information_board.json" | ||||
| import {Utils} from "../../Utils"; | ||||
| import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload"; | ||||
| import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel"; | ||||
| import State from "../../State"; | ||||
| 
 | ||||
| export class FromJSON { | ||||
|  | @ -139,23 +137,15 @@ export class FromJSON { | |||
|          | ||||
|         if (typeof json === "string") { | ||||
|             switch (json) { | ||||
|                 case "picture": { | ||||
|                     return new ImageCarouselWithUploadConstructor() | ||||
|                 } | ||||
|                 case "pictures": { | ||||
|                     return new ImageCarouselWithUploadConstructor() | ||||
|                 } | ||||
|                 case "image": { | ||||
|                     return new ImageCarouselWithUploadConstructor() | ||||
|                     json = "{image_carousel()}{image_upload()}"; | ||||
|                     break; | ||||
|                 } | ||||
|                 case "images": { | ||||
|                    return new ImageCarouselWithUploadConstructor() | ||||
|                 } | ||||
|                 case "picturesNoUpload": { | ||||
|                     return new ImageCarouselConstructor() | ||||
|                     json = "{image_carousel()}{image_upload()}"; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             console.warn("Possible literal rendering:", json) | ||||
| 
 | ||||
|             return new TagRenderingOptions({ | ||||
|                 freeform: { | ||||
|  |  | |||
|  | @ -16,9 +16,9 @@ export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{ | |||
|         this._embedded = embedded; | ||||
|     } | ||||
| 
 | ||||
|     construct(dependencies): TagDependantUIElement { | ||||
|         return new OnlyShowIf(dependencies.tags,  | ||||
|             this._embedded.construct(dependencies), | ||||
|     construct(tags: UIEventSource<any>): TagDependantUIElement { | ||||
|         return new OnlyShowIf(tags,  | ||||
|             this._embedded.construct(tags), | ||||
|             this._tagsFilter); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import {Dependencies, TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; | ||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; | ||||
| import {TagsFilter, TagUtils} from "../Logic/Tags"; | ||||
| import {OnlyShowIfConstructor} from "./OnlyShowIf"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
|  | @ -137,8 +137,8 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { | |||
|                                      mappings?: { k: TagsFilter; txt: string | Translation; priority?: number; substitute?: boolean, hideInAnswer?: boolean }[] | ||||
|                                  }) => TagDependantUIElement; | ||||
| 
 | ||||
|     construct(dependencies: Dependencies): TagDependantUIElement { | ||||
|         return TagRenderingOptions.tagRendering(dependencies.tags, this.options); | ||||
|     construct(tags: UIEventSource<any>): TagDependantUIElement { | ||||
|         return TagRenderingOptions.tagRendering(tags, this.options); | ||||
|     } | ||||
| 
 | ||||
|     IsKnown(properties: any): boolean { | ||||
|  |  | |||
|  | @ -2,14 +2,9 @@ import {UIElement} from "../UI/UIElement"; | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import Translation from "../UI/i18n/Translation"; | ||||
| 
 | ||||
| 
 | ||||
| export interface Dependencies { | ||||
|     tags: UIEventSource<any> | ||||
| } | ||||
| 
 | ||||
| export interface TagDependantUIElementConstructor { | ||||
| 
 | ||||
|     construct(dependencies: Dependencies): TagDependantUIElement; | ||||
|     construct(tags: UIEventSource<any>): TagDependantUIElement; | ||||
|     IsKnown(properties: any): boolean; | ||||
|     IsQuestioning(properties: any): boolean; | ||||
|     GetContent(tags: any): Translation; | ||||
|  |  | |||
|  | @ -7,7 +7,6 @@ import Combine from "./UI/Base/Combine"; | |||
| import {UIElement} from "./UI/UIElement"; | ||||
| import {MoreScreen} from "./UI/MoreScreen"; | ||||
| import {FilteredLayer} from "./Logic/FilteredLayer"; | ||||
| import {FeatureInfoBox} from "./UI/FeatureInfoBox"; | ||||
| import {Basemap} from "./Logic/Leaflet/Basemap"; | ||||
| import State from "./State"; | ||||
| import {WelcomeMessage} from "./UI/WelcomeMessage"; | ||||
|  | @ -37,6 +36,7 @@ import {FromJSON} from "./Customizations/JSON/FromJSON"; | |||
| import {Utils} from "./Utils"; | ||||
| import BackgroundSelector from "./UI/BackgroundSelector"; | ||||
| import AvailableBaseLayers from "./Logic/AvailableBaseLayers"; | ||||
| import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox"; | ||||
| 
 | ||||
| export class InitUiElements { | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ export class StrayClickHandler { | |||
|         const map = State.state.bm.map; | ||||
|         State.state.filteredLayers.data.forEach((filteredLayer) => { | ||||
|             filteredLayer.isDisplayed.addCallback(isEnabled => { | ||||
|                 if(isEnabled){ | ||||
|                 if(isEnabled && self._lastMarker){ | ||||
|                     // When a layer is activated, we remove the 'last click location' in order to force the user to reclick
 | ||||
|                     // This reclick might be at a location where a feature now appeared...
 | ||||
|                      map.removeLayer(self._lastMarker); | ||||
|  |  | |||
|  | @ -1,72 +0,0 @@ | |||
| /** | ||||
|  * Helps in uplaoding, by generating the rigth title, decription and by adding the tag to the changeset | ||||
|  */ | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| import {ImageUploadFlow} from "../../UI/ImageUploadFlow"; | ||||
| import {SlideShow} from "../../UI/SlideShow"; | ||||
| import State from "../../State"; | ||||
| import {Tag} from "../Tags"; | ||||
| 
 | ||||
| export class OsmImageUploadHandler { | ||||
|     private readonly _tags: UIEventSource<any>; | ||||
|     private readonly _slideShow: SlideShow; | ||||
|     private readonly _preferedLicense: UIEventSource<string>; | ||||
| 
 | ||||
|     constructor(tags: UIEventSource<any>, | ||||
|                 preferedLicense: UIEventSource<string>, | ||||
|                 slideShow : SlideShow | ||||
|     ) { | ||||
|         this._slideShow = slideShow; // To move the slideshow (if any) to the last, just added element
 | ||||
|         this._tags = tags; | ||||
|         this._preferedLicense = preferedLicense; | ||||
|     } | ||||
| 
 | ||||
|     private generateOptions(license: string) { | ||||
|         const tags = this._tags.data; | ||||
|         const self = this; | ||||
|         license = license ?? "CC0" | ||||
|         const title = tags.name ?? "Unknown area"; | ||||
|         const description = [ | ||||
|             "author:" + State.state.osmConnection.userDetails.data.name, | ||||
|             "license:" + license, | ||||
|             "wikidata:" + tags.wikidata, | ||||
|             "osmid:" + tags.id, | ||||
|             "name:" + tags.name | ||||
|         ].join("\n"); | ||||
| 
 | ||||
|         const changes = State.state.changes; | ||||
|         return { | ||||
|             title: title, | ||||
|             description: description, | ||||
|             handleURL: (url) => { | ||||
| 
 | ||||
|                 let key = "image"; | ||||
|                 if (tags["image"] !== undefined) { | ||||
| 
 | ||||
|                     let freeIndex = 0; | ||||
|                     while (tags["image:" + freeIndex] !== undefined) { | ||||
|                         freeIndex++; | ||||
|                     } | ||||
|                     key = "image:" + freeIndex; | ||||
|                 } | ||||
|                 console.log("Adding image:" + key, url); | ||||
|                 changes.addTag(tags.id, new Tag(key, url)); | ||||
|                 self._slideShow.MoveTo(-1); // set the last (thus newly added) image) to view
 | ||||
|             }, | ||||
|             allDone: () => { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     getUI(): ImageUploadFlow { | ||||
|         const self = this; | ||||
|         return new ImageUploadFlow( | ||||
|             this._preferedLicense, | ||||
|             function (license) { | ||||
|                 return self.generateOptions(license) | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -40,7 +40,7 @@ export default class TagRenderingPreview extends UIElement { | |||
|                 new VariableUiElement(es.map(tagRenderingConfig => { | ||||
|                         try { | ||||
|                             const tr = FromJSON.TagRendering(tagRenderingConfig, "preview") | ||||
|                                 .construct({tags: self.previewTagValue}); | ||||
|                                 .construct(self.previewTagValue); | ||||
|                             return tr.Render(); | ||||
|                         } catch (e) { | ||||
|                             return new Combine(["Could not show this tagrendering:", e.message]).Render(); | ||||
|  |  | |||
|  | @ -1,38 +1,11 @@ | |||
| import {UIElement} from "../UIElement"; | ||||
| import {ImageSearcher} from "../../Logic/ImageSearcher"; | ||||
| import {SlideShow} from "../SlideShow"; | ||||
| import {SlideShow} from "./SlideShow"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import { | ||||
|     Dependencies, | ||||
|     TagDependantUIElement, | ||||
|     TagDependantUIElementConstructor | ||||
| } from "../../Customizations/UIElementConstructor"; | ||||
| import Translation from "../i18n/Translation"; | ||||
| import {TagDependantUIElement} from "../../Customizations/UIElementConstructor"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import DeleteImage from "./DeleteImage"; | ||||
| 
 | ||||
| export class ImageCarouselConstructor implements TagDependantUIElementConstructor { | ||||
|     IsKnown(properties: any): boolean { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     IsQuestioning(properties: any): boolean { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     Priority(): number { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     construct(dependencies: Dependencies): TagDependantUIElement { | ||||
|         return new ImageCarousel(dependencies.tags); | ||||
|     } | ||||
| 
 | ||||
|     GetContent(tags: any): Translation { | ||||
|         return new Translation({"en":"Images without upload"}); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class ImageCarousel extends TagDependantUIElement { | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,8 @@ | |||
| import { | ||||
|     Dependencies, | ||||
|     TagDependantUIElement, | ||||
|     TagDependantUIElementConstructor | ||||
| } from "../../Customizations/UIElementConstructor"; | ||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; | ||||
| import {ImageCarousel} from "./ImageCarousel"; | ||||
| import {ImageUploadFlow} from "../ImageUploadFlow"; | ||||
| import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler"; | ||||
| import State from "../../State"; | ||||
| import {ImageUploadFlow} from "./ImageUploadFlow"; | ||||
| import Translation from "../i18n/Translation"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| 
 | ||||
| export default class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ | ||||
|      | ||||
|  | @ -24,20 +19,25 @@ export default class ImageCarouselWithUploadConstructor implements TagDependantU | |||
|     } | ||||
|      | ||||
|     GetContent(tags: any): Translation { | ||||
|         return new Translation({"en":"Image carousel with uploader"}); | ||||
|         return new Translation({"*": "Image carousel with uploader"}); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class OsmImageUploadHandler { | ||||
|     constructor(tags: UIEventSource<any>) { | ||||
|          | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| class ImageCarouselWithUpload extends TagDependantUIElement { | ||||
|     private _imageElement: ImageCarousel; | ||||
|     private _pictureUploader: ImageUploadFlow; | ||||
| 
 | ||||
|     constructor(dependencies: Dependencies) { | ||||
|         super(dependencies.tags); | ||||
|         const tags = dependencies.tags; | ||||
|     constructor(tags: UIEventSource<any>) { | ||||
|         super(tags); | ||||
|         this._imageElement = new ImageCarousel(tags); | ||||
|         const license = State.state.osmConnection.GetPreference( "pictures-license"); | ||||
|         this._pictureUploader = new OsmImageUploadHandler(tags, license, this._imageElement.slideshow).getUI(); | ||||
|         this._pictureUploader = new OsmImageUploadHandler(tags).getUI(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,34 +1,27 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import $ from "jquery" | ||||
| import {DropDown} from "./Input/DropDown"; | ||||
| import Translations from "./i18n/Translations"; | ||||
| import Combine from "./Base/Combine"; | ||||
| import State from "../State"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {Imgur} from "../Logic/Web/Imgur"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import State from "../../State"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {Imgur} from "../../Logic/Web/Imgur"; | ||||
| import {DropDown} from "../Input/DropDown"; | ||||
| import {Tag} from "../../Logic/Tags"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| 
 | ||||
| export class ImageUploadFlow extends UIElement { | ||||
|     private _licensePicker: UIElement; | ||||
|     private _selectedLicence: UIEventSource<string>; | ||||
|     private _isUploading: UIEventSource<number> = new UIEventSource<number>(0) | ||||
|     private _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) }; | ||||
|     private _connectButton : UIElement; | ||||
|     private readonly _licensePicker: UIElement; | ||||
|     private readonly _tags: UIEventSource<any>; | ||||
|     private readonly _selectedLicence: UIEventSource<string>; | ||||
|     private readonly _isUploading: UIEventSource<number> = new UIEventSource<number>(0) | ||||
|     private readonly _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private readonly _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private readonly _connectButton: UIElement; | ||||
| 
 | ||||
|     constructor( | ||||
|         preferedLicense: UIEventSource<string>, | ||||
|         uploadOptions: ((license: string) => | ||||
|             { | ||||
|                 title: string, | ||||
|                 description: string, | ||||
|                 handleURL: ((url: string) => void), | ||||
|                 allDone: (() => void) | ||||
|             }) | ||||
|     ) { | ||||
|     constructor(tags: UIEventSource<any>) { | ||||
|         super(State.state.osmConnection.userDetails); | ||||
|         this._uploadOptions = uploadOptions; | ||||
|         this._tags = tags; | ||||
| 
 | ||||
|         this.ListenTo(this._isUploading); | ||||
|         this.ListenTo(this._didFail); | ||||
|         this.ListenTo(this._allDone); | ||||
|  | @ -39,7 +32,7 @@ export class ImageUploadFlow extends UIElement { | |||
|                 {value: "CC-BY-SA 4.0", shown: Translations.t.image.ccbs}, | ||||
|                 {value: "CC-BY 4.0", shown: Translations.t.image.ccb} | ||||
|             ], | ||||
|             preferedLicense | ||||
|             State.state.osmConnection.GetPreference("pictures-license") | ||||
|         ); | ||||
|         licensePicker.SetStyle("float:left"); | ||||
| 
 | ||||
|  | @ -54,7 +47,6 @@ export class ImageUploadFlow extends UIElement { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
| 
 | ||||
|         const t = Translations.t.image; | ||||
|  | @ -126,7 +118,7 @@ export class ImageUploadFlow extends UIElement { | |||
|             `<label for='fileselector-${this.id}'>` + | ||||
|             label.Render() + | ||||
|             "</label>" + | ||||
|             actualInputElement+ | ||||
|             actualInputElement + | ||||
|             "</form>"; | ||||
| 
 | ||||
|         return new Combine([ | ||||
|  | @ -136,6 +128,65 @@ export class ImageUploadFlow extends UIElement { | |||
|             .Render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private handleSuccessfulUpload(url) { | ||||
|         const tags = this._tags.data; | ||||
|         let key = "image"; | ||||
|         if (tags["image"] !== undefined) { | ||||
| 
 | ||||
|             let freeIndex = 0; | ||||
|             while (tags["image:" + freeIndex] !== undefined) { | ||||
|                 freeIndex++; | ||||
|             } | ||||
|             key = "image:" + freeIndex; | ||||
|         } | ||||
|         console.log("Adding image:" + key, url); | ||||
|         State.state.changes.addTag(tags.id, new Tag(key, url)); | ||||
|     } | ||||
| 
 | ||||
|     private handleFiles(files) { | ||||
|         this._isUploading.setData(files.length); | ||||
|         this._allDone.setData(false); | ||||
| 
 | ||||
|         if (this._selectedLicence.data === undefined) { | ||||
|             this._selectedLicence.setData("CC0"); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         const tags = this._tags.data; | ||||
|         const title = tags.name ?? "Unknown area"; | ||||
|         const description = [ | ||||
|             "author:" + State.state.osmConnection.userDetails.data.name, | ||||
|             "license:" + (this._selectedLicence.data ?? "CC0"), | ||||
|             "wikidata:" + tags.wikidata, | ||||
|             "osmid:" + tags.id, | ||||
|             "name:" + tags.name | ||||
|         ].join("\n"); | ||||
| 
 | ||||
|         const self = this; | ||||
| 
 | ||||
|         Imgur.uploadMultiple(title, | ||||
|             description, | ||||
|             files, | ||||
|             function (url) { | ||||
|                 console.log("File saved at", url); | ||||
|                 self._isUploading.setData(self._isUploading.data - 1); | ||||
|                 self.handleSuccessfulUpload(url); | ||||
|             }, | ||||
|             function () { | ||||
|                 console.log("All uploads completed"); | ||||
|                 self._allDone.setData(true); | ||||
|             }, | ||||
|             function (failReason) { | ||||
|                 console.log("Upload failed due to ", failReason) | ||||
|                 // No need to call something from the options -> we handle this here
 | ||||
|                 self._didFail.setData(true); | ||||
|                 self._isUploading.data--; | ||||
|                 self._isUploading.ping(); | ||||
|             }, 0 | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     InnerUpdate(htmlElement: HTMLElement) { | ||||
|         super.InnerUpdate(htmlElement); | ||||
|         const user = State.state.osmConnection.userDetails.data; | ||||
|  | @ -147,34 +198,7 @@ export class ImageUploadFlow extends UIElement { | |||
| 
 | ||||
|         function submitHandler() { | ||||
|             const files = $(selector).prop('files'); | ||||
|             self._isUploading.setData(files.length); | ||||
|             self._allDone.setData(false); | ||||
| 
 | ||||
|             if(self._selectedLicence.data === undefined){ | ||||
|                 self._selectedLicence.setData("CC0"); | ||||
|             } | ||||
|              | ||||
|             const opts = self._uploadOptions(self._selectedLicence.data); | ||||
| 
 | ||||
|             Imgur.uploadMultiple(opts.title, opts.description, files, | ||||
|                 function (url) { | ||||
|                     console.log("File saved at", url); | ||||
|                     self._isUploading.setData(self._isUploading.data - 1); | ||||
|                     opts.handleURL(url); | ||||
|                 }, | ||||
|                 function () { | ||||
|                     console.log("All uploads completed"); | ||||
|                     self._allDone.setData(true); | ||||
|                     opts.allDone(); | ||||
|                 }, | ||||
|                 function(failReason) { | ||||
|                     console.log("Upload failed due to ", failReason) | ||||
|                     // No need to call something from the options -> we handle this here
 | ||||
|                     self._didFail.setData(true); | ||||
|                     self._isUploading.data--; | ||||
|                     self._isUploading.ping(); | ||||
|                 },0 | ||||
|             ) | ||||
|             self.handleFiles(files) | ||||
|         } | ||||
| 
 | ||||
|         if (selector != null && form != null) { | ||||
|  | @ -1,6 +1,6 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| 
 | ||||
| export class SlideShow extends UIElement { | ||||
| 
 | ||||
|  | @ -15,6 +15,12 @@ export class SlideShow extends UIElement { | |||
|         super(embeddedElements); | ||||
|         this._embeddedElements = embeddedElements; | ||||
|         this.ListenTo(this._currentSlide); | ||||
|         this._embeddedElements | ||||
|             .stabilized(1000) | ||||
|             .addCallback(embedded => { | ||||
|                 // Always move to the last image - but at most once per second
 | ||||
|                 this._currentSlide.setData(this._embeddedElements.data.length - 1); | ||||
|             }); | ||||
| 
 | ||||
|         const self = this; | ||||
|         this._prev = new FixedUiElement("<div class='prev-button'>" + | ||||
|  | @ -1,16 +1,15 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import {VerticalCombine} from "./Base/VerticalCombine"; | ||||
| import {OsmLink} from "../Customizations/Questions/OsmLink"; | ||||
| import {WikipediaLink} from "../Customizations/Questions/WikipediaLink"; | ||||
| import {And} from "../Logic/Tags"; | ||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "../Customizations/UIElementConstructor"; | ||||
| import Translations from "./i18n/Translations"; | ||||
| import {Changes} from "../Logic/Osm/Changes"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import State from "../State"; | ||||
| import {TagRenderingOptions} from "../Customizations/TagRenderingOptions"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import Combine from "./Base/Combine"; | ||||
| import {VerticalCombine} from "../Base/VerticalCombine"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {WikipediaLink} from "../../Customizations/Questions/WikipediaLink"; | ||||
| import {OsmLink} from "../../Customizations/Questions/OsmLink"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {TagRenderingOptions} from "../../Customizations/TagRenderingOptions"; | ||||
| import State from "../../State"; | ||||
| import {And} from "../../Logic/Tags"; | ||||
| import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| 
 | ||||
| export class FeatureInfoBox extends UIElement { | ||||
| 
 | ||||
|  | @ -22,7 +21,6 @@ export class FeatureInfoBox extends UIElement { | |||
|      * The tags, wrapped in a global event source | ||||
|      */ | ||||
|     private readonly _tagsES: UIEventSource<any>; | ||||
|     private readonly _changes: Changes; | ||||
|     private readonly _title: UIElement; | ||||
|     private readonly _infoboxes: TagDependantUIElement[]; | ||||
| 
 | ||||
|  | @ -37,10 +35,13 @@ export class FeatureInfoBox extends UIElement { | |||
|     ) { | ||||
|         super(tagsES); | ||||
|         this._feature = feature; | ||||
|         this._tagsES = tagsES; | ||||
|         this._tagsES = tagsES | ||||
|         if(tagsES === undefined){ | ||||
|             throw "No Tags event source given" | ||||
|         } | ||||
|         this.ListenTo(State.state.osmConnection.userDetails); | ||||
|         this.SetClass("featureinfobox"); | ||||
|         const deps = {tags: this._tagsES, changes: this._changes} | ||||
|         const tags = this._tagsES; | ||||
| 
 | ||||
|         this._infoboxes = []; | ||||
|         elementsToShow = elementsToShow ?? [] | ||||
|  | @ -48,13 +49,13 @@ export class FeatureInfoBox extends UIElement { | |||
|         const self = this; | ||||
|         for (const tagRenderingOption of elementsToShow) { | ||||
|             self._infoboxes.push( | ||||
|                 tagRenderingOption.construct(deps)); | ||||
|                 tagRenderingOption.construct(tags)); | ||||
|         } | ||||
|         function initTags() { | ||||
|             self._infoboxes.splice(0, self._infoboxes.length); | ||||
|             for (const tagRenderingOption of elementsToShow) { | ||||
|                 self._infoboxes.push( | ||||
|                     tagRenderingOption.construct(deps)); | ||||
|                     tagRenderingOption.construct(tags)); | ||||
|             } | ||||
|             self.Update(); | ||||
|         } | ||||
|  | @ -74,7 +75,7 @@ export class FeatureInfoBox extends UIElement { | |||
|         } else if (title instanceof UIElement) { | ||||
|             renderedTitle = title; | ||||
|         } else { | ||||
|             renderedTitle = title.construct(deps); | ||||
|             renderedTitle = title.construct(tags); | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|  | @ -83,10 +84,10 @@ export class FeatureInfoBox extends UIElement { | |||
|             .SetClass("title-font") | ||||
| 
 | ||||
|         const osmLink = new OsmLink() | ||||
|             .construct(deps) | ||||
|             .construct(tags) | ||||
|             .SetStyle("width: 24px; display:block;") | ||||
|         const wikipedialink = new WikipediaLink() | ||||
|             .construct(deps) | ||||
|             .construct(tags) | ||||
|             .SetStyle("width: 24px; display:block;") | ||||
| 
 | ||||
|         this._title = new Combine([ | ||||
|  | @ -1,6 +1,6 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import Translations from "./i18n/Translations"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| 
 | ||||
| export class SaveButton extends UIElement { | ||||
|     private _value: UIEventSource<any>; | ||||
|  | @ -1,23 +1,23 @@ | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {And, Tag, TagsFilter, TagUtils} from "../Logic/Tags"; | ||||
| import Translations from "../UI/i18n/Translations"; | ||||
| import Locale from "../UI/i18n/Locale"; | ||||
| import Translation from "../UI/i18n/Translation"; | ||||
| import Combine from "../UI/Base/Combine"; | ||||
| import {TagDependantUIElement} from "../Customizations/UIElementConstructor"; | ||||
| import {UIElement} from "./UIElement"; | ||||
| import {VariableUiElement} from "./Base/VariableUIElement"; | ||||
| import InputElementMap from "./Input/InputElementMap"; | ||||
| import {InputElement} from "./Input/InputElement"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import Translation from "../i18n/Translation"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import InputElementMap from "../Input/InputElementMap"; | ||||
| import CheckBoxes from "../Input/Checkboxes"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {And, Tag, TagsFilter, TagUtils} from "../../Logic/Tags"; | ||||
| import {InputElement} from "../Input/InputElement"; | ||||
| import {SaveButton} from "./SaveButton"; | ||||
| import {RadioButton} from "./Input/RadioButton"; | ||||
| import {FixedInputElement} from "./Input/FixedInputElement"; | ||||
| import {TagRenderingOptions} from "../Customizations/TagRenderingOptions"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import ValidatedTextField from "./Input/ValidatedTextField"; | ||||
| import CheckBoxes from "./Input/Checkboxes"; | ||||
| import State from "../State"; | ||||
| import SpecialVisualizations from "./SpecialVisualizations"; | ||||
| import {RadioButton} from "../Input/RadioButton"; | ||||
| import {FixedInputElement} from "../Input/FixedInputElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import ValidatedTextField from "../Input/ValidatedTextField"; | ||||
| import {TagRenderingOptions} from "../../Customizations/TagRenderingOptions"; | ||||
| import State from "../../State"; | ||||
| import {SubstitutedTranslation} from "../SpecialVisualizations"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {TagDependantUIElement} from "../../Customizations/UIElementConstructor"; | ||||
| import Locale from "../i18n/Locale"; | ||||
| 
 | ||||
| export class TagRendering extends UIElement implements TagDependantUIElement { | ||||
| 
 | ||||
|  | @ -25,7 +25,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|     private readonly _question: string | Translation; | ||||
|     private readonly _mapping: { k: TagsFilter, txt: string | Translation, priority?: number }[]; | ||||
| 
 | ||||
|     private currentTags: UIEventSource<any>; | ||||
|     private readonly currentTags: UIEventSource<any>; | ||||
| 
 | ||||
| 
 | ||||
|     private readonly _freeform: { | ||||
|  | @ -71,6 +71,9 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|         mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[] | ||||
|     }) { | ||||
|         super(tags); | ||||
|         if (tags === undefined) { | ||||
|             throw "No tags given for a tagrendering..." | ||||
|         } | ||||
|         this.ListenTo(Locale.language); | ||||
|         this.ListenTo(this._editMode); | ||||
|         this.ListenTo(this._questionSkipped); | ||||
|  | @ -78,8 +81,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
| 
 | ||||
|         const self = this; | ||||
| 
 | ||||
|         this.currentTags = this._source.map(tags =>  | ||||
|             { | ||||
|         this.currentTags = this._source.map(tags => { | ||||
| 
 | ||||
|                 if (options.tagsPreprocessor === undefined) { | ||||
|                     return tags; | ||||
|  | @ -186,6 +188,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|         }, [Locale.language]); | ||||
|         // And at last, set up the skip button
 | ||||
|         this._skipButton = new VariableUiElement(cancelContents).onClick(cancel); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -297,7 +300,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|         } | ||||
| 
 | ||||
|         let txt = this.ApplyTemplate(mapping.txt); | ||||
|         if(txt.Render().indexOf("<img") >= 0){ | ||||
|         if (txt.Render().indexOf("<img") >= 0) { | ||||
|             txt.SetClass("question-option-with-border"); | ||||
|         } | ||||
|         const inputEl = new FixedInputElement(txt, mapping.k, | ||||
|  | @ -324,14 +327,14 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|         let type = prepost[1]; | ||||
| 
 | ||||
|         let isTextArea = false; | ||||
|         if(type === "text"){ | ||||
|         if (type === "text") { | ||||
|             isTextArea = true; | ||||
|             type = "string"; | ||||
|         } | ||||
| 
 | ||||
|         if(ValidatedTextField.AllTypes[type] === undefined){ | ||||
|             console.error("Type:",type, ValidatedTextField.AllTypes) | ||||
|             throw "Unkown type: "+type; | ||||
|         if (ValidatedTextField.AllTypes[type] === undefined) { | ||||
|             console.error("Type:", type, ValidatedTextField.AllTypes) | ||||
|             throw "Unkown type: " + type; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -444,7 +447,9 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|         return new FixedUiElement(""); | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
| 
 | ||||
|     private CreateComponent(): UIElement { | ||||
| 
 | ||||
| 
 | ||||
|         if (this.IsQuestioning() | ||||
|             && (State.state !== undefined) // If State.state is undefined, we are testing/custom theme building -> show regular save
 | ||||
|  | @ -452,31 +457,25 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
| 
 | ||||
|             const question = | ||||
|                 this.ApplyTemplate(this._question).SetClass('question-text'); | ||||
|             return "<div class='question'>" + | ||||
|                 new Combine([ | ||||
|                     question, | ||||
|                     "<br/>", | ||||
|                     this._questionElement, | ||||
|                     this._friendlyLogin, | ||||
|                 ]).Render() + "</div>"; | ||||
|             return new Combine(["<div class='question'>", | ||||
|                 question, | ||||
|                 "<br/>", | ||||
|                 this._questionElement, | ||||
|                 this._friendlyLogin, "</div>" | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|         if (this.IsQuestioning() || this._editMode.data) { | ||||
|             // Not yet known or questioning, we have to ask a question
 | ||||
| 
 | ||||
|             return "<div class='question'>" + | ||||
|                 new Combine([ | ||||
|                     "<span class='question-text'>", | ||||
|                     this.ApplyTemplate(this._question),  | ||||
|                     "</span>", | ||||
|                     "<br/>", | ||||
|                     "<div>", this._questionElement , "</div>", | ||||
|                     this._skipButton, | ||||
|                     this._saveButton, | ||||
|                     "<br/>", | ||||
|                     this._appliedTags | ||||
|                 ]).Render() + | ||||
|                 "</div>" | ||||
|             return new Combine([ | ||||
|                 this.ApplyTemplate(this._question).SetStyle('question-text'), | ||||
|                 "<br/>", | ||||
|                 "<div>", this._questionElement, "</div>", | ||||
|                 this._skipButton, | ||||
|                 this._saveButton, | ||||
|                 "<br/>", | ||||
|                 this._appliedTags | ||||
|             ]).SetClass('question'); | ||||
|         } | ||||
| 
 | ||||
|         if (this.IsKnown()) { | ||||
|  | @ -484,7 +483,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|             const answer = this.RenderAnswer(); | ||||
| 
 | ||||
|             if (answer.IsEmpty()) { | ||||
|                 return ""; | ||||
|                 return new FixedUiElement(""); | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -499,37 +498,35 @@ export class TagRendering extends UIElement implements TagDependantUIElement { | |||
|                 return new Combine([ | ||||
|                     answer, | ||||
|                     this._editButton]) | ||||
|                     .SetStyle(answerStyle) | ||||
|                     .Render(); | ||||
|                     .SetStyle(answerStyle); | ||||
|             } | ||||
| 
 | ||||
|             return answer.SetStyle(answerStyle).Render(); | ||||
|             return answer.SetStyle(answerStyle); | ||||
|         } | ||||
| 
 | ||||
|         return ""; | ||||
|         return new FixedUiElement(""); | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return this.CreateComponent().Render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     protected InnerUpdate(htmlElement: HTMLElement) { | ||||
|         this._editButton.Update(); | ||||
|     } | ||||
| 
 | ||||
|     private answerCache = {} | ||||
| 
 | ||||
|     private ApplyTemplate(template: string | Translation): UIElement { | ||||
|         if (template === undefined || template === null) { | ||||
|             return undefined; | ||||
|         const tr = Translations.WT(template); | ||||
|         if (this.answerCache[tr.id]) { | ||||
|             return this.answerCache[tr.id]; | ||||
|         } | ||||
|          | ||||
|         const knownSpecials : {funcName: string, constr: ((arg: string) => UIElement)}[]= SpecialVisualizations.specialVisualizations.map( | ||||
|             special => ({ | ||||
|                 funcName: special.funcName, | ||||
|                 constr: arg => special.constr(this.currentTags, arg.split(",")) | ||||
|             }) | ||||
|         ) | ||||
|          | ||||
|         return new VariableUiElement(this.currentTags.map(tags => { | ||||
|             return Translations.WT(template) | ||||
|                 .Subs(tags) | ||||
|                 .EvaluateSpecialComponents(knownSpecials) | ||||
|                 .InnerRender() | ||||
|         })).ListenTo(Locale.language); | ||||
|         // We have to cache these elemnts, otherwise it is to slow
 | ||||
|         const el = new SubstitutedTranslation(tr, this.currentTags); | ||||
|         this.answerCache[tr.id] = el; | ||||
|         return el; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
| } | ||||
|  | @ -3,6 +3,79 @@ import OpeningHoursVisualization from "./OhVisualization"; | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {VariableUiElement} from "./Base/VariableUIElement"; | ||||
| import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"; | ||||
| import {ImageCarousel} from "./Image/ImageCarousel"; | ||||
| import Translation from "./i18n/Translation"; | ||||
| import Combine from "./Base/Combine"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import Locale from "../UI/i18n/Locale"; | ||||
| import {ImageUploadFlow} from "./Image/ImageUploadFlow"; | ||||
| 
 | ||||
| export class SubstitutedTranslation extends UIElement { | ||||
|     private readonly tags: UIEventSource<any>; | ||||
|     private readonly translation: Translation; | ||||
|     private content: UIElement; | ||||
| 
 | ||||
|     constructor( | ||||
|         translation: Translation, | ||||
|         tags: UIEventSource<any>) { | ||||
|         super(tags); | ||||
|         this.translation = translation; | ||||
|         this.tags = tags; | ||||
|         const self = this; | ||||
|         Locale.language.addCallbackAndRun(() => { | ||||
|             self.content = self.CreateContent(); | ||||
|             self.Update(); | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return this.content.Render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     CreateContent(): UIElement { | ||||
|         let txt = this.translation?.txt; | ||||
|         if (txt === undefined) { | ||||
|             return new FixedUiElement("") | ||||
|         } | ||||
|         const tags = this.tags.data; | ||||
|         for (const key in tags) { | ||||
|             // Poor mans replace all
 | ||||
|             txt = txt.split("{" + key + "}").join(tags[key]); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return new Combine(this.EvaluateSpecialComponents(txt)); | ||||
|     } | ||||
| 
 | ||||
|     public EvaluateSpecialComponents(template: string): UIElement[] { | ||||
| 
 | ||||
|         for (const knownSpecial of SpecialVisualizations.specialVisualizations) { | ||||
| 
 | ||||
|             // NOte: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
 | ||||
|             const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)}(.*)`); | ||||
|             if (matched != null) { | ||||
| 
 | ||||
|                 // We found a special component that should be brought to live
 | ||||
|                 const partBefore = this.EvaluateSpecialComponents(matched[1]); | ||||
|                 const argument = matched[2]; | ||||
|                 const partAfter = this.EvaluateSpecialComponents(matched[3]); | ||||
|                 try { | ||||
|                     const args = argument.trim().split(",").map(str => str.trim()); | ||||
|                     const element = knownSpecial.constr(this.tags, args); | ||||
|                     return [...partBefore, element, ...partAfter] | ||||
|                 } catch (e) { | ||||
|                     console.error(e); | ||||
|                     return [...partBefore, ...partAfter] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // IF we end up here, no changes have to be made
 | ||||
|         return [new FixedUiElement(template)]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export default class SpecialVisualizations { | ||||
| 
 | ||||
|  | @ -13,18 +86,55 @@ export default class SpecialVisualizations { | |||
|         args: { name: string, defaultValue?: string, doc: string }[] | ||||
|     }[] = | ||||
| 
 | ||||
|         [{ | ||||
|             funcName: "opening_hours_table", | ||||
|             docs: "Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'.", | ||||
|             args: [{name: "key", defaultValue: "opening_hours", doc: "The tag from which the table is constructed"}], | ||||
|             constr: (tagSource: UIEventSource<any>, args) => { | ||||
|                 let keyname = args[0]; | ||||
|                 if (keyname === undefined || keyname === "") { | ||||
|                     keyname = keyname ?? "opening_hours" | ||||
|         [ | ||||
|             { | ||||
|                 funcName: "image_carousel", | ||||
|                 docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)", | ||||
|                 args: [{ | ||||
|                     name: "image tag(s)", | ||||
|                     defaultValue: "image,image:*,wikidata,wikipedia,wikimedia_commons", | ||||
|                     doc: "Image tag(s) where images are searched" | ||||
|                 }], | ||||
|                 constr: (tags, args) => { | ||||
|                     if (args.length > 0) { | ||||
|                         console.error("TODO HANDLE THESE ARGS") // TODO FIXME
 | ||||
| 
 | ||||
|                     } | ||||
|                     return new ImageCarousel(tags); | ||||
|                 } | ||||
|                 return new OpeningHoursVisualization(tagSource, keyname) | ||||
|             } | ||||
|         }, | ||||
|             }, | ||||
| 
 | ||||
|             { | ||||
|                 funcName: "image_upload", | ||||
|                 docs: "Creates a button where a user can upload an image to IMGUR", | ||||
|                 args: [{ | ||||
|                     doc: "Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)", | ||||
|                     defaultValue: "image", name: "image-key" | ||||
|                 }], | ||||
|                 constr: (tags, args) => { | ||||
|                     if (args.length > 0) { | ||||
|                         console.error("TODO HANDLE THESE ARGS") // TODO FIXME
 | ||||
| 
 | ||||
|                     } | ||||
|                     return new ImageUploadFlow(tags) | ||||
|                 } | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "opening_hours_table", | ||||
|                 docs: "Creates an opening-hours table. Usage: {opening_hours_table(opening_hours)} to create a table of the tag 'opening_hours'.", | ||||
|                 args: [{ | ||||
|                     name: "key", | ||||
|                     defaultValue: "opening_hours", | ||||
|                     doc: "The tag from which the table is constructed" | ||||
|                 }], | ||||
|                 constr: (tagSource: UIEventSource<any>, args) => { | ||||
|                     let keyname = args[0]; | ||||
|                     if (keyname === undefined || keyname === "") { | ||||
|                         keyname = keyname ?? "opening_hours" | ||||
|                     } | ||||
|                     return new OpeningHoursVisualization(tagSource, keyname) | ||||
|                 } | ||||
|             }, | ||||
| 
 | ||||
|             { | ||||
|                 funcName: "live", | ||||
|  |  | |||
|  | @ -40,39 +40,7 @@ export default class Translation extends UIElement { | |||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public EvaluateSpecialComponents(knownSpecials: { funcName: string, constr: ((call: string) => UIElement) }[]): UIElement { | ||||
|         const newTranslations = {}; | ||||
|         for (const lang in this.translations) { | ||||
|             let template: string = this.translations[lang]; | ||||
|   | ||||
|             for (const knownSpecial of knownSpecials) { | ||||
| 
 | ||||
|                 do { | ||||
|                     const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*)\\)}(.*)`); | ||||
|                     if (matched === null) { | ||||
|                         break; | ||||
|                     } | ||||
|                     const partBefore = matched[1]; | ||||
|                     const argument = matched[2]; | ||||
|                     const partAfter = matched[3]; | ||||
| 
 | ||||
|                     try { | ||||
| 
 | ||||
|                         const element = knownSpecial.constr(argument).Render(); | ||||
|                         template = partBefore + element + partAfter; | ||||
|                     } catch (e) { | ||||
|                         console.error(e); | ||||
|                         template = partBefore + partAfter; | ||||
|                     } | ||||
| 
 | ||||
|                 } while (true); | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|             newTranslations[lang] = template; | ||||
|         } | ||||
|         return new Translation(newTranslations); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     get txt(): string { | ||||
|  |  | |||
|  | @ -968,12 +968,18 @@ export default class Translations { | |||
|         return s; | ||||
|     } | ||||
| 
 | ||||
|     private static wtcache = {} | ||||
|     public static WT(s: string | Translation): Translation { | ||||
|         if(s === undefined){ | ||||
|             return undefined; | ||||
|         } | ||||
|         if (typeof (s) === "string") { | ||||
|             return new Translation({en: s}); | ||||
|             if(Translations.wtcache[s]){ | ||||
|                 return Translations.wtcache[s]; | ||||
|             } | ||||
|             const tr = new Translation({en: s}); | ||||
|             Translations.wtcache[s]=  tr; | ||||
|             return tr; | ||||
|         } | ||||
|         if (s instanceof Translation) { | ||||
|             return s; | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import {UIEventSource} from "./Logic/UIEventSource"; | ||||
| import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty"; | ||||
| import {TagRendering} from "./UI/TagRendering"; | ||||
| import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson"; | ||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||
| import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel"; | ||||
| import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; | ||||
| import {TagRendering} from "./UI/Popup/TagRendering"; | ||||
| 
 | ||||
| let layout = GenerateEmpty.createEmptyLayout(); | ||||
| if (window.location.hash.length > 10) { | ||||
|  |  | |||
|  | @ -367,6 +367,7 @@ body { | |||
| } | ||||
| 
 | ||||
| .question { | ||||
|     display: block; | ||||
|     margin-top: 1em; | ||||
|     background-color: #e5f5ff; | ||||
|     padding: 1em; | ||||
|  |  | |||
							
								
								
									
										2
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -6,7 +6,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters"; | |||
| import {UIEventSource} from "./Logic/UIEventSource"; | ||||
| import * as $ from "jquery"; | ||||
| import {FromJSON} from "./Customizations/JSON/FromJSON"; | ||||
| import {TagRendering} from "./UI/TagRendering"; | ||||
| import {TagRendering} from "./UI/Popup/TagRendering"; | ||||
| 
 | ||||
| TagRendering.injectFunction(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue