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 maps from "../../assets/layers/maps/maps.json"
|
||||||
import * as information_boards from "../../assets/layers/information_board/information_board.json"
|
import * as information_boards from "../../assets/layers/information_board/information_board.json"
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
|
|
||||||
import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel";
|
|
||||||
import State from "../../State";
|
import State from "../../State";
|
||||||
|
|
||||||
export class FromJSON {
|
export class FromJSON {
|
||||||
|
@ -139,23 +137,15 @@ export class FromJSON {
|
||||||
|
|
||||||
if (typeof json === "string") {
|
if (typeof json === "string") {
|
||||||
switch (json) {
|
switch (json) {
|
||||||
case "picture": {
|
|
||||||
return new ImageCarouselWithUploadConstructor()
|
|
||||||
}
|
|
||||||
case "pictures": {
|
case "pictures": {
|
||||||
return new ImageCarouselWithUploadConstructor()
|
json = "{image_carousel()}{image_upload()}";
|
||||||
}
|
break;
|
||||||
case "image": {
|
|
||||||
return new ImageCarouselWithUploadConstructor()
|
|
||||||
}
|
}
|
||||||
case "images": {
|
case "images": {
|
||||||
return new ImageCarouselWithUploadConstructor()
|
json = "{image_carousel()}{image_upload()}";
|
||||||
}
|
|
||||||
case "picturesNoUpload": {
|
|
||||||
return new ImageCarouselConstructor()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.warn("Possible literal rendering:", json)
|
||||||
|
|
||||||
return new TagRenderingOptions({
|
return new TagRenderingOptions({
|
||||||
freeform: {
|
freeform: {
|
||||||
|
|
|
@ -16,9 +16,9 @@ export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{
|
||||||
this._embedded = embedded;
|
this._embedded = embedded;
|
||||||
}
|
}
|
||||||
|
|
||||||
construct(dependencies): TagDependantUIElement {
|
construct(tags: UIEventSource<any>): TagDependantUIElement {
|
||||||
return new OnlyShowIf(dependencies.tags,
|
return new OnlyShowIf(tags,
|
||||||
this._embedded.construct(dependencies),
|
this._embedded.construct(tags),
|
||||||
this._tagsFilter);
|
this._tagsFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Dependencies, TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
|
import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
|
||||||
import {TagsFilter, TagUtils} from "../Logic/Tags";
|
import {TagsFilter, TagUtils} from "../Logic/Tags";
|
||||||
import {OnlyShowIfConstructor} from "./OnlyShowIf";
|
import {OnlyShowIfConstructor} from "./OnlyShowIf";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
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 }[]
|
mappings?: { k: TagsFilter; txt: string | Translation; priority?: number; substitute?: boolean, hideInAnswer?: boolean }[]
|
||||||
}) => TagDependantUIElement;
|
}) => TagDependantUIElement;
|
||||||
|
|
||||||
construct(dependencies: Dependencies): TagDependantUIElement {
|
construct(tags: UIEventSource<any>): TagDependantUIElement {
|
||||||
return TagRenderingOptions.tagRendering(dependencies.tags, this.options);
|
return TagRenderingOptions.tagRendering(tags, this.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsKnown(properties: any): boolean {
|
IsKnown(properties: any): boolean {
|
||||||
|
|
|
@ -2,14 +2,9 @@ import {UIElement} from "../UI/UIElement";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
import Translation from "../UI/i18n/Translation";
|
import Translation from "../UI/i18n/Translation";
|
||||||
|
|
||||||
|
|
||||||
export interface Dependencies {
|
|
||||||
tags: UIEventSource<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TagDependantUIElementConstructor {
|
export interface TagDependantUIElementConstructor {
|
||||||
|
|
||||||
construct(dependencies: Dependencies): TagDependantUIElement;
|
construct(tags: UIEventSource<any>): TagDependantUIElement;
|
||||||
IsKnown(properties: any): boolean;
|
IsKnown(properties: any): boolean;
|
||||||
IsQuestioning(properties: any): boolean;
|
IsQuestioning(properties: any): boolean;
|
||||||
GetContent(tags: any): Translation;
|
GetContent(tags: any): Translation;
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Combine from "./UI/Base/Combine";
|
||||||
import {UIElement} from "./UI/UIElement";
|
import {UIElement} from "./UI/UIElement";
|
||||||
import {MoreScreen} from "./UI/MoreScreen";
|
import {MoreScreen} from "./UI/MoreScreen";
|
||||||
import {FilteredLayer} from "./Logic/FilteredLayer";
|
import {FilteredLayer} from "./Logic/FilteredLayer";
|
||||||
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
|
||||||
import {Basemap} from "./Logic/Leaflet/Basemap";
|
import {Basemap} from "./Logic/Leaflet/Basemap";
|
||||||
import State from "./State";
|
import State from "./State";
|
||||||
import {WelcomeMessage} from "./UI/WelcomeMessage";
|
import {WelcomeMessage} from "./UI/WelcomeMessage";
|
||||||
|
@ -37,6 +36,7 @@ import {FromJSON} from "./Customizations/JSON/FromJSON";
|
||||||
import {Utils} from "./Utils";
|
import {Utils} from "./Utils";
|
||||||
import BackgroundSelector from "./UI/BackgroundSelector";
|
import BackgroundSelector from "./UI/BackgroundSelector";
|
||||||
import AvailableBaseLayers from "./Logic/AvailableBaseLayers";
|
import AvailableBaseLayers from "./Logic/AvailableBaseLayers";
|
||||||
|
import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox";
|
||||||
|
|
||||||
export class InitUiElements {
|
export class InitUiElements {
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class StrayClickHandler {
|
||||||
const map = State.state.bm.map;
|
const map = State.state.bm.map;
|
||||||
State.state.filteredLayers.data.forEach((filteredLayer) => {
|
State.state.filteredLayers.data.forEach((filteredLayer) => {
|
||||||
filteredLayer.isDisplayed.addCallback(isEnabled => {
|
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
|
// 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...
|
// This reclick might be at a location where a feature now appeared...
|
||||||
map.removeLayer(self._lastMarker);
|
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 => {
|
new VariableUiElement(es.map(tagRenderingConfig => {
|
||||||
try {
|
try {
|
||||||
const tr = FromJSON.TagRendering(tagRenderingConfig, "preview")
|
const tr = FromJSON.TagRendering(tagRenderingConfig, "preview")
|
||||||
.construct({tags: self.previewTagValue});
|
.construct(self.previewTagValue);
|
||||||
return tr.Render();
|
return tr.Render();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return new Combine(["Could not show this tagrendering:", e.message]).Render();
|
return new Combine(["Could not show this tagrendering:", e.message]).Render();
|
||||||
|
|
|
@ -1,38 +1,11 @@
|
||||||
import {UIElement} from "../UIElement";
|
import {UIElement} from "../UIElement";
|
||||||
import {ImageSearcher} from "../../Logic/ImageSearcher";
|
import {ImageSearcher} from "../../Logic/ImageSearcher";
|
||||||
import {SlideShow} from "../SlideShow";
|
import {SlideShow} from "./SlideShow";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {
|
import {TagDependantUIElement} from "../../Customizations/UIElementConstructor";
|
||||||
Dependencies,
|
|
||||||
TagDependantUIElement,
|
|
||||||
TagDependantUIElementConstructor
|
|
||||||
} from "../../Customizations/UIElementConstructor";
|
|
||||||
import Translation from "../i18n/Translation";
|
|
||||||
import Combine from "../Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import DeleteImage from "./DeleteImage";
|
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 {
|
export class ImageCarousel extends TagDependantUIElement {
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
import {
|
import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor";
|
||||||
Dependencies,
|
|
||||||
TagDependantUIElement,
|
|
||||||
TagDependantUIElementConstructor
|
|
||||||
} from "../../Customizations/UIElementConstructor";
|
|
||||||
import {ImageCarousel} from "./ImageCarousel";
|
import {ImageCarousel} from "./ImageCarousel";
|
||||||
import {ImageUploadFlow} from "../ImageUploadFlow";
|
import {ImageUploadFlow} from "./ImageUploadFlow";
|
||||||
import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler";
|
|
||||||
import State from "../../State";
|
|
||||||
import Translation from "../i18n/Translation";
|
import Translation from "../i18n/Translation";
|
||||||
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
|
||||||
export default class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{
|
export default class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{
|
||||||
|
|
||||||
|
@ -24,20 +19,25 @@ export default class ImageCarouselWithUploadConstructor implements TagDependantU
|
||||||
}
|
}
|
||||||
|
|
||||||
GetContent(tags: any): Translation {
|
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 {
|
class ImageCarouselWithUpload extends TagDependantUIElement {
|
||||||
private _imageElement: ImageCarousel;
|
private _imageElement: ImageCarousel;
|
||||||
private _pictureUploader: ImageUploadFlow;
|
private _pictureUploader: ImageUploadFlow;
|
||||||
|
|
||||||
constructor(dependencies: Dependencies) {
|
constructor(tags: UIEventSource<any>) {
|
||||||
super(dependencies.tags);
|
super(tags);
|
||||||
const tags = dependencies.tags;
|
|
||||||
this._imageElement = new ImageCarousel(tags);
|
this._imageElement = new ImageCarousel(tags);
|
||||||
const license = State.state.osmConnection.GetPreference( "pictures-license");
|
this._pictureUploader = new OsmImageUploadHandler(tags).getUI();
|
||||||
this._pictureUploader = new OsmImageUploadHandler(tags, license, this._imageElement.slideshow).getUI();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,27 @@
|
||||||
import {UIElement} from "./UIElement";
|
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
import {DropDown} from "./Input/DropDown";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Translations from "./i18n/Translations";
|
import {UIElement} from "../UIElement";
|
||||||
import Combine from "./Base/Combine";
|
import State from "../../State";
|
||||||
import State from "../State";
|
import Combine from "../Base/Combine";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import {Imgur} from "../Logic/Web/Imgur";
|
import {Imgur} from "../../Logic/Web/Imgur";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import {DropDown} from "../Input/DropDown";
|
||||||
|
import {Tag} from "../../Logic/Tags";
|
||||||
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
export class ImageUploadFlow extends UIElement {
|
export class ImageUploadFlow extends UIElement {
|
||||||
private _licensePicker: UIElement;
|
private readonly _licensePicker: UIElement;
|
||||||
private _selectedLicence: UIEventSource<string>;
|
private readonly _tags: UIEventSource<any>;
|
||||||
private _isUploading: UIEventSource<number> = new UIEventSource<number>(0)
|
private readonly _selectedLicence: UIEventSource<string>;
|
||||||
private _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
private readonly _isUploading: UIEventSource<number> = new UIEventSource<number>(0)
|
||||||
private _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
private readonly _didFail: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) };
|
private readonly _allDone: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
private _connectButton : UIElement;
|
private readonly _connectButton: UIElement;
|
||||||
|
|
||||||
constructor(
|
constructor(tags: UIEventSource<any>) {
|
||||||
preferedLicense: UIEventSource<string>,
|
|
||||||
uploadOptions: ((license: string) =>
|
|
||||||
{
|
|
||||||
title: string,
|
|
||||||
description: string,
|
|
||||||
handleURL: ((url: string) => void),
|
|
||||||
allDone: (() => void)
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
super(State.state.osmConnection.userDetails);
|
super(State.state.osmConnection.userDetails);
|
||||||
this._uploadOptions = uploadOptions;
|
this._tags = tags;
|
||||||
|
|
||||||
this.ListenTo(this._isUploading);
|
this.ListenTo(this._isUploading);
|
||||||
this.ListenTo(this._didFail);
|
this.ListenTo(this._didFail);
|
||||||
this.ListenTo(this._allDone);
|
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-SA 4.0", shown: Translations.t.image.ccbs},
|
||||||
{value: "CC-BY 4.0", shown: Translations.t.image.ccb}
|
{value: "CC-BY 4.0", shown: Translations.t.image.ccb}
|
||||||
],
|
],
|
||||||
preferedLicense
|
State.state.osmConnection.GetPreference("pictures-license")
|
||||||
);
|
);
|
||||||
licensePicker.SetStyle("float:left");
|
licensePicker.SetStyle("float:left");
|
||||||
|
|
||||||
|
@ -54,7 +47,6 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
|
|
||||||
const t = Translations.t.image;
|
const t = Translations.t.image;
|
||||||
|
@ -126,7 +118,7 @@ export class ImageUploadFlow extends UIElement {
|
||||||
`<label for='fileselector-${this.id}'>` +
|
`<label for='fileselector-${this.id}'>` +
|
||||||
label.Render() +
|
label.Render() +
|
||||||
"</label>" +
|
"</label>" +
|
||||||
actualInputElement+
|
actualInputElement +
|
||||||
"</form>";
|
"</form>";
|
||||||
|
|
||||||
return new Combine([
|
return new Combine([
|
||||||
|
@ -136,6 +128,65 @@ export class ImageUploadFlow extends UIElement {
|
||||||
.Render();
|
.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) {
|
InnerUpdate(htmlElement: HTMLElement) {
|
||||||
super.InnerUpdate(htmlElement);
|
super.InnerUpdate(htmlElement);
|
||||||
const user = State.state.osmConnection.userDetails.data;
|
const user = State.state.osmConnection.userDetails.data;
|
||||||
|
@ -147,34 +198,7 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
function submitHandler() {
|
function submitHandler() {
|
||||||
const files = $(selector).prop('files');
|
const files = $(selector).prop('files');
|
||||||
self._isUploading.setData(files.length);
|
self.handleFiles(files)
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selector != null && form != null) {
|
if (selector != null && form != null) {
|
|
@ -1,6 +1,6 @@
|
||||||
import {UIElement} from "./UIElement";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import {UIElement} from "../UIElement";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
|
|
||||||
export class SlideShow extends UIElement {
|
export class SlideShow extends UIElement {
|
||||||
|
|
||||||
|
@ -15,6 +15,12 @@ export class SlideShow extends UIElement {
|
||||||
super(embeddedElements);
|
super(embeddedElements);
|
||||||
this._embeddedElements = embeddedElements;
|
this._embeddedElements = embeddedElements;
|
||||||
this.ListenTo(this._currentSlide);
|
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;
|
const self = this;
|
||||||
this._prev = new FixedUiElement("<div class='prev-button'>" +
|
this._prev = new FixedUiElement("<div class='prev-button'>" +
|
|
@ -1,16 +1,15 @@
|
||||||
import {UIElement} from "./UIElement";
|
import {VerticalCombine} from "../Base/VerticalCombine";
|
||||||
import {VerticalCombine} from "./Base/VerticalCombine";
|
import {UIElement} from "../UIElement";
|
||||||
import {OsmLink} from "../Customizations/Questions/OsmLink";
|
import Combine from "../Base/Combine";
|
||||||
import {WikipediaLink} from "../Customizations/Questions/WikipediaLink";
|
import {WikipediaLink} from "../../Customizations/Questions/WikipediaLink";
|
||||||
import {And} from "../Logic/Tags";
|
import {OsmLink} from "../../Customizations/Questions/OsmLink";
|
||||||
import {TagDependantUIElement, TagDependantUIElementConstructor} from "../Customizations/UIElementConstructor";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Translations from "./i18n/Translations";
|
import {TagRenderingOptions} from "../../Customizations/TagRenderingOptions";
|
||||||
import {Changes} from "../Logic/Osm/Changes";
|
import State from "../../State";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import {And} from "../../Logic/Tags";
|
||||||
import State from "../State";
|
import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor";
|
||||||
import {TagRenderingOptions} from "../Customizations/TagRenderingOptions";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import Translations from "../i18n/Translations";
|
||||||
import Combine from "./Base/Combine";
|
|
||||||
|
|
||||||
export class FeatureInfoBox extends UIElement {
|
export class FeatureInfoBox extends UIElement {
|
||||||
|
|
||||||
|
@ -22,7 +21,6 @@ export class FeatureInfoBox extends UIElement {
|
||||||
* The tags, wrapped in a global event source
|
* The tags, wrapped in a global event source
|
||||||
*/
|
*/
|
||||||
private readonly _tagsES: UIEventSource<any>;
|
private readonly _tagsES: UIEventSource<any>;
|
||||||
private readonly _changes: Changes;
|
|
||||||
private readonly _title: UIElement;
|
private readonly _title: UIElement;
|
||||||
private readonly _infoboxes: TagDependantUIElement[];
|
private readonly _infoboxes: TagDependantUIElement[];
|
||||||
|
|
||||||
|
@ -37,10 +35,13 @@ export class FeatureInfoBox extends UIElement {
|
||||||
) {
|
) {
|
||||||
super(tagsES);
|
super(tagsES);
|
||||||
this._feature = feature;
|
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.ListenTo(State.state.osmConnection.userDetails);
|
||||||
this.SetClass("featureinfobox");
|
this.SetClass("featureinfobox");
|
||||||
const deps = {tags: this._tagsES, changes: this._changes}
|
const tags = this._tagsES;
|
||||||
|
|
||||||
this._infoboxes = [];
|
this._infoboxes = [];
|
||||||
elementsToShow = elementsToShow ?? []
|
elementsToShow = elementsToShow ?? []
|
||||||
|
@ -48,13 +49,13 @@ export class FeatureInfoBox extends UIElement {
|
||||||
const self = this;
|
const self = this;
|
||||||
for (const tagRenderingOption of elementsToShow) {
|
for (const tagRenderingOption of elementsToShow) {
|
||||||
self._infoboxes.push(
|
self._infoboxes.push(
|
||||||
tagRenderingOption.construct(deps));
|
tagRenderingOption.construct(tags));
|
||||||
}
|
}
|
||||||
function initTags() {
|
function initTags() {
|
||||||
self._infoboxes.splice(0, self._infoboxes.length);
|
self._infoboxes.splice(0, self._infoboxes.length);
|
||||||
for (const tagRenderingOption of elementsToShow) {
|
for (const tagRenderingOption of elementsToShow) {
|
||||||
self._infoboxes.push(
|
self._infoboxes.push(
|
||||||
tagRenderingOption.construct(deps));
|
tagRenderingOption.construct(tags));
|
||||||
}
|
}
|
||||||
self.Update();
|
self.Update();
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ export class FeatureInfoBox extends UIElement {
|
||||||
} else if (title instanceof UIElement) {
|
} else if (title instanceof UIElement) {
|
||||||
renderedTitle = title;
|
renderedTitle = title;
|
||||||
} else {
|
} else {
|
||||||
renderedTitle = title.construct(deps);
|
renderedTitle = title.construct(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,10 +84,10 @@ export class FeatureInfoBox extends UIElement {
|
||||||
.SetClass("title-font")
|
.SetClass("title-font")
|
||||||
|
|
||||||
const osmLink = new OsmLink()
|
const osmLink = new OsmLink()
|
||||||
.construct(deps)
|
.construct(tags)
|
||||||
.SetStyle("width: 24px; display:block;")
|
.SetStyle("width: 24px; display:block;")
|
||||||
const wikipedialink = new WikipediaLink()
|
const wikipedialink = new WikipediaLink()
|
||||||
.construct(deps)
|
.construct(tags)
|
||||||
.SetStyle("width: 24px; display:block;")
|
.SetStyle("width: 24px; display:block;")
|
||||||
|
|
||||||
this._title = new Combine([
|
this._title = new Combine([
|
|
@ -1,6 +1,6 @@
|
||||||
import {UIElement} from "./UIElement";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Translations from "./i18n/Translations";
|
import {UIElement} from "../UIElement";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
export class SaveButton extends UIElement {
|
export class SaveButton extends UIElement {
|
||||||
private _value: UIEventSource<any>;
|
private _value: UIEventSource<any>;
|
|
@ -1,23 +1,23 @@
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIElement} from "../UIElement";
|
||||||
import {And, Tag, TagsFilter, TagUtils} from "../Logic/Tags";
|
import Translation from "../i18n/Translation";
|
||||||
import Translations from "../UI/i18n/Translations";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import Locale from "../UI/i18n/Locale";
|
import InputElementMap from "../Input/InputElementMap";
|
||||||
import Translation from "../UI/i18n/Translation";
|
import CheckBoxes from "../Input/Checkboxes";
|
||||||
import Combine from "../UI/Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import {TagDependantUIElement} from "../Customizations/UIElementConstructor";
|
import {And, Tag, TagsFilter, TagUtils} from "../../Logic/Tags";
|
||||||
import {UIElement} from "./UIElement";
|
import {InputElement} from "../Input/InputElement";
|
||||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
|
||||||
import InputElementMap from "./Input/InputElementMap";
|
|
||||||
import {InputElement} from "./Input/InputElement";
|
|
||||||
import {SaveButton} from "./SaveButton";
|
import {SaveButton} from "./SaveButton";
|
||||||
import {RadioButton} from "./Input/RadioButton";
|
import {RadioButton} from "../Input/RadioButton";
|
||||||
import {FixedInputElement} from "./Input/FixedInputElement";
|
import {FixedInputElement} from "../Input/FixedInputElement";
|
||||||
import {TagRenderingOptions} from "../Customizations/TagRenderingOptions";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import ValidatedTextField from "../Input/ValidatedTextField";
|
||||||
import ValidatedTextField from "./Input/ValidatedTextField";
|
import {TagRenderingOptions} from "../../Customizations/TagRenderingOptions";
|
||||||
import CheckBoxes from "./Input/Checkboxes";
|
import State from "../../State";
|
||||||
import State from "../State";
|
import {SubstitutedTranslation} from "../SpecialVisualizations";
|
||||||
import SpecialVisualizations 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 {
|
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 _question: string | Translation;
|
||||||
private readonly _mapping: { k: TagsFilter, txt: string | Translation, priority?: number }[];
|
private readonly _mapping: { k: TagsFilter, txt: string | Translation, priority?: number }[];
|
||||||
|
|
||||||
private currentTags: UIEventSource<any>;
|
private readonly currentTags: UIEventSource<any>;
|
||||||
|
|
||||||
|
|
||||||
private readonly _freeform: {
|
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 }[]
|
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[]
|
||||||
}) {
|
}) {
|
||||||
super(tags);
|
super(tags);
|
||||||
|
if (tags === undefined) {
|
||||||
|
throw "No tags given for a tagrendering..."
|
||||||
|
}
|
||||||
this.ListenTo(Locale.language);
|
this.ListenTo(Locale.language);
|
||||||
this.ListenTo(this._editMode);
|
this.ListenTo(this._editMode);
|
||||||
this.ListenTo(this._questionSkipped);
|
this.ListenTo(this._questionSkipped);
|
||||||
|
@ -78,8 +81,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
this.currentTags = this._source.map(tags =>
|
this.currentTags = this._source.map(tags => {
|
||||||
{
|
|
||||||
|
|
||||||
if (options.tagsPreprocessor === undefined) {
|
if (options.tagsPreprocessor === undefined) {
|
||||||
return tags;
|
return tags;
|
||||||
|
@ -186,6 +188,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
}, [Locale.language]);
|
}, [Locale.language]);
|
||||||
// And at last, set up the skip button
|
// And at last, set up the skip button
|
||||||
this._skipButton = new VariableUiElement(cancelContents).onClick(cancel);
|
this._skipButton = new VariableUiElement(cancelContents).onClick(cancel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -297,7 +300,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let txt = this.ApplyTemplate(mapping.txt);
|
let txt = this.ApplyTemplate(mapping.txt);
|
||||||
if(txt.Render().indexOf("<img") >= 0){
|
if (txt.Render().indexOf("<img") >= 0) {
|
||||||
txt.SetClass("question-option-with-border");
|
txt.SetClass("question-option-with-border");
|
||||||
}
|
}
|
||||||
const inputEl = new FixedInputElement(txt, mapping.k,
|
const inputEl = new FixedInputElement(txt, mapping.k,
|
||||||
|
@ -324,14 +327,14 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
let type = prepost[1];
|
let type = prepost[1];
|
||||||
|
|
||||||
let isTextArea = false;
|
let isTextArea = false;
|
||||||
if(type === "text"){
|
if (type === "text") {
|
||||||
isTextArea = true;
|
isTextArea = true;
|
||||||
type = "string";
|
type = "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ValidatedTextField.AllTypes[type] === undefined){
|
if (ValidatedTextField.AllTypes[type] === undefined) {
|
||||||
console.error("Type:",type, ValidatedTextField.AllTypes)
|
console.error("Type:", type, ValidatedTextField.AllTypes)
|
||||||
throw "Unkown type: "+type;
|
throw "Unkown type: " + type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -444,7 +447,9 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
return new FixedUiElement("");
|
return new FixedUiElement("");
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerRender(): string {
|
|
||||||
|
private CreateComponent(): UIElement {
|
||||||
|
|
||||||
|
|
||||||
if (this.IsQuestioning()
|
if (this.IsQuestioning()
|
||||||
&& (State.state !== undefined) // If State.state is undefined, we are testing/custom theme building -> show regular save
|
&& (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 =
|
const question =
|
||||||
this.ApplyTemplate(this._question).SetClass('question-text');
|
this.ApplyTemplate(this._question).SetClass('question-text');
|
||||||
return "<div class='question'>" +
|
return new Combine(["<div class='question'>",
|
||||||
new Combine([
|
question,
|
||||||
question,
|
"<br/>",
|
||||||
"<br/>",
|
this._questionElement,
|
||||||
this._questionElement,
|
this._friendlyLogin, "</div>"
|
||||||
this._friendlyLogin,
|
]);
|
||||||
]).Render() + "</div>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.IsQuestioning() || this._editMode.data) {
|
if (this.IsQuestioning() || this._editMode.data) {
|
||||||
// Not yet known or questioning, we have to ask a question
|
// Not yet known or questioning, we have to ask a question
|
||||||
|
return new Combine([
|
||||||
return "<div class='question'>" +
|
this.ApplyTemplate(this._question).SetStyle('question-text'),
|
||||||
new Combine([
|
"<br/>",
|
||||||
"<span class='question-text'>",
|
"<div>", this._questionElement, "</div>",
|
||||||
this.ApplyTemplate(this._question),
|
this._skipButton,
|
||||||
"</span>",
|
this._saveButton,
|
||||||
"<br/>",
|
"<br/>",
|
||||||
"<div>", this._questionElement , "</div>",
|
this._appliedTags
|
||||||
this._skipButton,
|
]).SetClass('question');
|
||||||
this._saveButton,
|
|
||||||
"<br/>",
|
|
||||||
this._appliedTags
|
|
||||||
]).Render() +
|
|
||||||
"</div>"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.IsKnown()) {
|
if (this.IsKnown()) {
|
||||||
|
@ -484,7 +483,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
const answer = this.RenderAnswer();
|
const answer = this.RenderAnswer();
|
||||||
|
|
||||||
if (answer.IsEmpty()) {
|
if (answer.IsEmpty()) {
|
||||||
return "";
|
return new FixedUiElement("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,37 +498,35 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||||
return new Combine([
|
return new Combine([
|
||||||
answer,
|
answer,
|
||||||
this._editButton])
|
this._editButton])
|
||||||
.SetStyle(answerStyle)
|
.SetStyle(answerStyle);
|
||||||
.Render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
private ApplyTemplate(template: string | Translation): UIElement {
|
||||||
if (template === undefined || template === null) {
|
const tr = Translations.WT(template);
|
||||||
return undefined;
|
if (this.answerCache[tr.id]) {
|
||||||
|
return this.answerCache[tr.id];
|
||||||
}
|
}
|
||||||
|
// We have to cache these elemnts, otherwise it is to slow
|
||||||
const knownSpecials : {funcName: string, constr: ((arg: string) => UIElement)}[]= SpecialVisualizations.specialVisualizations.map(
|
const el = new SubstitutedTranslation(tr, this.currentTags);
|
||||||
special => ({
|
this.answerCache[tr.id] = el;
|
||||||
funcName: special.funcName,
|
return el;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,6 +3,79 @@ import OpeningHoursVisualization from "./OhVisualization";
|
||||||
import {UIEventSource} from "../Logic/UIEventSource";
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||||
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler";
|
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 {
|
export default class SpecialVisualizations {
|
||||||
|
|
||||||
|
@ -13,18 +86,55 @@ export default class SpecialVisualizations {
|
||||||
args: { name: string, defaultValue?: string, doc: string }[]
|
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'.",
|
funcName: "image_carousel",
|
||||||
args: [{name: "key", defaultValue: "opening_hours", doc: "The tag from which the table is constructed"}],
|
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)",
|
||||||
constr: (tagSource: UIEventSource<any>, args) => {
|
args: [{
|
||||||
let keyname = args[0];
|
name: "image tag(s)",
|
||||||
if (keyname === undefined || keyname === "") {
|
defaultValue: "image,image:*,wikidata,wikipedia,wikimedia_commons",
|
||||||
keyname = keyname ?? "opening_hours"
|
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",
|
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 {
|
get txt(): string {
|
||||||
|
|
|
@ -968,12 +968,18 @@ export default class Translations {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static wtcache = {}
|
||||||
public static WT(s: string | Translation): Translation {
|
public static WT(s: string | Translation): Translation {
|
||||||
if(s === undefined){
|
if(s === undefined){
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (typeof (s) === "string") {
|
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) {
|
if (s instanceof Translation) {
|
||||||
return s;
|
return s;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
|
import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
|
||||||
import {TagRendering} from "./UI/TagRendering";
|
|
||||||
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
|
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
|
||||||
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
||||||
import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel";
|
import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel";
|
||||||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
|
||||||
|
import {TagRendering} from "./UI/Popup/TagRendering";
|
||||||
|
|
||||||
let layout = GenerateEmpty.createEmptyLayout();
|
let layout = GenerateEmpty.createEmptyLayout();
|
||||||
if (window.location.hash.length > 10) {
|
if (window.location.hash.length > 10) {
|
||||||
|
|
|
@ -367,6 +367,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.question {
|
.question {
|
||||||
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
background-color: #e5f5ff;
|
background-color: #e5f5ff;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
|
2
index.ts
2
index.ts
|
@ -6,7 +6,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters";
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import * as $ from "jquery";
|
import * as $ from "jquery";
|
||||||
import {FromJSON} from "./Customizations/JSON/FromJSON";
|
import {FromJSON} from "./Customizations/JSON/FromJSON";
|
||||||
import {TagRendering} from "./UI/TagRendering";
|
import {TagRendering} from "./UI/Popup/TagRendering";
|
||||||
|
|
||||||
TagRendering.injectFunction();
|
TagRendering.injectFunction();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue