From 232664ee14fac71cc6ce2cc06fe5ed0f06ec5273 Mon Sep 17 00:00:00 2001 From: Pieter Fiers Date: Mon, 20 Jul 2020 12:39:43 +0200 Subject: [PATCH 01/29] Half complete i18n --- Customizations/AllKnownLayouts.ts | 6 +++--- Customizations/Layout.ts | 14 ++++++++------ Customizations/Layouts/Cyclofix.ts | 14 ++++---------- Customizations/TagRendering.ts | 4 ++-- UI/i18n/Locale.ts | 18 ++++++++++++++++++ UI/i18n/Translation.ts | 16 ++++++++++++++++ UI/i18n/Translations.ts | 18 ++++++++++++++++++ index.css | 4 ++++ index.html | 5 +++++ index.ts | 25 ++++++++++++++++++++++--- 10 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 UI/i18n/Locale.ts create mode 100644 UI/i18n/Translation.ts create mode 100644 UI/i18n/Translations.ts diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 9a8728e..cc4f2fb 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -8,9 +8,9 @@ import {All} from "./Layouts/All"; import {Layout} from "./Layout"; export class AllKnownLayouts { - public static allSets: any = AllKnownLayouts.AllLayouts(); + public static allSets = AllKnownLayouts.AllLayouts(); - private static AllLayouts() : any{ + private static AllLayouts(): Map { const all = new All(); const layouts : Layout[] = [ new Groen(), @@ -22,7 +22,7 @@ export class AllKnownLayouts { new Statues(), */ ]; - const allSets = {}; + const allSets: Map = new Map(); for (const layout of layouts) { allSets[layout.name] = layout; all.layers = all.layers.concat(layout.layers); diff --git a/Customizations/Layout.ts b/Customizations/Layout.ts index 7c72927..a1d888e 100644 --- a/Customizations/Layout.ts +++ b/Customizations/Layout.ts @@ -1,13 +1,15 @@ import {LayerDefinition} from "./LayerDefinition"; +import { UIElement } from "../UI/UIElement"; +import { FixedUiElement } from "../UI/Base/FixedUiElement"; /** * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). */ export class Layout { public name: string; - public title: string; + public title: UIElement; public layers: LayerDefinition[]; - public welcomeMessage: string; + public welcomeMessage: UIElement; public gettingStartedPlzLogin: string; public welcomeBackMessage: string; @@ -33,23 +35,23 @@ export class Layout { */ constructor( name: string, - title: string, + title: UIElement | string, layers: LayerDefinition[], startzoom: number, startLat: number, startLon: number, - welcomeMessage: string, + welcomeMessage: UIElement | string, gettingStartedPlzLogin: string = "Please login to get started", welcomeBackMessage: string = "You are logged in. Welcome back!", welcomeTail: string = "" ) { - this.title = title; + this.title = typeof(title) === 'string' ? new FixedUiElement(title) : title; this.startLon = startLon; this.startLat = startLat; this.startzoom = startzoom; this.name = name; this.layers = layers; - this.welcomeMessage = welcomeMessage; + this.welcomeMessage = typeof(welcomeMessage) === 'string' ? new FixedUiElement(welcomeMessage) : welcomeMessage; this.gettingStartedPlzLogin = gettingStartedPlzLogin; this.welcomeBackMessage = welcomeBackMessage; this.welcomeTail = welcomeTail; diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 9ed6a3b..328acda 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -2,27 +2,21 @@ import {Layout} from "../Layout"; import BikeParkings from "../Layers/BikeParkings"; import BikeServices from "../Layers/BikeStations"; import {GhostBike} from "../Layers/GhostBike"; +import Translations from "../../UI/i18n/Translations"; export default class Cyclofix extends Layout { constructor() { super( "pomp", - "Cyclofix bicycle infrastructure", + Translations.t.cylofix.title, [new GhostBike(), new BikeServices(), new BikeParkings()], 16, 50.8465573, 4.3516970, - - - "

Cyclofix bicycle infrastructure

\n" + + "

" + Translations.t.cylofix.title.Render() + "

\n" + "\n" + - "

EN> On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + - "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.

" + - "

NL> Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + - "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.

" + - "

FR> Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + - "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins.

" + `

${Translations.t.cylofix.description.Render()}

` , "", ""); } diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index c5f8d63..60b0713 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -35,7 +35,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { * If 'question' is undefined, then the question is never asked at all * If the question is "" (empty string) then the question is */ - question?: string, + question?: UIElement | string, /** * What is the priority of the question. @@ -56,7 +56,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { * * */ - mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[], + mappings?: { k: TagsFilter, txt: UIElement | string, priority?: number, substitute?: boolean }[], /** diff --git a/UI/i18n/Locale.ts b/UI/i18n/Locale.ts new file mode 100644 index 0000000..52c45bb --- /dev/null +++ b/UI/i18n/Locale.ts @@ -0,0 +1,18 @@ +import { UIEventSource } from "../UIEventSource"; + + +const LANGUAGE_KEY = 'language' + +export default class Locale { + public static language: UIEventSource = new UIEventSource(Locale.getInitialLanguage()) + + public static init() { + Locale.language.addCallback(data => { + localStorage.setItem(LANGUAGE_KEY, data) + }) + } + + private static getInitialLanguage() { + return localStorage.getItem(LANGUAGE_KEY) + } +} diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts new file mode 100644 index 0000000..4b53b17 --- /dev/null +++ b/UI/i18n/Translation.ts @@ -0,0 +1,16 @@ +import { UIElement } from "../UIElement" +import Locale from "./Locale" + + +export default class Translation extends UIElement{ + protected InnerRender(): string { + return this.translations[Locale.language.data] + } + + public readonly translations: object + + constructor(translations: object) { + super(Locale.language) + this.translations = translations + } +} diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts new file mode 100644 index 0000000..edf0561 --- /dev/null +++ b/UI/i18n/Translations.ts @@ -0,0 +1,18 @@ +import Translation from "./Translation"; + + +export default class Translations { + static t = { + cylofix: { + title: new Translation({en: 'Cyclofix bicycle infrastructure', nl: 'Cyclofix fietsinfrastructuur', fr: 'TODO: FRENCH TRANSLATION'}), + description: new Translation({ + en: "On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + + "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.", + nl: "Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + + "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.", + fr: "Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + + "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins." + }) + } + } +} diff --git a/index.css b/index.css index c86b574..672e558 100644 --- a/index.css +++ b/index.css @@ -238,6 +238,10 @@ form { height: 1em; } +#language-select { + pointer-events: all; + cursor: pointer; +} #messagesbox-wrapper { } diff --git a/index.html b/index.html index 16c5387..bcdeca3 100644 --- a/index.html +++ b/index.html @@ -34,6 +34,11 @@
+
diff --git a/index.ts b/index.ts index 6696d39..0e41b1f 100644 --- a/index.ts +++ b/index.ts @@ -22,6 +22,10 @@ import {SearchAndGo} from "./UI/SearchAndGo"; import {CollapseButton} from "./UI/Base/CollapseButton"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; import {All} from "./Customizations/Layouts/All"; +import Translations from "./UI/i18n/Translations"; +import Translation from "./UI/i18n/Translation"; +import Locale from "./UI/i18n/Locale"; +import { Layout } from "./Customizations/Layout"; @@ -87,10 +91,13 @@ if (paramDict.test) { dryRun = true; } -const layoutToUse = AllKnownLayouts.allSets[defaultLayout]; +const layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout]; console.log("Using layout: ", layoutToUse.name); -document.title = layoutToUse.title; +document.title = layoutToUse.title.Render(); +Locale.language.addCallback(e => { + document.title = layoutToUse.title.Render(); +}) // ----------------- Setup a few event sources ------------- @@ -242,7 +249,7 @@ var welcomeMessage = () => { login = layoutToUse.welcomeBackMessage; } return "
" + - layoutToUse.welcomeMessage + login + layoutToUse.welcomeTail + + layoutToUse.welcomeMessage.Render() + login + layoutToUse.welcomeTail + "
"; }), function () { @@ -277,3 +284,15 @@ new GeoLocationHandler(bm).AttachTo("geolocate-button"); locationControl.ping(); messageBox.update(); + + +// --- Locale --- + +Locale.init() + +window.setLanguage = function(language:string) { + Locale.language.setData(language) +} + +// const eLanguageSelect = document.getElementById('language-select') as HTMLOptionElement +// eLanguageSelect.addEventListener('selectionchange') From dcf5d240021e80a5e2ee0f299004462645dae679 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 20 Jul 2020 13:28:45 +0200 Subject: [PATCH 02/29] Intermediary --- Logic/Changes.ts | 1 - Logic/Question.ts | 508 ------------------------------ UI/Base/FixedInputElement.ts | 25 ++ UI/Base/TextField.ts | 46 ++- UI/Base/UIInputElement.ts | 2 + UI/Base/UIRadioButton.ts | 38 ++- UI/Base/UIRadioButtonWithOther.ts | 72 ----- UI/UIEventSource.ts | 25 +- UI/UserBadge.ts | 1 + test.ts | 30 +- 10 files changed, 140 insertions(+), 608 deletions(-) delete mode 100644 Logic/Question.ts create mode 100644 UI/Base/FixedInputElement.ts delete mode 100644 UI/Base/UIRadioButtonWithOther.ts diff --git a/Logic/Changes.ts b/Logic/Changes.ts index e0dd9ce..488ea50 100644 --- a/Logic/Changes.ts +++ b/Logic/Changes.ts @@ -6,7 +6,6 @@ import {OsmConnection} from "./OsmConnection"; import {OsmNode, OsmObject} from "./OsmObject"; import {ElementStorage} from "./ElementStorage"; import {UIEventSource} from "../UI/UIEventSource"; -import {Question, QuestionDefinition} from "./Question"; import {And, Tag, TagsFilter} from "./TagsFilter"; export class Changes { diff --git a/Logic/Question.ts b/Logic/Question.ts deleted file mode 100644 index 3ffd646..0000000 --- a/Logic/Question.ts +++ /dev/null @@ -1,508 +0,0 @@ -import {Changes} from "./Changes"; -import {UIElement} from "../UI/UIElement"; -import {UIEventSource} from "../UI/UIEventSource"; - -export class QuestionUI extends UIElement { - private readonly _q: Question; - private readonly _tags: UIEventSource; - /** - * The ID of the calling question - used to trigger it's onsave - */ - private readonly _qid; - - constructor(q: Question, qid: number, tags: UIEventSource) { - super(tags); - this._q = q; - this._tags = tags; - this._qid = qid; - } - - - private RenderRadio() { - let radios = ""; - let c = 0; - for (let answer of this._q.question.answers) { - const human = answer.text; - const ansId = "q" + this._qid + "-answer" + c; - radios += - "" + - "" + - "
"; - c++; - } - return radios; - } - - private RenderRadioText() { - let radios = ""; - let c = 0; - for (let answer of this._q.question.answers) { - const human = answer.text; - const ansId = "q" + this._qid + "-answer" + c; - radios += - "" + - "" + - "
"; - c++; - } - const ansId = "q" + this._qid + "-answer" + c; - - radios += - "" + - "" + - "
"; - - return radios; - } - - - InnerRender(): string { - - if (!this._q.Applicable(this._tags.data)) { - return ""; - } - - - const q = this._q.question; - - - let answers = ""; - if (q.type == "radio") { - answers += this.RenderRadio(); - } else if (q.type == "text") { - answers += "
" - } else if (q.type == "radio+text") { - answers += this.RenderRadioText(); - } else { - alert("PLZ RENDER TYPE " + q.type); - } - - - const embeddedScriptSave = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", false )'; - const embeddedScriptSkip = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", true )'; - const saveButton = ""; - const skip = ""; - return q.question + "
" + answers + saveButton + skip; - } - - InnerUpdate(htmlElement: HTMLElement) { - } -} - - -export class QuestionDefinition { - - - static noNameOrNameQuestion(question: string, noExplicitName : string, severity : number) : QuestionDefinition{ - const q = new QuestionDefinition(question); - - q.type = 'radio+text'; - q.addAnwser(noExplicitName, "noname","yes"); - q.addUnrequiredTag("name", "*"); - q.addUnrequiredTag("noname", "yes"); - - q.key = "name"; - q.severity = severity; - return q; - } - - static textQuestion( - question: string, - key: string, - severity: number - ): QuestionDefinition { - const q = new QuestionDefinition(question); - q.type = 'text'; - q.key = key; - q.severity = severity; - q.addUnrequiredTag(key, '*'); - return q; - } - - static radioQuestionSimple( - question: string, - severity: number, - key: string, - answers: { text: string, value: string }[]) { - - - const answers0: { - text: string, - tags: { k: string, v: string }[], - }[] = []; - for (const i in answers) { - const answer = answers[i]; - answers0.push({text: answer.text, tags: [{k: key, v: answer.value}]}) - } - - var q = this.radioQuestion(question, severity, answers0); - q.key = key; - q.addUnrequiredTag(key, '*'); - return q; - } - - static radioAndTextQuestion( - question: string, - severity: number, - key: string, - answers: { text: string, value: string }[]) { - - const q = this.radioQuestionSimple(question, severity, key, answers); - q.type = 'radio+text'; - return q; - - } - - static radioQuestion( - question: string, - severity: number, - answers: - { - text: string, - tags: { k: string, v: string }[], - }[] - ): QuestionDefinition { - - - const q = new QuestionDefinition(question); - q.severity = severity; - q.type = 'radio'; - q.answers = answers; - for (const i in answers) { - const answer = answers[i]; - for (const j in answer.tags) { - const tag = answer.tags[j]; - q.addUnrequiredTag(tag.k, tag.v); - } - } - - return q; - } - - - static GrbNoNumberQuestion() : QuestionDefinition{ - const q = new QuestionDefinition("Heeft dit gebouw een huisnummer?"); - q.type = "radio"; - q.severity = 10; - q.answers = [{ - text: "Ja, het OSM-huisnummer is correct", - tags: [{k: "fixme", v: ""}] - }, { - - text: "Nee, het is een enkele garage", - tags: [{k: "building", v: "garage"}, {k: "fixme", v: ""}] - }, { - - text: "Nee, het zijn meerdere garages", - tags: [{k: "building", v: "garages"}, {k: "fixme", v: ""}] - } - - - ]; - q.addRequiredTag("fixme", "GRB thinks that this has number no number") - return q; - } - - static GrbHouseNumberQuestion() : QuestionDefinition{ - - - const q = new QuestionDefinition("Wat is het huisnummer?"); - q.type = "radio+text"; - q.severity = 10; - - q.answers = [{ - text: "Het OSM-huisnummer is correct", - tags: [{k: "fixme", v: ""}], - }] - q.key = "addr:housenumber"; - - - q.addRequiredTag("fixme", "*"); - - return q; - } - - - private constructor(question: string) { - this.question = question; - } - - /** - * Question for humans - */ - public question: string; - - /** - * 'type' indicates how the answers are rendered and must be one of: - * 'text' for a free to fill text field - * 'radio' for radiobuttons - * 'radio+text' for radiobuttons and a freefill text field - * 'dropdown' for a dropdown menu - * 'number' for a number field - * - * If 'text' or 'number' is specified, 'key' is used as tag for the answer. - * If 'radio' or 'dropdown' is specified, the answers are used from 'tags' - * - */ - public type: string = 'radio'; - /** - * Only used for 'text' or 'number' questions - */ - public key: string = null; - - public answers: { - text: string, - tags: { k: string, v: string }[] - }[]; - - /** - * Indicates that the element must have _all_ the tags defined below - * Dictionary 'key' => [values]; empty list is wildcard - */ - private mustHaveAllTags = []; - - /** - * Indicates that the element must _not_ have any of the tags defined below. - * Dictionary 'key' => [values] - */ - private mustNotHaveTags = []; - - /** - * Severity: how important the question is - * The higher, the sooner it'll be shown - */ - public severity: number = 0; - - addRequiredTag(key: string, value: string) { - if (this.mustHaveAllTags[key] === undefined) { - this.mustHaveAllTags[key] = [value]; - } else { - if(this.mustHaveAllTags[key] === []){ - // Wildcard - return; - } - this.mustHaveAllTags[key].push(value); - } - - if (value === '*') { - this.mustHaveAllTags[key] = []; - } - return this; - } - - addUnrequiredTag(key: string, value: string) { - let valueList = this.mustNotHaveTags[key]; - - if (valueList === undefined) { - valueList = [value]; - this.mustNotHaveTags[key] = valueList; - } else { - if (valueList === []) { - return; - } - valueList.push(value); - } - - if (value === '*') { - this.mustNotHaveTags[key] = []; - } - return this; - } - - private addAnwser(anwser: string, key: string, value: string) { - if (this.answers === undefined) { - this.answers = [{text: anwser, tags: [{k: key, v: value}]}]; - } else { - this.answers.push({text: anwser, tags: [{k: key, v: value}]}); - } - this.addUnrequiredTag(key, value); - } - - public isApplicable(alreadyExistingTags): boolean { - for (let k in this.mustHaveAllTags) { - - var actual = alreadyExistingTags[k]; - if (actual === undefined) { - return false; - } - - let possibleVals = this.mustHaveAllTags[k]; - if (possibleVals.length == 0) { - // Wildcard - continue; - } - - let index = possibleVals.indexOf(actual); - if (index < 0) { - return false - } - } - - for (var k in this.mustNotHaveTags) { - var actual = alreadyExistingTags[k]; - if (actual === undefined) { - continue; - } - let impossibleVals = this.mustNotHaveTags[k]; - if (impossibleVals.length == 0) { - // Wildcard - return false; - } - - let index = impossibleVals.indexOf(actual); - if (index >= 0) { - return false - } - } - - return true; - - } -} - - -export class Question { - - - // All the questions are stored in here, to be able to retrieve them globaly. This is a workaround, see below - static questions = Question.InitCallbackFunction(); - - static InitCallbackFunction(): Question[] { - - // This needs some explanation, as it is a workaround - Question.questions = []; - // The html in a popup is only created when the user actually clicks to open it - // This means that we can not bind code to an HTML-element (as it doesn't exist yet) - // We work around this, by letting the 'save' button just call the function 'questionAnswered' with the ID of the question - // THis defines and registers this global function - - - /** - * Calls back to the question with either the answer or 'skip' - * @param questionId - * @param elementId - */ - function questionAnswered(questionId, elementId, dontKnow) { - if (dontKnow) { - Question.questions[questionId].Skip(elementId); - } else { - Question.questions[questionId].OnSave(elementId); - } - } - - - function checkRadioButton(id) { - // @ts-ignore - document.getElementById(id).checked = true; - } - - // must cast as any to set property on window - // @ts-ignore - const _global = (window /* browser */ || global /* node */) as any; - _global.questionAnswered = questionAnswered; - _global.checkRadioButton = checkRadioButton; - return []; - } - - - public readonly question: QuestionDefinition; - private _changeHandler: Changes; - private readonly _qId; - public skippedElements: string[] = []; - - constructor( - changeHandler: Changes, - question: QuestionDefinition) { - - this.question = question; - - this._qId = Question.questions.length; - this._changeHandler = changeHandler; - Question.questions.push(this); - } - - /** - * SHould this question be asked? - * Returns false if question is already there or if a premise is missing - */ - public Applicable(tags): boolean { - - if (this.skippedElements.indexOf(tags.id) >= 0) { - return false; - } - - return this.question.isApplicable(tags); - } - - /** - * - * @param elementId: the OSM-id of the element to perform the change on, format 'way/123', 'node/456' or 'relation/789' - * @constructor - */ - protected OnSave(elementId: string) { - let tagsToApply: { k: string, v: string }[] = []; - const q: QuestionDefinition = this.question; - let tp = this.question.type; - if (tp === "radio") { - const selected = document.querySelector('input[name="q' + this._qId + '"]:checked'); - if (selected === null) { - console.log("No answer selected"); - return - } - let index = (selected as any).value; - tagsToApply = q.answers[index].tags; - } else if (tp === "text") { - // @ts-ignore - let value = document.getElementById("q-" + this._qId + "-textbox").value; - if (value === undefined || value.length == 0) { - console.log("Answer too short"); - return; - } - tagsToApply = [{k: q.key, v: value}]; - } else if (tp === "radio+text") { - const selected = document.querySelector('input[name="q' + this._qId + '"]:checked'); - if (selected === null) { - console.log("No answer selected"); - return - } - let index = (selected as any).value; - if (index < q.answers.length) { - // A 'proper' answer was selected - tagsToApply = q.answers[index].tags; - } else { - // The textfield was selected - // @ts-ignore - let value = document.getElementById("q-" + this._qId + "-textbox").value; - if (value === undefined || value.length < 3) { - console.log("Answer too short"); - return; - } - tagsToApply = [{k: q.key, v: value}]; - } - - } - - console.log("Question.ts: Applying tags",tagsToApply," to element ", elementId); - - for (const toApply of tagsToApply) { - this._changeHandler.addChange(elementId, toApply.k, toApply.v); - } - - } - - /** - * Creates the HTML question for this tag collection - */ - public CreateHtml(tags: UIEventSource): UIElement { - return new QuestionUI(this, this._qId, tags); - } - - - private Skip(elementId: any) { - this.skippedElements.push(elementId); - console.log("SKIP"); - // Yeah, this is cheating below - // It is an easy way to notify the UIElement that something has changed - this._changeHandler._allElements.getElement(elementId).ping(); - } -} \ No newline at end of file diff --git a/UI/Base/FixedInputElement.ts b/UI/Base/FixedInputElement.ts new file mode 100644 index 0000000..0d7cf0a --- /dev/null +++ b/UI/Base/FixedInputElement.ts @@ -0,0 +1,25 @@ +import {UIInputElement} from "./UIInputElement"; +import {UIEventSource} from "../UIEventSource"; +import {UIElement} from "../UIElement"; +import {FixedUiElement} from "./FixedUiElement"; + + +export class FixedInputElement extends UIInputElement { + private rendering: UIElement; + private value: UIEventSource; + + constructor(rendering: UIElement | string, value: T) { + super(undefined); + this.value = new UIEventSource(value); + this.rendering = typeof (rendering) === 'string' ? new FixedUiElement(rendering) : rendering; + } + + GetValue(): UIEventSource { + return this.value; + } + + protected InnerRender(): string { + return this.rendering.Render(); + } + +} \ No newline at end of file diff --git a/UI/Base/TextField.ts b/UI/Base/TextField.ts index ca0c4ea..31560d2 100644 --- a/UI/Base/TextField.ts +++ b/UI/Base/TextField.ts @@ -5,33 +5,59 @@ import {UIInputElement} from "./UIInputElement"; export class TextField extends UIInputElement { - public value: UIEventSource = new UIEventSource(""); + private value: UIEventSource; + private mappedValue: UIEventSource; /** * Pings and has the value data */ public enterPressed = new UIEventSource(undefined); private _placeholder: UIEventSource; - private _mapping: (string) => T; + private _pretext: string; + private _fromString: (string: string) => T; - constructor(placeholder: UIEventSource, - mapping: ((string) => T)) { - super(placeholder); - this._placeholder = placeholder; - this._mapping = mapping; + constructor(options: { + placeholder?: UIEventSource, + toString: (t: T) => string, + fromString: (string: string) => T, + pretext?: string, + value?: UIEventSource + }) { + super(options?.placeholder); + this.value = new UIEventSource(""); + this.mappedValue = options?.value ?? new UIEventSource(undefined); + + + this.value.addCallback((str) => this.mappedValue.setData(options.fromString(str))); + this.mappedValue.addCallback((t) => this.value.setData(options.toString(t))); + + + this._placeholder = options?.placeholder ?? new UIEventSource(""); + this._pretext = options?.pretext ?? ""; + + const self = this; + this.mappedValue.addCallback((t) => { + if (t === undefined && t === null) { + return; + } + const field = document.getElementById('text-' + this.id); + if (field === undefined && field === null) { + return; + } + field.value = options.toString(t); + }) } GetValue(): UIEventSource { - return this.value.map(this._mapping); + return this.mappedValue; } protected InnerRender(): string { - return "
" + + return this._pretext + "" + "" + "
"; } InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); const field = document.getElementById('text-' + this.id); const self = this; field.oninput = () => { diff --git a/UI/Base/UIInputElement.ts b/UI/Base/UIInputElement.ts index fd4a242..fc4b3a6 100644 --- a/UI/Base/UIInputElement.ts +++ b/UI/Base/UIInputElement.ts @@ -5,4 +5,6 @@ export abstract class UIInputElement extends UIElement{ abstract GetValue() : UIEventSource; + + } \ No newline at end of file diff --git a/UI/Base/UIRadioButton.ts b/UI/Base/UIRadioButton.ts index 2413073..12a1e24 100644 --- a/UI/Base/UIRadioButton.ts +++ b/UI/Base/UIRadioButton.ts @@ -7,25 +7,45 @@ export class UIRadioButton extends UIInputElement { public readonly SelectedElementIndex: UIEventSource = new UIEventSource(null); - private readonly _elements: UIEventSource + private value: UIEventSource; + private readonly _elements: UIInputElement[] private _selectFirstAsDefault: boolean; private _valueMapping: (i: number) => T; - constructor(elements: UIEventSource, - valueMapping: ((i: number) => T), + + constructor(elements: UIInputElement[], selectFirstAsDefault = true) { - super(elements); + super(undefined); this._elements = elements; this._selectFirstAsDefault = selectFirstAsDefault; const self = this; - this._valueMapping = valueMapping; this.SelectedElementIndex.addCallback(() => { self.InnerUpdate(undefined); }) + + + this.value = + UIEventSource.flatten(this.SelectedElementIndex.map( + (selectedIndex) => { + if (selectedIndex !== undefined && selectedIndex !== null) { + return elements[selectedIndex].GetValue() + } + } + ), elements.map(e => e.GetValue())) + ; + + + for (let i = 0; i < elements.length; i ++){ + elements[i].onClick(( ) => { + self.SelectedElementIndex.setData(i); + }); + } + + } GetValue(): UIEventSource { - return this.SelectedElementIndex.map(this._valueMapping); + return this.value; } @@ -37,7 +57,7 @@ export class UIRadioButton extends UIInputElement { let body = ""; let i = 0; - for (const el of this._elements.data) { + for (const el of this._elements) { const htmlElement = '' + '' + @@ -54,7 +74,7 @@ export class UIRadioButton extends UIInputElement { const self = this; function checkButtons() { - for (let i = 0; i < self._elements.data.length; i++) { + for (let i = 0; i < self._elements.length; i++) { const el = document.getElementById(self.IdFor(i)); // @ts-ignore if (el.checked) { @@ -87,7 +107,7 @@ export class UIRadioButton extends UIInputElement { var expected = this.SelectedElementIndex.data; if (expected) { - for (let i = 0; i < self._elements.data.length; i++) { + for (let i = 0; i < self._elements.length; i++) { const el = document.getElementById(self.IdFor(i)); // @ts-ignore if (el.checked) { diff --git a/UI/Base/UIRadioButtonWithOther.ts b/UI/Base/UIRadioButtonWithOther.ts deleted file mode 100644 index 806764d..0000000 --- a/UI/Base/UIRadioButtonWithOther.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {UIInputElement} from "./UIInputElement"; -import {UIEventSource} from "../UIEventSource"; -import {UIRadioButton} from "./UIRadioButton"; -import {UIElement} from "../UIElement"; -import {TextField} from "./TextField"; -import {FixedUiElement} from "./FixedUiElement"; - - -export class UIRadioButtonWithOther extends UIInputElement { - private readonly _radioSelector: UIRadioButton; - private readonly _freeformText: TextField; - private readonly _value: UIEventSource = new UIEventSource(undefined) - - constructor(choices: UIElement[], - otherChoiceTemplate: string, - placeholder: string, - choiceToValue: ((i: number) => T), - stringToValue: ((string: string) => T)) { - super(undefined); - const self = this; - - this._freeformText = new TextField( - new UIEventSource(placeholder), - stringToValue); - - - const otherChoiceElement = new FixedUiElement( - otherChoiceTemplate.replace("$$$", this._freeformText.Render())); - choices.push(otherChoiceElement); - - this._radioSelector = new UIRadioButton(new UIEventSource(choices), - (i) => { - if (i === undefined || i === null) { - return undefined; - } - if (i + 1 >= choices.length) { - return this._freeformText.GetValue().data - } - return choiceToValue(i); - }, - false); - - this._radioSelector.GetValue().addCallback( - (i) => { - self._value.setData(i); - }); - this._freeformText.GetValue().addCallback((str) => { - self._value.setData(str); - } - ); - this._freeformText.onClick(() => { - self._radioSelector.SelectedElementIndex.setData(choices.length - 1); - }) - - - } - - GetValue(): UIEventSource { - return this._value; - } - - protected InnerRender(): string { - return this._radioSelector.Render(); - } - - InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); - this._radioSelector.Update(); - this._freeformText.Update(); - } - -} \ No newline at end of file diff --git a/UI/UIEventSource.ts b/UI/UIEventSource.ts index eae2151..1e4aeb5 100644 --- a/UI/UIEventSource.ts +++ b/UI/UIEventSource.ts @@ -27,15 +27,32 @@ export class UIEventSource{ } } - public map(f: ((T) => J), - extraSources : UIEventSource[] = []): UIEventSource { - const self = this; + public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { + const sink = new UIEventSource(source.data?.data); + + source.addCallback((latestData) => { + sink.setData(latestData?.data); + }); + + for (const possibleSource of possibleSources) { + possibleSource.addCallback(() => { + sink.setData(source.data?.data); + + }) + } + return sink; + } + + public map(f: ((T) => J), + extraSources: UIEventSource[] = []): UIEventSource { + const self = this; + const update = function () { newSource.setData(f(self.data)); newSource.ping(); } - + this.addCallback(update); for (const extraSource of extraSources) { extraSource.addCallback(update); diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index a3912e0..c9a2bd5 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -21,6 +21,7 @@ export class UserBadge extends UIElement { pendingChanges: UIElement, basemap: Basemap) { super(userDetails); + this._userDetails = userDetails; this._pendingChanges = pendingChanges; this._basemap = basemap; diff --git a/test.ts b/test.ts index b7e9bd3..eb72b65 100644 --- a/test.ts +++ b/test.ts @@ -7,11 +7,33 @@ import {OsmLink} from "./Customizations/Questions/OsmLink"; import {ConfirmDialog} from "./UI/ConfirmDialog"; import {Imgur} from "./Logic/Imgur"; import {VariableUiElement} from "./UI/Base/VariableUIElement"; +import {UIRadioButton} from "./UI/Base/UIRadioButton"; +import {FixedInputElement} from "./UI/Base/FixedInputElement"; +import {TextField} from "./UI/Base/TextField"; -const html = new UIEventSource("Some text"); +const buttons = new UIRadioButton( + [new FixedInputElement("Five", 5), + new FixedInputElement("Ten", 10), + new TextField({ + fromString: (str) => parseInt(str), + toString: (i) => ("" + i), + }) + ] +).AttachTo("maindiv"); -const uielement = new VariableUiElement(html); -uielement.AttachTo("maindiv") +buttons.GetValue().addCallback(console.log); +buttons.GetValue().setData(10) -window.setTimeout(() => {html.setData("Different text")}, 1000) \ No newline at end of file +const value = new TextField({ + fromString: (str) => parseInt(str), + toString: (i) => { + if(isNaN(i)){ + return "" + } + return ("" + i) + }, +}).AttachTo("extradiv").GetValue(); + +value.setData(42); +value.addCallback(console.log) \ No newline at end of file From dc0da41fb1e901874f5f2263f176bc107820d9d5 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 20 Jul 2020 13:37:33 +0200 Subject: [PATCH 03/29] Checkbox example --- UI/Base/CheckBox.ts | 4 +++- test.ts | 13 ++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/UI/Base/CheckBox.ts b/UI/Base/CheckBox.ts index 6fd9214..9cdb21e 100644 --- a/UI/Base/CheckBox.ts +++ b/UI/Base/CheckBox.ts @@ -3,15 +3,17 @@ import {UIEventSource} from "../UIEventSource"; export class CheckBox extends UIElement{ + private data: UIEventSource; constructor(data: UIEventSource) { super(data); + this.data = data; } protected InnerRender(): string { - return ""; + return "Current val: "+this.data.data; } } \ No newline at end of file diff --git a/test.ts b/test.ts index b7e9bd3..8957dc0 100644 --- a/test.ts +++ b/test.ts @@ -7,11 +7,14 @@ import {OsmLink} from "./Customizations/Questions/OsmLink"; import {ConfirmDialog} from "./UI/ConfirmDialog"; import {Imgur} from "./Logic/Imgur"; import {VariableUiElement} from "./UI/Base/VariableUIElement"; +import {CheckBox} from "./UI/Base/CheckBox"; -const html = new UIEventSource("Some text"); +const eventSource = new UIEventSource(false); +eventSource.addCallback(console.log) -const uielement = new VariableUiElement(html); -uielement.AttachTo("maindiv") - -window.setTimeout(() => {html.setData("Different text")}, 1000) \ No newline at end of file +new CheckBox(eventSource) + .onClick(() => { + eventSource.setData(!eventSource.data); + }) + .AttachTo("maindiv"); From 93db813cfc76026901275ee7b806d4cc0fb4f0f5 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 20 Jul 2020 15:54:50 +0200 Subject: [PATCH 04/29] Intermediary refactoring --- Customizations/TagRendering.ts | 203 +++++++++++------- UI/AddButton.ts | 1 + UI/Base/FixedUiElement.ts | 2 +- UI/Base/UIInputElement.ts | 10 - UI/FeatureInfoBox.ts | 2 + UI/ImageUploadFlow.ts | 4 +- UI/{Base/DropDownUI.ts => Input/DropDown.ts} | 2 +- UI/{Base => Input}/FixedInputElement.ts | 10 +- UI/Input/InputElement.ts | 11 + UI/Input/InputElementWrapper.ts | 37 ++++ .../UIRadioButton.ts => Input/RadioButton.ts} | 86 ++++---- UI/{Base => Input}/TextField.ts | 47 ++-- UI/SearchAndGo.ts | 9 +- UI/UIElement.ts | 8 +- test.ts | 26 +-- 15 files changed, 280 insertions(+), 178 deletions(-) delete mode 100644 UI/Base/UIInputElement.ts rename UI/{Base/DropDownUI.ts => Input/DropDown.ts} (97%) rename UI/{Base => Input}/FixedInputElement.ts (70%) create mode 100644 UI/Input/InputElement.ts create mode 100644 UI/Input/InputElementWrapper.ts rename UI/{Base/UIRadioButton.ts => Input/RadioButton.ts} (56%) rename UI/{Base => Input}/TextField.ts (57%) diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index ea8ca32..2b9c8bf 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -1,17 +1,18 @@ import {UIElement} from "../UI/UIElement"; import {UIEventSource} from "../UI/UIEventSource"; import {And, Tag, TagsFilter, TagUtils} from "../Logic/TagsFilter"; -import {UIRadioButton} from "../UI/Base/UIRadioButton"; import {FixedUiElement} from "../UI/Base/FixedUiElement"; import {SaveButton} from "../UI/SaveButton"; import {Changes} from "../Logic/Changes"; -import {TextField} from "../UI/Base/TextField"; -import {UIInputElement} from "../UI/Base/UIInputElement"; -import {UIRadioButtonWithOther} from "../UI/Base/UIRadioButtonWithOther"; import {VariableUiElement} from "../UI/Base/VariableUIElement"; import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; import {OnlyShowIfConstructor} from "./OnlyShowIf"; import {UserDetails} from "../Logic/OsmConnection"; +import {TextField} from "../UI/Input/TextField"; +import {InputElement} from "../UI/Input/InputElement"; +import {InputElementWrapper} from "../UI/Input/InputElementWrapper"; +import {FixedInputElement} from "../UI/Input/FixedInputElement"; +import {RadioButton} from "../UI/Input/RadioButton"; export class TagRenderingOptions implements TagDependantUIElementConstructor { @@ -129,12 +130,9 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { class TagRendering extends UIElement implements TagDependantUIElement { - private _priority: number; private _userDetails: UIEventSource; + private _priority: number; - Priority(): number { - return this._priority; - } private _question: string; private _primer: string; @@ -150,8 +148,8 @@ class TagRendering extends UIElement implements TagDependantUIElement { extraTags?: TagsFilter }; - private readonly _questionElement: UIElement; - private readonly _textField: TextField; // Only here to update + + private readonly _questionElement: InputElement; private readonly _saveButton: UIElement; private readonly _skipButton: UIElement; @@ -238,60 +236,12 @@ class TagRendering extends UIElement implements TagDependantUIElement { } } - // Map radiobutton choice and textfield answer onto tagfilter. That tagfilter will be pushed into the changes later on - const pickChoice = (i => { - if (i === undefined || i === null) { - return undefined - } - return self._mapping[i].k - }); - const pickString = - (string) => { - if (string === "" || string === undefined) { - return undefined; - } - const tag = new Tag(self._freeform.key, string); - if (self._freeform.extraTags === undefined) { - return tag; - } - return new And([ - self._freeform.extraTags, - tag - ] - ); - }; - // Prepare the actual input element -> pick an appropriate implementation - let inputElement: UIInputElement; - - - if (this._freeform !== undefined && this._mapping !== undefined) { - // Radio buttons with 'other' - inputElement = new UIRadioButtonWithOther( - choices, - this._freeform.template, - this._freeform.placeholder, - pickChoice, - pickString - ); - this._questionElement = inputElement; - } else if (this._mapping !== [] && this._mapping.length > 0) { - // This is a classic radio selection element - inputElement = new UIRadioButton(new UIEventSource(choices), pickChoice, false) - this._questionElement = inputElement; - } else if (this._freeform !== undefined) { - this._textField = new TextField(new UIEventSource(this._freeform.placeholder), pickString); - inputElement = this._textField; - this._questionElement = new FixedUiElement( - "
" + this._freeform.template.replace("$$$", inputElement.Render()) + "
") - } else { - throw "Invalid questionRendering, expected at least choices or a freeform" - } - + this._questionElement = this.InputElementFor(options); const save = () => { - const selection = inputElement.GetValue().data; + const selection = self._questionElement.GetValue().data; if (selection) { changes.addTag(tags.data.id, selection); } @@ -305,21 +255,16 @@ class TagRendering extends UIElement implements TagDependantUIElement { } // Setup the save button and it's action - this._saveButton = new SaveButton(inputElement.GetValue()) + this._saveButton = new SaveButton(this._questionElement.GetValue()) .onClick(save); + this._editButton = new FixedUiElement(""); if (this._question !== undefined) { this._editButton = new FixedUiElement("edit") .onClick(() => { - console.log("Click", self._editButton); - if (self._textField) { - self._textField.value.setData(self._source.data["name"] ?? ""); - } - + self._questionElement.GetValue().setData(self.CurrentValue()); self._editMode.setData(true); }); - } else { - this._editButton = new FixedUiElement(""); } @@ -332,15 +277,95 @@ class TagRendering extends UIElement implements TagDependantUIElement { }); // And at last, set up the skip button this._skipButton = new VariableUiElement(cancelContents).onClick(cancel); + } + private InputElementFor(options: { + freeform?: { + key: string, template: string, + renderTemplate: string + placeholder?: string, + extraTags?: TagsFilter, + }, + mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[] + }): + InputElement { + + const elements = []; + + if (options.mappings !== undefined) { + for (const mapping of options.mappings) { + elements.push(this.InputElementForMapping(mapping)); + } + } + + if (options.freeform !== undefined) { + elements.push(this.InputForFreeForm(options.freeform)); + } + + + if (elements.length == 0) { + throw "NO TAGRENDERINGS!" + } + if (elements.length == 1) { + return elements[0]; + } + + return new RadioButton(elements, false); + } - private ApplyTemplate(template: string): string { - const tags = this._tagsPreprocessor(this._source.data); - return TagUtils.ApplyTemplate(template, tags); + + private InputElementForMapping(mapping: { k: TagsFilter, txt: string }) { + return new FixedInputElement(mapping.txt, mapping.k); } + + private InputForFreeForm(freeform): InputElement { + if (freeform === undefined) { + return undefined; + } + + + const pickString = + (string) => { + if (string === "" || string === undefined) { + return undefined; + } + const tag = new Tag(freeform.key, string); + if (freeform.extraTags === undefined) { + return tag; + } + return new And([ + freeform.extraTags, + tag + ] + ); + }; + + const toString = + (tag) => { + if (tag instanceof And) { + return toString(tag.and[0]) + } else if (tag instanceof Tag) { + return tag.value + } + return undefined; + } + + + let inputElement: InputElement; + const textField = new TextField({ + placeholder: this._freeform.placeholder, + fromString: pickString, + toString: toString + }); + + const prepost = freeform.template.split("$$$"); + return new InputElementWrapper(prepost[0], textField, prepost[1]); + } + + IsKnown(): boolean { const tags = TagUtils.proprtiesToKV(this._source.data); @@ -349,10 +374,26 @@ class TagRendering extends UIElement implements TagDependantUIElement { return true; } } - + return this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined; } + private CurrentValue(): TagsFilter { + const tags = TagUtils.proprtiesToKV(this._source.data); + + for (const oneOnOneElement of this._mapping.concat(this._renderMapping)) { + if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { + return oneOnOneElement.k; + } + } + if (this._freeform === undefined) { + return undefined; + } + + return new Tag(this._freeform.key, this._source.data[this._freeform.key]); + } + + IsQuestioning(): boolean { if (this.IsKnown()) { return false; @@ -430,7 +471,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { if(this._userDetails.data.loggedIn){ editButton = this._editButton.Render(); } - + return "" + "" + html + "" + editButton + @@ -441,13 +482,15 @@ class TagRendering extends UIElement implements TagDependantUIElement { } - InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); - this._questionElement.Update(); - this._saveButton.Update(); - this._skipButton.Update(); - this._textField?.Update(); - this._editButton.Update(); + + Priority(): number { + return this._priority; } + private ApplyTemplate(template: string): string { + const tags = this._tagsPreprocessor(this._source.data); + return TagUtils.ApplyTemplate(template, tags); + } + + } \ No newline at end of file diff --git a/UI/AddButton.ts b/UI/AddButton.ts index ac0a343..3b493a9 100644 --- a/UI/AddButton.ts +++ b/UI/AddButton.ts @@ -123,6 +123,7 @@ export class AddButton extends UIElement { const self = this; htmlElement.onclick = function (event) { + // @ts-ignore if(event.consumed){ return; } diff --git a/UI/Base/FixedUiElement.ts b/UI/Base/FixedUiElement.ts index 39fee94..6680b89 100644 --- a/UI/Base/FixedUiElement.ts +++ b/UI/Base/FixedUiElement.ts @@ -5,7 +5,7 @@ export class FixedUiElement extends UIElement { constructor(html: string) { super(undefined); - this._html = html; + this._html = html ?? ""; } protected InnerRender(): string { diff --git a/UI/Base/UIInputElement.ts b/UI/Base/UIInputElement.ts deleted file mode 100644 index fc4b3a6..0000000 --- a/UI/Base/UIInputElement.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {UIElement} from "../UIElement"; -import {UIEventSource} from "../UIEventSource"; - -export abstract class UIInputElement extends UIElement{ - - abstract GetValue() : UIEventSource; - - - -} \ No newline at end of file diff --git a/UI/FeatureInfoBox.ts b/UI/FeatureInfoBox.ts index 441c63a..47b5011 100644 --- a/UI/FeatureInfoBox.ts +++ b/UI/FeatureInfoBox.ts @@ -115,4 +115,6 @@ export class FeatureInfoBox extends UIElement { ""; } + + } diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 6542824..e332bc2 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -3,7 +3,7 @@ import {UIEventSource} from "./UIEventSource"; import $ from "jquery" import {Imgur} from "../Logic/Imgur"; import {UserDetails} from "../Logic/OsmConnection"; -import {DropDownUI} from "./Base/DropDownUI"; +import {DropDown} from "./Input/DropDown"; import {VariableUiElement} from "./Base/VariableUIElement"; export class ImageUploadFlow extends UIElement { @@ -31,7 +31,7 @@ export class ImageUploadFlow extends UIElement { this._uploadOptions = uploadOptions; this.ListenTo(this._isUploading); - const licensePicker = new DropDownUI("Jouw foto wordt gepubliceerd ", + const licensePicker = new DropDown("Jouw foto wordt gepubliceerd ", [ {value: "CC0", shown: "in het publiek domein"}, diff --git a/UI/Base/DropDownUI.ts b/UI/Input/DropDown.ts similarity index 97% rename from UI/Base/DropDownUI.ts rename to UI/Input/DropDown.ts index 208ea85..78e2896 100644 --- a/UI/Base/DropDownUI.ts +++ b/UI/Input/DropDown.ts @@ -1,7 +1,7 @@ import {UIEventSource} from "../UIEventSource"; import {UIElement} from "../UIElement"; -export class DropDownUI extends UIElement { +export class DropDown extends UIElement { selectedElement: UIEventSource private _label: string; diff --git a/UI/Base/FixedInputElement.ts b/UI/Input/FixedInputElement.ts similarity index 70% rename from UI/Base/FixedInputElement.ts rename to UI/Input/FixedInputElement.ts index 0d7cf0a..d3ea566 100644 --- a/UI/Base/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -1,10 +1,10 @@ -import {UIInputElement} from "./UIInputElement"; +import {InputElement} from "./InputElement"; import {UIEventSource} from "../UIEventSource"; import {UIElement} from "../UIElement"; -import {FixedUiElement} from "./FixedUiElement"; +import {FixedUiElement} from "../Base/FixedUiElement"; -export class FixedInputElement extends UIInputElement { +export class FixedInputElement extends InputElement { private rendering: UIElement; private value: UIEventSource; @@ -22,4 +22,8 @@ export class FixedInputElement extends UIInputElement { return this.rendering.Render(); } + IsValid(t: T): boolean { + return t === this.value.data; + } + } \ No newline at end of file diff --git a/UI/Input/InputElement.ts b/UI/Input/InputElement.ts new file mode 100644 index 0000000..62e0969 --- /dev/null +++ b/UI/Input/InputElement.ts @@ -0,0 +1,11 @@ +import {UIElement} from "../UIElement"; +import {UIEventSource} from "../UIEventSource"; +import {FixedUiElement} from "../Base/FixedUiElement"; + +export abstract class InputElement extends UIElement{ + + abstract GetValue() : UIEventSource; + + abstract IsValid(t: T) : boolean; + +} \ No newline at end of file diff --git a/UI/Input/InputElementWrapper.ts b/UI/Input/InputElementWrapper.ts new file mode 100644 index 0000000..e04a4f1 --- /dev/null +++ b/UI/Input/InputElementWrapper.ts @@ -0,0 +1,37 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../UIEventSource"; +import {UIElement} from "../UIElement"; +import {FixedUiElement} from "../Base/FixedUiElement"; + + +export class InputElementWrapper extends InputElement{ + private pre: UIElement ; + private input: InputElement; + private post: UIElement ; + + constructor( + pre: UIElement | string, + input: InputElement, + post: UIElement | string + + ) { + super(undefined); + this.pre = typeof(pre) === 'string' ? new FixedUiElement(pre) : pre + this.input = input; + this.post =typeof(post) === 'string' ? new FixedUiElement(post) : post + } + + + GetValue(): UIEventSource { + return this.input.GetValue(); + } + + protected InnerRender(): string { + return this.pre.Render() + this.input.Render() + this.post.Render(); + } + + IsValid(t: T): boolean { + return this.input.IsValid(t); + } + +} \ No newline at end of file diff --git a/UI/Base/UIRadioButton.ts b/UI/Input/RadioButton.ts similarity index 56% rename from UI/Base/UIRadioButton.ts rename to UI/Input/RadioButton.ts index 12a1e24..b463633 100644 --- a/UI/Base/UIRadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -1,31 +1,27 @@ import {UIElement} from "../UIElement"; import {UIEventSource} from "../UIEventSource"; -import {UIInputElement} from "./UIInputElement"; +import {InputElement} from "./InputElement"; -export class UIRadioButton extends UIInputElement { +export class RadioButton extends InputElement { - public readonly SelectedElementIndex: UIEventSource + private readonly _selectedElementIndex: UIEventSource = new UIEventSource(null); private value: UIEventSource; - private readonly _elements: UIInputElement[] + private readonly _elements: InputElement[] private _selectFirstAsDefault: boolean; - private _valueMapping: (i: number) => T; - constructor(elements: UIInputElement[], + constructor(elements: InputElement[], selectFirstAsDefault = true) { super(undefined); this._elements = elements; this._selectFirstAsDefault = selectFirstAsDefault; const self = this; - this.SelectedElementIndex.addCallback(() => { - self.InnerUpdate(undefined); - }) this.value = - UIEventSource.flatten(this.SelectedElementIndex.map( + UIEventSource.flatten(this._selectedElementIndex.map( (selectedIndex) => { if (selectedIndex !== undefined && selectedIndex !== null) { return elements[selectedIndex].GetValue() @@ -34,16 +30,29 @@ export class UIRadioButton extends UIInputElement { ), elements.map(e => e.GetValue())) ; + this.value.addCallback((t) => { + self.SetCorrectValue(t); + }) - for (let i = 0; i < elements.length; i ++){ - elements[i].onClick(( ) => { - self.SelectedElementIndex.setData(i); + + for (let i = 0; i < elements.length; i++) { + // If an element is clicked, the radio button corresponding with it should be selected as well + elements[i].onClick(() => { + self._selectedElementIndex.setData(i); }); } - } - + + IsValid(t: T): boolean { + for (const inputElement of this._elements) { + if (inputElement.IsValid(t)) { + return true; + } + } + return false; + } + GetValue(): UIEventSource { return this.value; } @@ -70,6 +79,24 @@ export class UIRadioButton extends UIInputElement { return "
" + body + "
"; } + private SetCorrectValue(t: T) { + if (t === undefined) { + return; + } + // We check that what is selected matches the previous rendering + for (let i = 0; i < this._elements.length; i++) { + const e = this._elements[i]; + if (e.IsValid(t)) { + this._selectedElementIndex.setData(i); + e.GetValue().setData(t); + // @ts-ignore + document.getElementById(this.IdFor(i)).checked = true; + return; + } + + } + } + InnerUpdate(htmlElement: HTMLElement) { const self = this; @@ -78,7 +105,7 @@ export class UIRadioButton extends UIInputElement { const el = document.getElementById(self.IdFor(i)); // @ts-ignore if (el.checked) { - self.SelectedElementIndex.setData(i); + self._selectedElementIndex.setData(i); } } } @@ -91,8 +118,9 @@ export class UIRadioButton extends UIInputElement { } ); - if (this.SelectedElementIndex.data == null) { - if (this._selectFirstAsDefault) { + if (this._selectFirstAsDefault) { + this.SetCorrectValue(this.value.data); + if (this._selectedElementIndex.data === null || this._selectedElementIndex.data === undefined) { const el = document.getElementById(this.IdFor(0)); if (el) { // @ts-ignore @@ -100,30 +128,10 @@ export class UIRadioButton extends UIInputElement { checkButtons(); } } - } else { - - // We check that what is selected matches the previous rendering - var checked = -1; - var expected = this.SelectedElementIndex.data; - if (expected) { - - for (let i = 0; i < self._elements.length; i++) { - const el = document.getElementById(self.IdFor(i)); - // @ts-ignore - if (el.checked) { - checked = i; - } - } - if (expected != checked) { - const el = document.getElementById(this.IdFor(expected)); - // @ts-ignore - el.checked = true; - } - } } - } + }; } \ No newline at end of file diff --git a/UI/Base/TextField.ts b/UI/Input/TextField.ts similarity index 57% rename from UI/Base/TextField.ts rename to UI/Input/TextField.ts index 31560d2..32ffb1c 100644 --- a/UI/Base/TextField.ts +++ b/UI/Input/TextField.ts @@ -1,9 +1,10 @@ import {UIElement} from "../UIElement"; import {UIEventSource} from "../UIEventSource"; -import {UIInputElement} from "./UIInputElement"; +import {InputElement} from "./InputElement"; +import {FixedUiElement} from "../Base/FixedUiElement"; -export class TextField extends UIInputElement { +export class TextField extends InputElement { private value: UIEventSource; private mappedValue: UIEventSource; @@ -11,28 +12,32 @@ export class TextField extends UIInputElement { * Pings and has the value data */ public enterPressed = new UIEventSource(undefined); - private _placeholder: UIEventSource; - private _pretext: string; - private _fromString: (string: string) => T; + private _placeholder: UIElement; + private _fromString?: (string: string) => T; + private _toString: (t: T) => string; constructor(options: { - placeholder?: UIEventSource, - toString: (t: T) => string, - fromString: (string: string) => T, - pretext?: string, + placeholder?: string | UIElement, + toString?: (t: T) => string, + fromString?: (string: string) => T, value?: UIEventSource }) { - super(options?.placeholder); + super(undefined); this.value = new UIEventSource(""); this.mappedValue = options?.value ?? new UIEventSource(undefined); + // @ts-ignore + this._fromString = options.fromString ?? ((str) => (str)) this.value.addCallback((str) => this.mappedValue.setData(options.fromString(str))); this.mappedValue.addCallback((t) => this.value.setData(options.toString(t))); - this._placeholder = options?.placeholder ?? new UIEventSource(""); - this._pretext = options?.pretext ?? ""; + this._placeholder = + typeof(options.placeholder) === "string" ? new FixedUiElement(options.placeholder) : + (options.placeholder ?? new FixedUiElement("")); + this._toString = options.toString ?? ((t) => ("" + t)); + const self = this; this.mappedValue.addCallback((t) => { @@ -40,9 +45,10 @@ export class TextField extends UIInputElement { return; } const field = document.getElementById('text-' + this.id); - if (field === undefined && field === null) { + if (field === undefined || field === null) { return; } + // @ts-ignore field.value = options.toString(t); }) } @@ -52,13 +58,16 @@ export class TextField extends UIInputElement { } protected InnerRender(): string { - return this._pretext + "
" + - "" + + return "" + + "" + "
"; } InnerUpdate(htmlElement: HTMLElement) { const field = document.getElementById('text-' + this.id); + if(field === null){ + return; + } const self = this; field.oninput = () => { // @ts-ignore @@ -75,6 +84,14 @@ export class TextField extends UIInputElement { } + IsValid(t: T): boolean { + if(t === undefined || t === null){ + return false; + } + const result = this._toString(t); + return result !== undefined && result !== null; + } + Clear() { const field = document.getElementById('text-' + this.id); if (field !== undefined) { diff --git a/UI/SearchAndGo.ts b/UI/SearchAndGo.ts index 3916168..1ddf995 100644 --- a/UI/SearchAndGo.ts +++ b/UI/SearchAndGo.ts @@ -1,5 +1,5 @@ import {UIElement} from "./UIElement"; -import {TextField} from "./Base/TextField"; +import {TextField} from "./Input/TextField"; import {UIEventSource} from "./UIEventSource"; import {FixedUiElement} from "./Base/FixedUiElement"; import {Geocoding} from "../Logic/Geocoding"; @@ -9,7 +9,10 @@ import {Basemap} from "../Logic/Basemap"; export class SearchAndGo extends UIElement { private _placeholder = new UIEventSource("Zoek naar een locatie...") - private _searchField = new TextField(this._placeholder); + private _searchField = new TextField({ + placeholder: this._placeholder + } + ); private _foundEntries = new UIEventSource([]); private _map: Basemap; @@ -33,7 +36,7 @@ export class SearchAndGo extends UIElement { // Triggered by 'enter' or onclick private RunSearch() { - const searchString = this._searchField.value.data; + const searchString = this._searchField.GetValue().data; this._searchField.Clear(); this._placeholder.setData("Bezig met zoeken..."); const self = this; diff --git a/UI/UIElement.ts b/UI/UIElement.ts index c3a9fdd..b4bdd69 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -1,8 +1,7 @@ import {UIEventSource} from "./UIEventSource"; -import instantiate = WebAssembly.instantiate; export abstract class UIElement { - + private static nextId: number = 0; public readonly id: string; @@ -35,7 +34,7 @@ export abstract class UIElement { this.Update(); return this; } - + Update(): void { let element = document.getElementById(this.id); if (element === null || element === undefined) { @@ -121,5 +120,6 @@ export abstract class UIElement { public IsEmpty(): boolean { return this.InnerRender() === ""; } +} + -} \ No newline at end of file diff --git a/test.ts b/test.ts index eb72b65..d71a3ff 100644 --- a/test.ts +++ b/test.ts @@ -7,33 +7,19 @@ import {OsmLink} from "./Customizations/Questions/OsmLink"; import {ConfirmDialog} from "./UI/ConfirmDialog"; import {Imgur} from "./Logic/Imgur"; import {VariableUiElement} from "./UI/Base/VariableUIElement"; -import {UIRadioButton} from "./UI/Base/UIRadioButton"; -import {FixedInputElement} from "./UI/Base/FixedInputElement"; -import {TextField} from "./UI/Base/TextField"; +import {TextField} from "./UI/Input/TextField"; +import {FixedInputElement} from "./UI/Input/FixedInputElement"; +import {RadioButton} from "./UI/Input/RadioButton"; -const buttons = new UIRadioButton( +const buttons = new RadioButton( [new FixedInputElement("Five", 5), new FixedInputElement("Ten", 10), new TextField({ fromString: (str) => parseInt(str), toString: (i) => ("" + i), }) - ] + ], false ).AttachTo("maindiv"); -buttons.GetValue().addCallback(console.log); -buttons.GetValue().setData(10) - -const value = new TextField({ - fromString: (str) => parseInt(str), - toString: (i) => { - if(isNaN(i)){ - return "" - } - return ("" + i) - }, -}).AttachTo("extradiv").GetValue(); - -value.setData(42); -value.addCallback(console.log) \ No newline at end of file +buttons.GetValue().addCallback(console.log); \ No newline at end of file From 069cddf034762434937095553ad3089af2c3e988 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 20 Jul 2020 15:59:48 +0200 Subject: [PATCH 05/29] Intermediate refactoring --- Customizations/AllKnownLayouts.js | 51 +++ Customizations/LayerDefinition.js | 33 ++ Customizations/Layers/Artwork.js | 87 +++++ Customizations/Layers/BikeParkings.js | 58 ++++ Customizations/Layers/BikeStations.js | 97 ++++++ Customizations/Layers/Birdhide.js | 145 ++++++++ Customizations/Layers/Bookcases.js | 167 +++++++++ Customizations/Layers/Bos.js | 85 +++++ Customizations/Layers/DrinkingWater.js | 71 ++++ Customizations/Layers/GhostBike.js | 80 +++++ Customizations/Layers/GrbToFix.js | 90 +++++ Customizations/Layers/InformationBoard.js | 122 +++++++ Customizations/Layers/Map.js | 99 ++++++ Customizations/Layers/NatureReserves.js | 134 ++++++++ Customizations/Layers/Park.js | 108 ++++++ Customizations/Layers/Toilets.js | 94 +++++ Customizations/Layers/Widths.js | 270 +++++++++++++++ Customizations/Layout.js | 38 +++ Customizations/Layouts/All.js | 26 ++ Customizations/Layouts/Bookcases.js | 36 ++ Customizations/Layouts/Cyclofix.js | 35 ++ Customizations/Layouts/GRB.js | 28 ++ Customizations/Layouts/Groen.js | 56 +++ Customizations/Layouts/MetaMap.js | 27 ++ Customizations/Layouts/Natuurpunt.js | 28 ++ Customizations/Layouts/Statues.js | 31 ++ Customizations/Layouts/StreetWidth.js | 37 ++ Customizations/Layouts/Toilets.js | 31 ++ Customizations/Layouts/WalkByBrussels.js | 33 ++ Customizations/OnlyShowIf.js | 89 +++++ Customizations/Questions/AccessTag.js | 50 +++ .../Questions/DescriptionQuestion.js | 36 ++ Customizations/Questions/FixedText.js | 30 ++ Customizations/Questions/NameInline.js | 41 +++ Customizations/Questions/NameQuestion.js | 48 +++ Customizations/Questions/OperatorTag.js | 41 +++ Customizations/Questions/OsmLink.js | 40 +++ Customizations/Questions/WikipediaLink.js | 59 ++++ Customizations/Questions/bike/ParkingType.js | 52 +++ .../Questions/bike/PumpManometer.js | 32 ++ Customizations/Questions/bike/PumpManual.js | 32 ++ .../Questions/bike/PumpOperational.js | 31 ++ Customizations/Questions/bike/PumpValves.js | 41 +++ Customizations/Questions/bike/StationBrand.js | 44 +++ Customizations/Questions/bike/StationChain.js | 32 ++ .../Questions/bike/StationOperator.js | 41 +++ .../Questions/bike/StationPumpTools.js | 33 ++ Customizations/Questions/bike/StationStand.js | 32 ++ Customizations/TagRendering.js | 321 ++++++++++++++++++ Customizations/UIElementConstructor.js | 25 ++ Helpers.js | 57 ++++ Logic/Basemap.js | 70 ++++ Logic/Changes.js | 222 ++++++++++++ Logic/ElementStorage.js | 51 +++ Logic/FilteredLayer.js | 173 ++++++++++ Logic/GeoLocationHandler.js | 117 +++++++ Logic/GeoOperations.js | 185 ++++++++++ Logic/Geocoding.js | 22 ++ Logic/ImageSearcher.js | 185 ++++++++++ Logic/Imgur.js | 90 +++++ Logic/LayerUpdater.js | 99 ++++++ Logic/OsmConnection.js | 256 ++++++++++++++ Logic/OsmImageUploadHandler.js | 56 +++ Logic/OsmObject.js | 160 +++++++++ Logic/Overpass.js | 47 +++ Logic/StrayClickHandler.js | 42 +++ Logic/TagsFilter.js | 240 +++++++++++++ Logic/Wikimedia.js | 135 ++++++++ Quests.js | 40 +++ UI/AddButton.js | 129 +++++++ UI/Base/Button.js | 49 +++ UI/Base/CollapseButton.js | 57 ++++ UI/Base/FixedUiElement.js | 30 ++ UI/Base/VariableUIElement.js | 38 +++ UI/Base/VerticalCombine.js | 45 +++ UI/CenterMessageBox.js | 72 ++++ UI/ConfirmDialog.js | 67 ++++ UI/FeatureInfoBox.js | 89 +++++ UI/Image/ImageCarousel.js | 112 ++++++ UI/Image/ImageCarouselWithUpload.js | 74 ++++ UI/Image/ImgurImage.js | 59 ++++ UI/Image/SimpleImageElement.js | 28 ++ UI/Image/WikimediaImage.js | 59 ++++ UI/ImageUploadFlow.js | 107 ++++++ UI/Img.js | 16 + UI/Input/DropDown.js | 69 ++++ UI/Input/FixedInputElement.js | 39 +++ UI/Input/InputElement.js | 25 ++ UI/Input/InputElementWrapper.js | 39 +++ UI/Input/RadioButton.js | 122 +++++++ UI/Input/TextField.js | 94 +++++ UI/MessageBoxHandler.js | 56 +++ UI/PendingChanges.js | 49 +++ UI/QuestionPicker.js | 51 +++ UI/SaveButton.js | 38 +++ UI/SearchAndGo.js | 83 +++++ UI/SimpleAddUI.js | 84 +++++ UI/SlideShow.js | 92 +++++ UI/UIElement.js | 115 +++++++ UI/UIEventSource.js | 58 ++++ UI/UserBadge.js | 112 ++++++ index.js | 187 ++++++++++ test.js | 12 + 103 files changed, 7950 insertions(+) create mode 100644 Customizations/AllKnownLayouts.js create mode 100644 Customizations/LayerDefinition.js create mode 100644 Customizations/Layers/Artwork.js create mode 100644 Customizations/Layers/BikeParkings.js create mode 100644 Customizations/Layers/BikeStations.js create mode 100644 Customizations/Layers/Birdhide.js create mode 100644 Customizations/Layers/Bookcases.js create mode 100644 Customizations/Layers/Bos.js create mode 100644 Customizations/Layers/DrinkingWater.js create mode 100644 Customizations/Layers/GhostBike.js create mode 100644 Customizations/Layers/GrbToFix.js create mode 100644 Customizations/Layers/InformationBoard.js create mode 100644 Customizations/Layers/Map.js create mode 100644 Customizations/Layers/NatureReserves.js create mode 100644 Customizations/Layers/Park.js create mode 100644 Customizations/Layers/Toilets.js create mode 100644 Customizations/Layers/Widths.js create mode 100644 Customizations/Layout.js create mode 100644 Customizations/Layouts/All.js create mode 100644 Customizations/Layouts/Bookcases.js create mode 100644 Customizations/Layouts/Cyclofix.js create mode 100644 Customizations/Layouts/GRB.js create mode 100644 Customizations/Layouts/Groen.js create mode 100644 Customizations/Layouts/MetaMap.js create mode 100644 Customizations/Layouts/Natuurpunt.js create mode 100644 Customizations/Layouts/Statues.js create mode 100644 Customizations/Layouts/StreetWidth.js create mode 100644 Customizations/Layouts/Toilets.js create mode 100644 Customizations/Layouts/WalkByBrussels.js create mode 100644 Customizations/OnlyShowIf.js create mode 100644 Customizations/Questions/AccessTag.js create mode 100644 Customizations/Questions/DescriptionQuestion.js create mode 100644 Customizations/Questions/FixedText.js create mode 100644 Customizations/Questions/NameInline.js create mode 100644 Customizations/Questions/NameQuestion.js create mode 100644 Customizations/Questions/OperatorTag.js create mode 100644 Customizations/Questions/OsmLink.js create mode 100644 Customizations/Questions/WikipediaLink.js create mode 100644 Customizations/Questions/bike/ParkingType.js create mode 100644 Customizations/Questions/bike/PumpManometer.js create mode 100644 Customizations/Questions/bike/PumpManual.js create mode 100644 Customizations/Questions/bike/PumpOperational.js create mode 100644 Customizations/Questions/bike/PumpValves.js create mode 100644 Customizations/Questions/bike/StationBrand.js create mode 100644 Customizations/Questions/bike/StationChain.js create mode 100644 Customizations/Questions/bike/StationOperator.js create mode 100644 Customizations/Questions/bike/StationPumpTools.js create mode 100644 Customizations/Questions/bike/StationStand.js create mode 100644 Customizations/TagRendering.js create mode 100644 Customizations/UIElementConstructor.js create mode 100644 Helpers.js create mode 100644 Logic/Basemap.js create mode 100644 Logic/Changes.js create mode 100644 Logic/ElementStorage.js create mode 100644 Logic/FilteredLayer.js create mode 100644 Logic/GeoLocationHandler.js create mode 100644 Logic/GeoOperations.js create mode 100644 Logic/Geocoding.js create mode 100644 Logic/ImageSearcher.js create mode 100644 Logic/Imgur.js create mode 100644 Logic/LayerUpdater.js create mode 100644 Logic/OsmConnection.js create mode 100644 Logic/OsmImageUploadHandler.js create mode 100644 Logic/OsmObject.js create mode 100644 Logic/Overpass.js create mode 100644 Logic/StrayClickHandler.js create mode 100644 Logic/TagsFilter.js create mode 100644 Logic/Wikimedia.js create mode 100644 Quests.js create mode 100644 UI/AddButton.js create mode 100644 UI/Base/Button.js create mode 100644 UI/Base/CollapseButton.js create mode 100644 UI/Base/FixedUiElement.js create mode 100644 UI/Base/VariableUIElement.js create mode 100644 UI/Base/VerticalCombine.js create mode 100644 UI/CenterMessageBox.js create mode 100644 UI/ConfirmDialog.js create mode 100644 UI/FeatureInfoBox.js create mode 100644 UI/Image/ImageCarousel.js create mode 100644 UI/Image/ImageCarouselWithUpload.js create mode 100644 UI/Image/ImgurImage.js create mode 100644 UI/Image/SimpleImageElement.js create mode 100644 UI/Image/WikimediaImage.js create mode 100644 UI/ImageUploadFlow.js create mode 100644 UI/Img.js create mode 100644 UI/Input/DropDown.js create mode 100644 UI/Input/FixedInputElement.js create mode 100644 UI/Input/InputElement.js create mode 100644 UI/Input/InputElementWrapper.js create mode 100644 UI/Input/RadioButton.js create mode 100644 UI/Input/TextField.js create mode 100644 UI/MessageBoxHandler.js create mode 100644 UI/PendingChanges.js create mode 100644 UI/QuestionPicker.js create mode 100644 UI/SaveButton.js create mode 100644 UI/SearchAndGo.js create mode 100644 UI/SimpleAddUI.js create mode 100644 UI/SlideShow.js create mode 100644 UI/UIElement.js create mode 100644 UI/UIEventSource.js create mode 100644 UI/UserBadge.js create mode 100644 index.js create mode 100644 test.js diff --git a/Customizations/AllKnownLayouts.js b/Customizations/AllKnownLayouts.js new file mode 100644 index 0000000..b1d47be --- /dev/null +++ b/Customizations/AllKnownLayouts.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AllKnownLayouts = void 0; +var Groen_1 = require("./Layouts/Groen"); +var GRB_1 = require("./Layouts/GRB"); +var Bookcases_1 = require("./Layouts/Bookcases"); +var Cyclofix_1 = require("./Layouts/Cyclofix"); +var WalkByBrussels_1 = require("./Layouts/WalkByBrussels"); +var All_1 = require("./Layouts/All"); +var MetaMap_1 = require("./Layouts/MetaMap"); +var StreetWidth_1 = require("./Layouts/StreetWidth"); +var Natuurpunt_1 = require("./Layouts/Natuurpunt"); +var AllKnownLayouts = /** @class */ (function () { + function AllKnownLayouts() { + } + AllKnownLayouts.AllLayouts = function () { + var all = new All_1.All(); + var layouts = [ + new Groen_1.Groen(), + new GRB_1.GRB(), + new Cyclofix_1.default(), + new Bookcases_1.Bookcases(), + new WalkByBrussels_1.WalkByBrussels(), + new MetaMap_1.MetaMap(), + new StreetWidth_1.StreetWidth(), + new Natuurpunt_1.Natuurpunt(), + all + /*new Toilets(), + new Statues(), + */ + ]; + var allSets = {}; + for (var _i = 0, layouts_1 = layouts; _i < layouts_1.length; _i++) { + var layout = layouts_1[_i]; + allSets[layout.name] = layout; + all.layers = all.layers.concat(layout.layers); + } + return allSets; + }; + AllKnownLayouts.GetSets = function (layoutNames) { + var all = new All_1.All(); + for (var _i = 0, layoutNames_1 = layoutNames; _i < layoutNames_1.length; _i++) { + var name_1 = layoutNames_1[_i]; + all.layers = all.layers.concat(AllKnownLayouts.allSets[name_1].layers); + } + return all; + }; + AllKnownLayouts.allSets = AllKnownLayouts.AllLayouts(); + return AllKnownLayouts; +}()); +exports.AllKnownLayouts = AllKnownLayouts; diff --git a/Customizations/LayerDefinition.js b/Customizations/LayerDefinition.js new file mode 100644 index 0000000..d646849 --- /dev/null +++ b/Customizations/LayerDefinition.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LayerDefinition = void 0; +var FilteredLayer_1 = require("../Logic/FilteredLayer"); +var LayerDefinition = /** @class */ (function () { + function LayerDefinition(options) { + if (options === void 0) { options = undefined; } + var _a; + /** + * If an object of the next layer is contained for this many percent in this feature, it is eaten and not shown + */ + this.maxAllowedOverlapPercentage = undefined; + if (options === undefined) { + console.log("No options!"); + return; + } + this.name = options.name; + this.maxAllowedOverlapPercentage = (_a = options.maxAllowedOverlapPercentage) !== null && _a !== void 0 ? _a : 0; + this.newElementTags = options.newElementTags; + this.icon = options.icon; + this.minzoom = options.minzoom; + this.overpassFilter = options.overpassFilter; + this.title = options.title; + this.elementsToShow = options.elementsToShow; + this.style = options.style; + console.log(this); + } + LayerDefinition.prototype.asLayer = function (basemap, allElements, changes, userDetails, selectedElement, showOnPopup) { + return new FilteredLayer_1.FilteredLayer(this.name, basemap, allElements, changes, this.overpassFilter, this.maxAllowedOverlapPercentage, this.style, selectedElement, showOnPopup); + }; + return LayerDefinition; +}()); +exports.LayerDefinition = LayerDefinition; diff --git a/Customizations/Layers/Artwork.js b/Customizations/Layers/Artwork.js new file mode 100644 index 0000000..34d7371 --- /dev/null +++ b/Customizations/Layers/Artwork.js @@ -0,0 +1,87 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Artwork = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var Question_1 = require("../../Logic/Question"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var leaflet_1 = require("leaflet"); +var Artwork = /** @class */ (function (_super) { + __extends(Artwork, _super); + function Artwork() { + var _this = _super.call(this) || this; + _this.name = "artwork"; + _this.newElementTags = [new TagsFilter_1.Tag("tourism", "artwork")]; + _this.icon = "./assets/statue.svg"; + _this.overpassFilter = new TagsFilter_1.Tag("tourism", "artwork"); + _this.minzoom = 13; + _this.questions = [ + Question_1.QuestionDefinition.radioAndTextQuestion("What kind of artwork is this?", 10, "artwork_type", [ + { text: "A statue", value: "statue" }, + { text: "A bust (thus a statue, but only of the head and shoulders)", value: "bust" }, + { text: "A sculpture", value: "sculpture" }, + { text: "A mural painting", value: "mural" }, + { text: "A painting", value: "painting" }, + { text: "A graffiti", value: "graffiti" }, + { text: "A relief", value: "relief" }, + { text: "An installation", value: "installation" } + ]), + Question_1.QuestionDefinition.textQuestion("Whom or what is depicted in this statue?", "subject", 20).addUnrequiredTag("subject:wikidata", "*"), + Question_1.QuestionDefinition.textQuestion("Is there an inscription on this artwork?", "inscription", 16), + Question_1.QuestionDefinition.textQuestion("What is the name of this artwork? If there is no explicit name, skip the question", "name", 15), + ]; + _this.style = function (tags) { + return { + icon: new leaflet_1.default.icon({ + iconUrl: "assets/statue.svg", + iconSize: [40, 40], + text: "hi" + }), + color: "#0000ff" + }; + }; + _this.elementsToShow = [ + new TagMappingOptions({ + key: "name", + template: "

Artwork '{name}'

", + missing: "Artwork" + }), + new TagMappingOptions({ + key: "artwork_type", + template: "This artwork is a {artwork_type}" + }), + new TagMappingOptions({ + key: "artist_name", + template: "This artwork was made by {artist_name}" + }), + new TagMappingOptions({ + key: "subject", + template: "This artwork depicts {subject}" + }), + new TagMappingOptions({ + key: "subject:wikidata", + template: "See more data about the subject" + }), + new TagMappingOptions({ + key: "website", + template: "Website of the statue" + }), + new TagMappingOptions({ key: "image", template: "image" }) + ]; + return _this; + } + return Artwork; +}(LayerDefinition_1.LayerDefinition)); +exports.Artwork = Artwork; diff --git a/Customizations/Layers/BikeParkings.js b/Customizations/Layers/BikeParkings.js new file mode 100644 index 0000000..95c1208 --- /dev/null +++ b/Customizations/Layers/BikeParkings.js @@ -0,0 +1,58 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var OperatorTag_1 = require("../Questions/OperatorTag"); +var L = require("leaflet"); +var FixedText_1 = require("../Questions/FixedText"); +var ParkingType_1 = require("../Questions/bike/ParkingType"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var BikeParkings = /** @class */ (function (_super) { + __extends(BikeParkings, _super); + function BikeParkings() { + var _this = _super.call(this) || this; + _this.name = "bike_parking"; + _this.icon = "./assets/bike/parking.svg"; + _this.overpassFilter = new TagsFilter_1.Tag("amenity", "bicycle_parking"); + _this.newElementTags = [ + new TagsFilter_1.Tag("amenity", "bicycle_parking"), + ]; + _this.maxAllowedOverlapPercentage = 10; + _this.minzoom = 13; + _this.style = _this.generateStyleFunction(); + _this.title = new FixedText_1.default("Fietsparking"); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new OperatorTag_1.OperatorTag(), + new ParkingType_1.default() + ]; + return _this; + } + BikeParkings.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + return { + color: "#00bb00", + icon: L.icon({ + iconUrl: self.icon, + iconSize: [50, 50] + }) + }; + }; + }; + return BikeParkings; +}(LayerDefinition_1.LayerDefinition)); +exports.default = BikeParkings; diff --git a/Customizations/Layers/BikeStations.js b/Customizations/Layers/BikeStations.js new file mode 100644 index 0000000..fcd7aa9 --- /dev/null +++ b/Customizations/Layers/BikeStations.js @@ -0,0 +1,97 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var L = require("leaflet"); +var StationChain_1 = require("../Questions/bike/StationChain"); +var StationPumpTools_1 = require("../Questions/bike/StationPumpTools"); +var StationStand_1 = require("../Questions/bike/StationStand"); +var PumpManual_1 = require("../Questions/bike/PumpManual"); +var StationOperator_1 = require("../Questions/bike/StationOperator"); +var FixedText_1 = require("../Questions/FixedText"); +var PumpManometer_1 = require("../Questions/bike/PumpManometer"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var PumpOperational_1 = require("../Questions/bike/PumpOperational"); +var PumpValves_1 = require("../Questions/bike/PumpValves"); +var BikeStations = /** @class */ (function (_super) { + __extends(BikeStations, _super); + function BikeStations() { + var _this = _super.call(this) || this; + _this.pump = new TagsFilter_1.Tag("service:bicycle:pump", "yes"); + _this.pumpOperationalAny = new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "yes"); + _this.pumpOperationalOk = new TagsFilter_1.Or([new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "yes"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "operational"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "ok"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "")]); + _this.tools = new TagsFilter_1.Tag("service:bicycle:tools", "yes"); + _this.name = "bike station or pump"; + _this.icon = "./assets/wrench.svg"; + _this.overpassFilter = new TagsFilter_1.And([ + new TagsFilter_1.Tag("amenity", "bicycle_repair_station") + ]); + _this.newElementTags = [ + new TagsFilter_1.Tag("amenity", "bicycle_repair_station") + ]; + _this.maxAllowedOverlapPercentage = 10; + _this.minzoom = 13; + _this.style = _this.generateStyleFunction(); + _this.title = new FixedText_1.default("Bike station"); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new StationPumpTools_1.default(), + new StationChain_1.default().OnlyShowIf(_this.tools), + new StationStand_1.default().OnlyShowIf(_this.tools), + new PumpManual_1.default().OnlyShowIf(_this.pump), + new PumpManometer_1.default().OnlyShowIf(_this.pump), + new PumpValves_1.default().OnlyShowIf(_this.pump), + new PumpOperational_1.default().OnlyShowIf(_this.pump), + new StationOperator_1.default(), + ]; + return _this; + } + BikeStations.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + var hasPump = self.pump.matchesProperties(properties); + var isOperational = self.pumpOperationalOk.matchesProperties(properties); + var hasTools = self.tools.matchesProperties(properties); + var iconName = ""; + if (hasPump) { + if (hasTools) { + iconName = "repair_station_pump.svg"; + } + else { + if (isOperational) { + iconName = "pump.svg"; + } + else { + iconName = "pump_broken.svg"; + } + } + } + else { + iconName = "repair_station.svg"; + } + var iconUrl = "./assets/bike/" + iconName; + return { + color: "#00bb00", + icon: L.icon({ + iconUrl: iconUrl, + iconSize: [50, 50] + }) + }; + }; + }; + return BikeStations; +}(LayerDefinition_1.LayerDefinition)); +exports.default = BikeStations; diff --git a/Customizations/Layers/Birdhide.js b/Customizations/Layers/Birdhide.js new file mode 100644 index 0000000..f7ca70b --- /dev/null +++ b/Customizations/Layers/Birdhide.js @@ -0,0 +1,145 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Birdhide = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var FixedText_1 = require("../Questions/FixedText"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var leaflet_1 = require("leaflet"); +var Birdhide = /** @class */ (function (_super) { + __extends(Birdhide, _super); + function Birdhide() { + var _this = _super.call(this, { + name: "vogelkijkplaats", + overpassFilter: Birdhide.birdhide, + elementsToShow: [new FixedText_1.default("hi")], + icon: "assets/nature/birdhide.svg", + minzoom: 12, + newElementTags: [Birdhide.birdhide], + style: function (tags) { + return { color: "", icon: undefined }; + }, + }) || this; + function rmStart(toRemove, title) { + if (title.toLowerCase().indexOf(toRemove.toLowerCase()) == 0) { + return title.substr(toRemove.length).trim(); + } + return title; + } + function rmStarts(toRemove, title) { + for (var _i = 0, toRemove_1 = toRemove; _i < toRemove_1.length; _i++) { + var toRm = toRemove_1[_i]; + title = rmStart(toRm, title); + } + return title; + } + _this.title = new TagRendering_1.TagRenderingOptions({ + tagsPreprocessor: function (tags) { + if (tags.name) { + var nm = rmStarts(["Vogelkijkhut", "Vogelkijkwand", "Kijkwand", "Kijkhut"], tags.name); + tags.name = " '" + nm + "'"; + } + else { + tags.name = ""; + } + }, + mappings: [ + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("shelter", "no"), new TagsFilter_1.Tag("building", "")]), + txt: "Vogelkijkwand{name}" + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes")]), + txt: "Vogelijkhut{name}" + }, + { + k: new TagsFilter_1.Tag("amenity", "shelter"), + txt: "Vogelijkhut{name}" + }, + { + k: new TagsFilter_1.Tag("building", "yes"), + txt: "Vogelijkhut{name}" + }, + { k: null, txt: "Vogelkijkplaats{name}" } + ] + }); + _this.style = function (properties) { + var icon = "assets/nature/birdhide.svg"; + if (new TagsFilter_1.Or([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes"), new TagsFilter_1.Tag("shelter", "yes")]).matchesProperties(properties)) { + icon = "assets/nature/birdshelter.svg"; + } + return { + color: "#0000bb", + icon: leaflet_1.default.icon({ + iconUrl: icon, + iconSize: [40, 40], + iconAnchor: [20, 20] + }) + }; + }; + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "Is dit een kijkwand of kijkhut?", + mappings: [ + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("shelter", "no"), new TagsFilter_1.Tag("building", ""), new TagsFilter_1.Tag("amenity", "")]), + txt: "Vogelkijkwand" + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes"), new TagsFilter_1.Tag("shelter", "yes")]), + txt: "Vogelijkhut" + } + ] + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is ze rolstoeltoegankelijk?", + mappings: [ + { + k: new TagsFilter_1.Tag("wheelchair", "no"), + txt: "Niet rolstoeltoegankelijk" + }, + { + k: new TagsFilter_1.Tag("wheelchair", "limited"), + txt: "Een rolstoel raakt er, maar het is niet makkelijk" + }, + { + k: new TagsFilter_1.Tag("wheelchair", "yes"), + txt: "Een rolstoel raakt er gemakkelijk" + } + ] + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wie beheert deze?", + freeform: { + key: "operator", + template: "Beheer door $$$", + renderTemplate: "Beheer door {operator}", + placeholder: "organisatie" + }, + mappings: [ + { k: new TagsFilter_1.Tag("operator", "Natuurpunt"), txt: "Natuurpunt" }, + { k: new TagsFilter_1.Tag("operator", "Agentschap Natuur en Bos"), txt: "het Agentschap Natuur en Bos (ANB)" }, + ] + }) + ]; + return _this; + } + Birdhide.birdhide = new TagsFilter_1.Tag("leisure", "bird_hide"); + return Birdhide; +}(LayerDefinition_1.LayerDefinition)); +exports.Birdhide = Birdhide; diff --git a/Customizations/Layers/Bookcases.js b/Customizations/Layers/Bookcases.js new file mode 100644 index 0000000..39793ab --- /dev/null +++ b/Customizations/Layers/Bookcases.js @@ -0,0 +1,167 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Bookcases = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var leaflet_1 = require("leaflet"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var NameInline_1 = require("../Questions/NameInline"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var Bookcases = /** @class */ (function (_super) { + __extends(Bookcases, _super); + function Bookcases() { + var _this = _super.call(this) || this; + _this.name = "boekenkast"; + _this.newElementTags = [new TagsFilter_1.Tag("amenity", "public_bookcase")]; + _this.icon = "./assets/bookcase.svg"; + _this.overpassFilter = new TagsFilter_1.Tag("amenity", "public_bookcase"); + _this.minzoom = 11; + _this.title = new NameInline_1.NameInline("ruilboekenkastje"); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "Heeft dit boekenruilkastje een naam?", + freeform: { + key: "name", + template: "De naam is $$$", + renderTemplate: "", + placeholder: "", + extraTags: new TagsFilter_1.Tag("noname", "") + }, + mappings: [ + { k: new TagsFilter_1.Tag("noname", "yes"), txt: "Neen, er is geen naam aangeduid op het boekenruilkastje" }, + ] + }), + new TagRendering_1.TagRenderingOptions({ + question: "Hoeveel boeken passen in dit boekenruilkastje?", + freeform: { + renderTemplate: "Er passen {capacity} boeken in dit boekenruilkastje", + template: "Er passen $$$ boeken in dit boekenruilkastje", + key: "capacity", + placeholder: "aantal" + }, + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wat voor soort boeken heeft dit boekenruilkastje?", + mappings: [ + { k: new TagsFilter_1.Tag("books", "children"), txt: "Voornamelijk kinderboeken" }, + { k: new TagsFilter_1.Tag("books", "adults"), txt: "Voornamelijk boeken voor volwassenen" }, + { k: new TagsFilter_1.Tag("books", "children;adults"), txt: "Zowel kinderboeken als boeken voor volwassenen" } + ], + }), + new TagRendering_1.TagRenderingOptions({ + question: "Staat dit boekenruilkastje binnen of buiten?", + mappings: [ + { k: new TagsFilter_1.Tag("indoor", "yes"), txt: "Dit boekenruilkastje staat binnen" }, + { k: new TagsFilter_1.Tag("indoor", "no"), txt: "Dit boekenruilkastje staat buiten" }, + { k: new TagsFilter_1.Tag("indoor", ""), txt: "Dit boekenruilkastje staat buiten" } + ] + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is dit boekenruilkastje vrij toegankelijk?", + mappings: [ + { k: new TagsFilter_1.Tag("access", "yes"), txt: "Ja, vrij toegankelijk" }, + { k: new TagsFilter_1.Tag("access", "customers"), txt: "Enkel voor klanten" }, + ] + }).OnlyShowIf(new TagsFilter_1.Tag("indoor", "yes")), + new TagRendering_1.TagRenderingOptions({ + question: "Wie (welke organisatie) beheert dit boekenruilkastje?", + freeform: { + key: "opeartor", + renderTemplate: "Dit boekenruilkastje wordt beheerd door {operator}", + template: "Dit boekenruilkastje wordt beheerd door $$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Zijn er openingsuren voor dit boekenruilkastje?", + mappings: [ + { k: new TagsFilter_1.Tag("opening_hours", "24/7"), txt: "Dag en nacht toegankelijk" }, + { k: new TagsFilter_1.Tag("opening_hours", ""), txt: "Dag en nacht toegankelijk" }, + { k: new TagsFilter_1.Tag("opening_hours", "sunrise-sunset"), txt: "Van zonsopgang tot zonsondergang" }, + ], + freeform: { + key: "opening_hours", + renderTemplate: "De openingsuren zijn {opening_hours}", + template: "De openingsuren zijn $$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is dit boekenruilkastje deel van een netwerk?", + freeform: { + key: "brand", + renderTemplate: "Deel van het netwerk {brand}", + template: "Deel van het netwerk $$$" + }, + mappings: [{ + k: new TagsFilter_1.And([new TagsFilter_1.Tag("brand", "Little Free Library"), new TagsFilter_1.Tag("nobrand", "")]), + txt: "Little Free Library" + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("brand", ""), new TagsFilter_1.Tag("nobrand", "yes")]), + txt: "Maakt geen deel uit van een groter netwerk" + }] + }).OnlyShowIf(new TagsFilter_1.Or([ + new TagsFilter_1.Tag("ref", ""), + new TagsFilter_1.And([new TagsFilter_1.Tag("ref", "*"), new TagsFilter_1.Tag("brand", "")]) + ])), + new TagRendering_1.TagRenderingOptions({ + question: "Wat is het referentienummer van dit boekenruilkastje?", + freeform: { + key: "ref", + template: "Het referentienummer is $$$", + renderTemplate: "Gekend als {brand} {ref}" + } + }).OnlyShowIf(new TagsFilter_1.Tag("brand", "*")), + new TagRendering_1.TagRenderingOptions({ + question: "Wanneer werd dit boekenruilkastje geinstalleerd?", + priority: -1, + freeform: { + key: "start_date", + renderTemplate: "Geplaatst op {start_date}", + template: "Geplaatst op $$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is er een website waar we er meer informatie is over dit boekenruilkastje?", + freeform: { + key: "website", + renderTemplate: "Meer informatie over dit boekenruilkastje", + template: "$$$", + placeholder: "website" + } + }), + new TagRendering_1.TagRenderingOptions({ + freeform: { + key: "description", + renderTemplate: "Beschrijving door de uitbater:
{description}", + template: "$$$", + } + }) + ]; + _this.style = function (tags) { + return { + icon: new leaflet_1.default.icon({ + iconUrl: "assets/bookcase.svg", + iconSize: [40, 40] + }), + color: "#0000ff" + }; + }; + return _this; + } + return Bookcases; +}(LayerDefinition_1.LayerDefinition)); +exports.Bookcases = Bookcases; diff --git a/Customizations/Layers/Bos.js b/Customizations/Layers/Bos.js new file mode 100644 index 0000000..ef95dec --- /dev/null +++ b/Customizations/Layers/Bos.js @@ -0,0 +1,85 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Bos = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var AccessTag_1 = require("../Questions/AccessTag"); +var OperatorTag_1 = require("../Questions/OperatorTag"); +var NameQuestion_1 = require("../Questions/NameQuestion"); +var NameInline_1 = require("../Questions/NameInline"); +var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var Bos = /** @class */ (function (_super) { + __extends(Bos, _super); + function Bos() { + var _this = _super.call(this) || this; + _this.name = "bos"; + _this.icon = "./assets/tree_white_background.svg"; + _this.overpassFilter = new TagsFilter_1.Or([ + new TagsFilter_1.Tag("natural", "wood"), + new TagsFilter_1.Tag("landuse", "forest"), + new TagsFilter_1.Tag("natural", "scrub") + ]); + _this.newElementTags = [ + new TagsFilter_1.Tag("landuse", "forest"), + new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen") + ]; + _this.maxAllowedOverlapPercentage = 10; + _this.minzoom = 13; + _this.style = _this.generateStyleFunction(); + _this.title = new NameInline_1.NameInline("bos"); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new NameQuestion_1.NameQuestion(), + new AccessTag_1.AccessTag(), + new OperatorTag_1.OperatorTag(), + new DescriptionQuestion_1.DescriptionQuestion("bos") + ]; + return _this; + } + Bos.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + var questionSeverity = 0; + for (var _i = 0, _a = self.elementsToShow; _i < _a.length; _i++) { + var qd = _a[_i]; + if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { + continue; + } + if (qd.IsQuestioning(properties)) { + questionSeverity = Math.max(questionSeverity, qd.Priority()); + } + } + var colormapping = { + 0: "#00bb00", + 1: "#00ff00", + 10: "#dddd00", + 20: "#ff0000" + }; + var colour = colormapping[questionSeverity]; + while (colour == undefined) { + questionSeverity--; + colour = colormapping[questionSeverity]; + } + return { + color: colour, + icon: undefined + }; + }; + }; + return Bos; +}(LayerDefinition_1.LayerDefinition)); +exports.Bos = Bos; diff --git a/Customizations/Layers/DrinkingWater.js b/Customizations/Layers/DrinkingWater.js new file mode 100644 index 0000000..ba18984 --- /dev/null +++ b/Customizations/Layers/DrinkingWater.js @@ -0,0 +1,71 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DrinkingWater = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var OperatorTag_1 = require("../Questions/OperatorTag"); +var L = require("leaflet"); +var FixedText_1 = require("../Questions/FixedText"); +var TagRendering_1 = require("../TagRendering"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var DrinkingWater = /** @class */ (function (_super) { + __extends(DrinkingWater, _super); + function DrinkingWater() { + var _this = _super.call(this) || this; + _this.name = "drinking_water"; + _this.icon = "./assets/bug.svg"; + _this.overpassFilter = new TagsFilter_1.Or([ + new TagsFilter_1.And([ + new TagsFilter_1.Tag("amenity", "drinking_water") + ]) + ]); + _this.newElementTags = [ + new TagsFilter_1.Tag("amenity", "drinking_water"), + ]; + _this.maxAllowedOverlapPercentage = 10; + _this.minzoom = 13; + _this.style = _this.generateStyleFunction(); + _this.title = new FixedText_1.default("Drinking water"); + _this.elementsToShow = [ + new OperatorTag_1.OperatorTag(), + ]; + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "How easy is it to fill water bottles?", + mappings: [ + { k: new TagsFilter_1.Tag("bottle", "yes"), txt: "It is easy to refill water bottles" }, + { k: new TagsFilter_1.Tag("bottle", "no"), txt: "Water bottles may not fit" } + ], + }) + ]; + return _this; + } + DrinkingWater.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + return { + color: "#00bb00", + icon: new L.icon({ + iconUrl: self.icon, + iconSize: [40, 40] + }) + }; + }; + }; + return DrinkingWater; +}(LayerDefinition_1.LayerDefinition)); +exports.DrinkingWater = DrinkingWater; diff --git a/Customizations/Layers/GhostBike.js b/Customizations/Layers/GhostBike.js new file mode 100644 index 0000000..23de7ef --- /dev/null +++ b/Customizations/Layers/GhostBike.js @@ -0,0 +1,80 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GhostBike = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var FixedText_1 = require("../Questions/FixedText"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var leaflet_1 = require("leaflet"); +var GhostBike = /** @class */ (function (_super) { + __extends(GhostBike, _super); + function GhostBike() { + var _this = _super.call(this) || this; + _this.name = "ghost bike"; + _this.overpassFilter = new TagsFilter_1.Tag("memorial", "ghost_bike"); + _this.title = new FixedText_1.default("Ghost bike"); + _this.elementsToShow = [ + new FixedText_1.default("A ghost bike is a memorial for a cyclist who died in a traffic accident," + + " in the form of a white bicycle placed permanently near the accident location."), + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "Whom is remembered by this ghost bike?" + + "" + + "
" + + "Please respect privacy - only fill out the name if it is widely published or marked on the cycle." + + "
", + mappings: [{ k: new TagsFilter_1.Tag("noname", "yes"), txt: "There is no name marked on the bike" },], + freeform: { + key: "name", + extraTags: new TagsFilter_1.Tag("noname", ""), + template: "$$$", + renderTemplate: "In the remembrance of {name}", + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "When was the ghost bike installed?", + freeform: { + key: "start_date", + template: "The ghost bike was placed on $$$", + renderTemplate: "The ghost bike was placed on {start_date}", + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "On what URL can more information be found?" + + "If available, add a link to a news report about the accident or about the placing of the ghost bike", + freeform: { + key: "source", + template: "More information available on $$$", + renderTemplate: "More information", + } + }), + ]; + _this.style = function (tags) { + return { + color: "#000000", + icon: leaflet_1.default.icon({ + iconUrl: 'assets/bike/ghost.svg', + iconSize: [40, 40], + iconAnchor: [20, 20], + }) + }; + }; + return _this; + } + return GhostBike; +}(LayerDefinition_1.LayerDefinition)); +exports.GhostBike = GhostBike; diff --git a/Customizations/Layers/GrbToFix.js b/Customizations/Layers/GrbToFix.js new file mode 100644 index 0000000..bec3a35 --- /dev/null +++ b/Customizations/Layers/GrbToFix.js @@ -0,0 +1,90 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GrbToFix = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var leaflet_1 = require("leaflet"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var GrbToFix = /** @class */ (function (_super) { + __extends(GrbToFix, _super); + function GrbToFix() { + var _this = _super.call(this) || this; + _this.name = "grb"; + _this.newElementTags = undefined; + _this.icon = "./assets/star.svg"; + _this.overpassFilter = new TagsFilter_1.Regex("fixme", "GRB"); + _this.minzoom = 13; + _this.style = function (tags) { + return { + icon: new leaflet_1.default.icon({ + iconUrl: "assets/star.svg", + iconSize: [40, 40], + text: "hi" + }), + color: "#ff0000" + }; + }; + _this.title = new TagRendering_1.TagRenderingOptions({ + freeform: { + key: "fixme", + renderTemplate: "{fixme}", + template: "Fixme $$$" + } + }); + _this.elementsToShow = [ + new TagRendering_1.TagRenderingOptions({ + freeform: { + key: "addr:street", + renderTemplate: "Het adres is {addr:street} {addr:housenumber}", + template: "Straat? $$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wat is het huisnummer?", + tagsPreprocessor: function (tags) { + var telltale = "GRB thinks that this has number "; + var index = tags.fixme.indexOf(telltale); + if (index >= 0) { + var housenumber = tags.fixme.slice(index + telltale.length); + tags["grb:housenumber:human"] = housenumber; + tags["grb:housenumber"] = housenumber == "no number" ? "" : housenumber; + } + }, + freeform: { + key: "addr:housenumber", + template: "Het huisnummer is $$$", + renderTemplate: "Het huisnummer is {addr:housenumber}, GRB denkt {grb:housenumber:human}", + extraTags: new TagsFilter_1.Tag("fixme", "") + }, + mappings: [ + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("addr:housenumber", "{grb:housenumber}"), new TagsFilter_1.Tag("fixme", "")]), + txt: "Volg GRB: {grb:housenumber:human}", + substitute: true + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("addr:housenumber", "{addr:housenumber}"), new TagsFilter_1.Tag("fixme", "")]), + txt: "Volg OSM: {addr:housenumber}", + substitute: true + } + ] + }) + ]; + return _this; + } + return GrbToFix; +}(LayerDefinition_1.LayerDefinition)); +exports.GrbToFix = GrbToFix; diff --git a/Customizations/Layers/InformationBoard.js b/Customizations/Layers/InformationBoard.js new file mode 100644 index 0000000..57c8e24 --- /dev/null +++ b/Customizations/Layers/InformationBoard.js @@ -0,0 +1,122 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InformationBoard = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var leaflet_1 = require("leaflet"); +var InformationBoard = /** @class */ (function (_super) { + __extends(InformationBoard, _super); + function InformationBoard() { + var _this = _super.call(this, { + name: "Informatiebord", + minzoom: 12, + overpassFilter: new TagsFilter_1.Tag("tourism", "information"), + newElementTags: [new TagsFilter_1.Tag("tourism", "information")], + maxAllowedOverlapPercentage: 0, + icon: "assets/nature/info.png", + }) || this; + var isMap = new TagsFilter_1.Tag("information", "map"); + var isOsmSource = new TagsFilter_1.Tag("map_source", "OpenStreetMap"); + _this.title = new TagRendering_1.TagRenderingOptions({ + mappings: [ + { k: isMap, txt: "Kaart" }, + { k: null, txt: "Informatiebord" } + ] + }); + _this.style = function (properties) { + var icon = "assets/nature/info.png"; + if (isMap.matchesProperties(properties)) { + icon = "assets/map.svg"; + if (isOsmSource.matchesProperties(properties)) { + icon = "assets/osm-logo-white-bg.svg"; + var attr = properties["map_source:attribution"]; + if (attr == "sticker") { + icon = "assets/map-stickered.svg"; + } + else if (attr == "no") { + icon = "assets/osm-logo-buggy-attr.svg"; + } + } + } + return { + color: "#000000", + icon: leaflet_1.default.icon({ + iconUrl: icon, + iconSize: [50, 50] + }) + }; + }; + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "Heeft dit informatiebord een kaart?", + mappings: [ + { k: new TagsFilter_1.Tag("information", "board"), txt: "Dit is een informatiebord" }, + { k: isMap, txt: "Dit is een kaart" } + ] + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is this map based on OpenStreetMap?", + mappings: [ + { + k: isOsmSource, + txt: "This map is based on OpenStreetMap" + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("map_source:attribution", ""), new TagsFilter_1.Tag("map_source", "")]), + txt: "Unknown" + }, + ], + freeform: { + key: "map_source", + extraTags: new TagsFilter_1.Tag("map_source:attribution", ""), + renderTemplate: "The map data is based on {map_source}", + template: "The map data is based on $$$" + } + }).OnlyShowIf(isMap), + new TagRendering_1.TagRenderingOptions({ + question: "Is the attribution present?", + mappings: [ + { + k: new TagsFilter_1.Tag("map_source:attribution", "yes"), + txt: "OpenStreetMap is clearly attribute, including the ODBL-license" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "incomplete"), + txt: "OpenStreetMap is clearly attribute, but the license is not mentioned" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "sticker"), + txt: "OpenStreetMap wasn't mentioned, but someone put an OpenStreetMap-sticker on it" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "no"), + txt: "There is no attribution at all" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "none"), + txt: "There is no attribution at all" + } + ] + }).OnlyShowIf(new TagsFilter_1.Tag("map_source", "OpenStreetMap")) + ]; + return _this; + } + return InformationBoard; +}(LayerDefinition_1.LayerDefinition)); +exports.InformationBoard = InformationBoard; diff --git a/Customizations/Layers/Map.js b/Customizations/Layers/Map.js new file mode 100644 index 0000000..6206d8f --- /dev/null +++ b/Customizations/Layers/Map.js @@ -0,0 +1,99 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Map = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var FixedText_1 = require("../Questions/FixedText"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var leaflet_1 = require("leaflet"); +var Map = /** @class */ (function (_super) { + __extends(Map, _super); + function Map() { + var _this = _super.call(this) || this; + _this.name = "Map"; + _this.title = new FixedText_1.default("Map"); + _this.minzoom = 12; + _this.overpassFilter = new TagsFilter_1.Tag("information", "map"); + _this.newElementTags = [new TagsFilter_1.Tag("tourism", "information"), new TagsFilter_1.Tag("information", "map")]; + var isOsmSource = new TagsFilter_1.Tag("map_source", "OpenStreetMap"); + _this.style = function (properties) { + var icon = "assets/map.svg"; + if (isOsmSource.matchesProperties(properties)) { + icon = "assets/osm-logo-white-bg.svg"; + var attr = properties["map_source:attribution"]; + if (attr == "sticker") { + icon = "assets/map-stickered.svg"; + } + else if (attr == "no") { + icon = "assets/osm-logo-buggy-attr.svg"; + } + } + return { + color: "#000000", + icon: leaflet_1.default.icon({ + iconUrl: icon, + iconSize: [50, 50] + }) + }; + }; + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new TagRendering_1.TagRenderingOptions({ + question: "Is this map based on OpenStreetMap?", + mappings: [ + { + k: isOsmSource, + txt: "This map is based on OpenStreetMap" + }, + ], + freeform: { + key: "map_source", + renderTemplate: "The map data is based on {map_source}", + template: "The map data is based on $$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Is the attribution present?", + mappings: [ + { + k: new TagsFilter_1.Tag("map_source:attribution", "yes"), + txt: "OpenStreetMap is clearly attribute, including the ODBL-license" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "incomplete"), + txt: "OpenStreetMap is clearly attribute, but the license is not mentioned" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "sticker"), + txt: "OpenStreetMap wasn't mentioned, but someone put an OpenStreetMap-sticker on it" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "no"), + txt: "There is no attribution at all" + }, + { + k: new TagsFilter_1.Tag("map_source:attribution", "none"), + txt: "There is no attribution at all" + } + ] + }).OnlyShowIf(new TagsFilter_1.Tag("map_source", "OpenStreetMap")) + ]; + return _this; + } + return Map; +}(LayerDefinition_1.LayerDefinition)); +exports.Map = Map; diff --git a/Customizations/Layers/NatureReserves.js b/Customizations/Layers/NatureReserves.js new file mode 100644 index 0000000..76e2777 --- /dev/null +++ b/Customizations/Layers/NatureReserves.js @@ -0,0 +1,134 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NatureReserves = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var AccessTag_1 = require("../Questions/AccessTag"); +var OperatorTag_1 = require("../Questions/OperatorTag"); +var NameQuestion_1 = require("../Questions/NameQuestion"); +var NameInline_1 = require("../Questions/NameInline"); +var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var NatureReserves = /** @class */ (function (_super) { + __extends(NatureReserves, _super); + function NatureReserves(moreQuests) { + if (moreQuests === void 0) { moreQuests = false; } + var _this = _super.call(this) || this; + _this.name = "natuurgebied"; + _this.icon = "./assets/tree_white_background.svg"; + _this.overpassFilter = + new TagsFilter_1.Or([new TagsFilter_1.Tag("leisure", "nature_reserve"), new TagsFilter_1.Tag("boundary", "protected_area")]); + _this.maxAllowedOverlapPercentage = 10; + _this.newElementTags = [new TagsFilter_1.Tag("leisure", "nature_reserve"), + new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")]; + _this.minzoom = 13; + _this.title = new NameInline_1.NameInline("natuurreservaat"); + _this.style = _this.generateStyleFunction(); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new NameQuestion_1.NameQuestion(), + new AccessTag_1.AccessTag(), + new OperatorTag_1.OperatorTag(), + new DescriptionQuestion_1.DescriptionQuestion("natuurgebied") + ]; + var extraRenderings = [ + new TagRendering_1.TagRenderingOptions({ + question: "Mogen honden in dit natuurgebied?", + mappings: [ + { k: new TagsFilter_1.Tag("dog", "leashed"), txt: "Honden moeten aan de leiband" }, + { k: new TagsFilter_1.Tag("dog", "no"), txt: "Honden zijn niet toegestaan" }, + { k: new TagsFilter_1.Tag("dog", "yes"), txt: "Honden zijn welkom" }, + ] + }).OnlyShowIf(new TagsFilter_1.Tag("access", "yes")), + new TagRendering_1.TagRenderingOptions({ + question: "Op welke website kunnen we meer informatie vinden over dit natuurgebied?", + freeform: { + key: "website", + renderTemplate: "Meer informatie", + template: "$$$" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wie is de conservator van dit gebied?
" + + "Geef de naam van de conservator énkel als die duidelijk online staat gepubliceerd.", + freeform: { + renderTemplate: "De conservator van dit gebied is {curator}", + template: "$$$", + key: "curator" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wat is het email-adres van de beheerder?
" + + "Geef bij voorkeur het emailadres van de Natuurpunt-afdeling; geef enkel een email-adres van de conservator als dit duidelijk is gepubliceerd", + freeform: { + renderTemplate: "Bij problemen of vragen, de conservator kan bereikt worden via " + + "{email}", + template: "$$$", + key: "email" + } + }), + new TagRendering_1.TagRenderingOptions({ + question: "Wat is het telefoonnummer van de beheerder?
" + + "Geef bij voorkeur het telefoonnummer van de Natuurpunt-afdeling; geef enkel een email-adres van de conservator als dit duidelijk is gepubliceerd", + freeform: { + renderTemplate: "Bij problemen of vragen, de {conservator} kan bereikt worden via " + + "{phone}", + template: "$$$", + key: "phone" + } + }), + ]; + if (moreQuests) { + _this.elementsToShow = + _this.elementsToShow.concat(extraRenderings); + } + return _this; + } + NatureReserves.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + var _a; + var questionSeverity = 0; + for (var _i = 0, _b = self.elementsToShow; _i < _b.length; _i++) { + var qd = _b[_i]; + if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { + continue; + } + if (qd.IsQuestioning(properties)) { + questionSeverity = Math.max(questionSeverity, (_a = qd.Priority()) !== null && _a !== void 0 ? _a : 0); + } + } + var colormapping = { + 0: "#00bb00", + 1: "#00ff00", + 10: "#dddd00", + 20: "#ff0000" + }; + var colour = colormapping[questionSeverity]; + while (colour == undefined) { + questionSeverity--; + colour = colormapping[questionSeverity]; + } + return { + color: colour, + icon: undefined + }; + }; + }; + return NatureReserves; +}(LayerDefinition_1.LayerDefinition)); +exports.NatureReserves = NatureReserves; diff --git a/Customizations/Layers/Park.js b/Customizations/Layers/Park.js new file mode 100644 index 0000000..d9751e3 --- /dev/null +++ b/Customizations/Layers/Park.js @@ -0,0 +1,108 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Park = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var NameQuestion_1 = require("../Questions/NameQuestion"); +var NameInline_1 = require("../Questions/NameInline"); +var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); +var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); +var Park = /** @class */ (function (_super) { + __extends(Park, _super); + function Park() { + var _this = _super.call(this) || this; + _this.accessByDefault = new TagRendering_1.TagRenderingOptions({ + question: "Is dit park publiek toegankelijk?", + mappings: [ + { k: new TagsFilter_1.Tag("access", "yes"), txt: "Publiek toegankelijk" }, + { k: new TagsFilter_1.Tag("access", ""), txt: "Publiek toegankelijk" }, + { k: new TagsFilter_1.Tag("access", "no"), txt: "Niet publiek toegankelijk" }, + { k: new TagsFilter_1.Tag("access", "private"), txt: "Niet publiek toegankelijk, want privaat" }, + { k: new TagsFilter_1.Tag("access", "guided"), txt: "Enkel toegankelijk met een gids of op een activiteit" }, + ], + freeform: { + key: "access", + renderTemplate: "Dit park is niet toegankelijk: {access}", + template: "De toegankelijkheid van dit park is: $$$" + }, + priority: 20 + }); + _this.operatorByDefault = new TagRendering_1.TagRenderingOptions({ + question: "Wie beheert dit park?", + freeform: { + key: "operator", + renderTemplate: "Dit park wordt beheerd door {operator}", + template: "$$$", + }, + mappings: [{ + k: null, txt: "De gemeente beheert dit park" + }], + priority: 15 + }); + _this.name = "park"; + _this.icon = "./assets/tree_white_background.svg"; + _this.overpassFilter = + new TagsFilter_1.Or([new TagsFilter_1.Tag("leisure", "park"), new TagsFilter_1.Tag("landuse", "village_green")]); + _this.newElementTags = [new TagsFilter_1.Tag("leisure", "park"), + new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")]; + _this.maxAllowedOverlapPercentage = 25; + _this.minzoom = 13; + _this.style = _this.generateStyleFunction(); + _this.title = new NameInline_1.NameInline("park"); + _this.elementsToShow = [ + new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), + new NameQuestion_1.NameQuestion(), + _this.accessByDefault, + _this.operatorByDefault, + new DescriptionQuestion_1.DescriptionQuestion("park"), + ]; + return _this; + } + Park.prototype.generateStyleFunction = function () { + var self = this; + return function (properties) { + var _a; + var questionSeverity = 0; + for (var _i = 0, _b = self.elementsToShow; _i < _b.length; _i++) { + var qd = _b[_i]; + if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { + continue; + } + if (qd.IsQuestioning(properties)) { + questionSeverity = Math.max(questionSeverity, (_a = qd.Priority()) !== null && _a !== void 0 ? _a : 0); + } + } + var colormapping = { + 0: "#00bb00", + 1: "#00ff00", + 10: "#dddd00", + 20: "#ff0000" + }; + var colour = colormapping[questionSeverity]; + while (colour == undefined) { + questionSeverity--; + colour = colormapping[questionSeverity]; + } + return { + color: colour, + icon: undefined + }; + }; + }; + return Park; +}(LayerDefinition_1.LayerDefinition)); +exports.Park = Park; diff --git a/Customizations/Layers/Toilets.js b/Customizations/Layers/Toilets.js new file mode 100644 index 0000000..564c59c --- /dev/null +++ b/Customizations/Layers/Toilets.js @@ -0,0 +1,94 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Toilets = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var Quests_1 = require("../../Quests"); +var FixedUiElement_1 = require("../../UI/Base/FixedUiElement"); +var leaflet_1 = require("leaflet"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var Toilets = /** @class */ (function (_super) { + __extends(Toilets, _super); + function Toilets() { + var _a, _b; + var _this = _super.call(this) || this; + _this.name = "toilet"; + _this.newElementTags = [new TagsFilter_1.Tag("amenity", "toilets")]; + _this.icon = "./assets/toilets.svg"; + _this.overpassFilter = new TagsFilter_1.Tag("amenity", "toilets"); + _this.minzoom = 13; + _this.questions = [Quests_1.Quests.hasFee, + Quests_1.Quests.toiletsWheelChairs, + Quests_1.Quests.toiletsChangingTable, + Quests_1.Quests.toiletsChangingTableLocation, + Quests_1.Quests.toiletsPosition]; + _this.style = function (tags) { + if (tags.wheelchair == "yes") { + return { icon: new leaflet_1.default.icon({ + iconUrl: "assets/wheelchair.svg", + iconSize: [40, 40] + }) }; + } + return { icon: new leaflet_1.default.icon({ + iconUrl: "assets/toilets.svg", + iconSize: [40, 40] + }) }; + }; + _this.elementsToShow = [ + new FixedUiElement_1.FixedUiElement("Toiletten"), + new TagMappingOptions({ + key: "access", + mapping: { + yes: "Toegankelijk", + no: "Niet toegankelijk", + private: "Niet toegankelijk", + customers: "Enkel voor klanten", + } + }), + new TagMappingOptions({ + key: "fee", + mapping: (_a = { + yes: "Betalend", + no: "Gratis" + }, + _a["0"] = "Gratis", + _a), + template: "Betalend, men vraagt {fee}" + }), + new TagMappingOptions({ + key: "toilets:position", + mapping: (_b = { + seated: 'Gewone zittoiletten', + urinal: 'Een enkele urinoir', + urinals: 'Urinoirs' + }, + _b['urinals;seated'] = "Urinoirs en gewone toiletten", + _b['seated;urinals'] = "Urinoirs en gewone toiletten", + _b) + }), + new TagMappingOptions({ + key: "wheelchair", + mapping: { + yes: "Rolstoeltoegankelijk", + no: "Niet Rolstoeltoegankelijk", + limited: "Beperkt rolstoeltoegankelijk", + } + }), + ]; + return _this; + } + return Toilets; +}(LayerDefinition_1.LayerDefinition)); +exports.Toilets = Toilets; diff --git a/Customizations/Layers/Widths.js b/Customizations/Layers/Widths.js new file mode 100644 index 0000000..748e453 --- /dev/null +++ b/Customizations/Layers/Widths.js @@ -0,0 +1,270 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Widths = void 0; +var LayerDefinition_1 = require("../LayerDefinition"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var TagRendering_1 = require("../TagRendering"); +var Widths = /** @class */ (function (_super) { + __extends(Widths, _super); + function Widths(carWidth, cyclistWidth, pedestrianWidth) { + var _this = _super.call(this) || this; + _this._bothSideParking = new TagsFilter_1.Tag("parking:lane:both", "parallel"); + _this._noSideParking = new TagsFilter_1.Tag("parking:lane:both", "no_parking"); + _this._otherParkingMode = new TagsFilter_1.Or([ + new TagsFilter_1.Tag("parking:lane:both", "perpendicular"), + new TagsFilter_1.Tag("parking:lane:left", "perpendicular"), + new TagsFilter_1.Tag("parking:lane:right", "perpendicular"), + new TagsFilter_1.Tag("parking:lane:both", "diagonal"), + new TagsFilter_1.Tag("parking:lane:left", "diagonal"), + new TagsFilter_1.Tag("parking:lane:right", "diagonal"), + ]); + _this._leftSideParking = new TagsFilter_1.And([new TagsFilter_1.Tag("parking:lane:left", "parallel"), new TagsFilter_1.Tag("parking:lane:right", "no_parking")]); + _this._rightSideParking = new TagsFilter_1.And([new TagsFilter_1.Tag("parking:lane:right", "parallel"), new TagsFilter_1.Tag("parking:lane:left", "no_parking")]); + _this._sidewalkBoth = new TagsFilter_1.Tag("sidewalk", "both"); + _this._sidewalkLeft = new TagsFilter_1.Tag("sidewalk", "left"); + _this._sidewalkRight = new TagsFilter_1.Tag("sidewalk", "right"); + _this._sidewalkNone = new TagsFilter_1.Tag("sidewalk", "none"); + _this._oneSideParking = new TagsFilter_1.Or([_this._leftSideParking, _this._rightSideParking]); + _this._carfree = new TagsFilter_1.Or([new TagsFilter_1.Tag("highway", "pedestrian"), new TagsFilter_1.Tag("highway", "living_street")]); + _this._notCarFree = new TagsFilter_1.Not(_this._carfree); + _this.carWidth = carWidth; + _this.cyclistWidth = cyclistWidth; + _this.pedestrianWidth = pedestrianWidth; + _this.minzoom = 12; + function r(n) { + var pre = Math.floor(n); + var post = Math.floor((n * 10) % 10); + return "" + pre + "." + post; + } + _this.name = "widths"; + _this.overpassFilter = new TagsFilter_1.Tag("width:carriageway", "*"); + _this.title = new TagRendering_1.TagRenderingOptions({ + freeform: { + renderTemplate: "{name}", + template: "$$$", + key: "name" + } + }); + var self = _this; + _this.style = function (properties) { + var c = "#f00"; + var props = self.calcProps(properties); + if (props.pedestrianFlowNeeded > 0) { + c = "#fa0"; + } + if (props.width >= props.targetWidth || !props.cyclingAllowed) { + c = "#0c0"; + } + if (!props.parkingStateKnown && properties["note:width:carriageway"] === undefined) { + c = "#f0f"; + } + if (_this._carfree.matchesProperties(properties)) { + c = "#aaa"; + } + // Mark probably wrong data + if (props.width > 15) { + c = "#f0f"; + } + var dashArray = undefined; + if (props.onewayBike) { + dashArray = [20, 8]; + } + return { + icon: null, + color: c, + weight: 7, + dashArray: dashArray + }; + }; + _this.elementsToShow = [ + new TagRendering_1.TagRenderingOptions({ + question: "Mogen auto's hier parkeren?", + mappings: [ + { + k: _this._bothSideParking, + txt: "Auto's kunnen langs beide zijden parkeren.Dit gebruikt " + r(_this.carWidth * 2) + "m
" + }, + { + k: _this._oneSideParking, + txt: "Auto's kunnen langs één kant parkeren.
Dit gebruikt " + r(_this.carWidth) + "m
" + }, + { + k: _this._otherParkingMode, + txt: "Deze straat heeft dwarsparkeren of diagonaalparkeren aan minstens één zijde. Deze parkeerruimte is niet opgenomen in de straatbreedte." + }, + { k: _this._noSideParking, txt: "Auto's mogen hier niet parkeren" }, + ], + freeform: { + key: "note:width:carriageway", + renderTemplate: "{note:width:carriageway}", + template: "$$$", + } + }).OnlyShowIf(_this._notCarFree), + new TagRendering_1.TagRenderingOptions({ + mappings: [ + { + k: _this._sidewalkNone, + txt: "Deze straat heeft geen voetpaden. Voetgangers hebben hier " + r(_this.pedestrianWidth * 2) + "m nodig" + }, + { + k: new TagsFilter_1.Or([_this._sidewalkLeft, _this._sidewalkRight]), + txt: "Deze straat heeft een voetpad aan één kant. Voetgangers hebben hier " + r(_this.pedestrianWidth) + "m nodig" + }, + { k: _this._sidewalkBoth, txt: "Deze straat heeft voetpad aan beide zijden." }, + ], + freeform: { + key: "note:width:carriageway", + renderTemplate: "{note:width:carriageway}", + template: "$$$", + } + }).OnlyShowIf(_this._notCarFree), + new TagRendering_1.TagRenderingOptions({ + mappings: [ + { + k: new TagsFilter_1.Tag("bicycle", "use_sidepath"), + txt: "Er is een afgescheiden, verplicht te gebruiken fietspad. Fietsen op dit wegsegment hoeft dus niet" + }, + { + k: new TagsFilter_1.Tag("bicycle", "no"), + txt: "Fietsen is hier niet toegestaan" + }, + { + k: new TagsFilter_1.Tag("oneway:bicycle", "yes"), + txt: "Eenrichtingsverkeer, óók voor fietsers. Dit gebruikt " + r(_this.carWidth + _this.cyclistWidth) + "m" + }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("oneway", "yes"), new TagsFilter_1.Tag("oneway:bicycle", "no")]), + txt: "Tweerichtingverkeer voor fietsers, eenrichting voor auto's Dit gebruikt " + r(_this.carWidth + 2 * _this.cyclistWidth) + "m" + }, + { + k: new TagsFilter_1.Tag("oneway", "yes"), + txt: "Eenrichtingsverkeer voor iedereen. Dit gebruikt " + (_this.carWidth + _this.cyclistWidth) + "m" + }, + { + k: null, + txt: "Tweerichtingsverkeer voor iedereen. Dit gebruikt " + r(2 * _this.carWidth + 2 * _this.cyclistWidth) + "m" + } + ] + }).OnlyShowIf(_this._notCarFree), + new TagRendering_1.TagRenderingOptions({ + tagsPreprocessor: function (tags) { + var props = self.calcProps(tags); + tags.targetWidth = r(props.targetWidth); + tags.short = ""; + if (props.width < props.targetWidth) { + tags.short = "Er is dus " + r(props.targetWidth - props.width) + "m te weinig"; + } + }, + freeform: { + key: "width:carriageway", + renderTemplate: "De totale nodige ruimte voor vlot en veilig verkeer is dus {targetWidth}m
" + + "{short}", + template: "$$$", + } + }).OnlyShowIf(_this._notCarFree), + new TagRendering_1.TagRenderingOptions({ + mappings: [ + { k: new TagsFilter_1.Tag("highway", "living_street"), txt: "Dit is een woonerf" }, + { k: new TagsFilter_1.Tag("highway", "pedestrian"), txt: "Deze weg is autovrij" } + ] + }), + new TagRendering_1.TagRenderingOptions({ + mappings: [ + { + k: new TagsFilter_1.Tag("sidewalk", "none"), + txt: "De afstand van huis tot huis is {width:carriageway}m" + }, + { + k: new TagsFilter_1.Tag("sidewalk", "left"), + txt: "De afstand van huis tot voetpad is {width:carriageway}m" + }, + { + k: new TagsFilter_1.Tag("sidewalk", "right"), + txt: "De afstand van huis tot voetpad is {width:carriageway}m" + }, + { + k: new TagsFilter_1.Tag("sidewalk", "both"), + txt: "De afstand van voetpad tot voetpad is {width:carriageway}m" + }, + { + k: new TagsFilter_1.Tag("sidewalk", ""), + txt: "De straatbreedte is {width:carriageway}m" + } + ] + }) + ]; + return _this; + } + Widths.prototype.calcProps = function (properties) { + var parkingStateKnown = true; + var parallelParkingCount = 0; + if (this._oneSideParking.matchesProperties(properties)) { + parallelParkingCount = 1; + } + else if (this._bothSideParking.matchesProperties(properties)) { + parallelParkingCount = 2; + } + else if (this._noSideParking.matchesProperties(properties)) { + parallelParkingCount = 0; + } + else if (this._otherParkingMode.matchesProperties(properties)) { + parallelParkingCount = 0; + } + else { + parkingStateKnown = false; + console.log("No parking data for ", properties.name, properties.id, properties); + } + var pedestrianFlowNeeded = 0; + if (this._sidewalkBoth.matchesProperties(properties)) { + pedestrianFlowNeeded = 0; + } + else if (this._sidewalkNone.matchesProperties(properties)) { + pedestrianFlowNeeded = 2; + } + else if (this._sidewalkLeft.matchesProperties(properties) || this._sidewalkRight.matches(properties)) { + pedestrianFlowNeeded = 1; + } + else { + pedestrianFlowNeeded = -1; + } + var onewayCar = properties.oneway === "yes"; + var onewayBike = properties["oneway:bicycle"] === "yes" || + (onewayCar && properties["oneway:bicycle"] === undefined); + var cyclingAllowed = !(properties.bicycle === "use_sidepath" + || properties.bicycle === "no"); + var carWidth = (onewayCar ? 1 : 2) * this.carWidth; + var cyclistWidth = 0; + if (cyclingAllowed) { + cyclistWidth = (onewayBike ? 1 : 2) * this.cyclistWidth; + } + var width = parseFloat(properties["width:carriageway"]); + var targetWidth = carWidth + + cyclistWidth + + Math.max(0, pedestrianFlowNeeded) * this.pedestrianWidth + + parallelParkingCount * this.carWidth; + return { + parkingLanes: parallelParkingCount, + parkingStateKnown: parkingStateKnown, + width: width, + targetWidth: targetWidth, + onewayBike: onewayBike, + pedestrianFlowNeeded: pedestrianFlowNeeded, + cyclingAllowed: cyclingAllowed + }; + }; + return Widths; +}(LayerDefinition_1.LayerDefinition)); +exports.Widths = Widths; diff --git a/Customizations/Layout.js b/Customizations/Layout.js new file mode 100644 index 0000000..0ba3214 --- /dev/null +++ b/Customizations/Layout.js @@ -0,0 +1,38 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Layout = void 0; +/** + * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). + */ +var Layout = /** @class */ (function () { + /** + * + * @param name: The name used in the query string. If in the query "quests=" is defined, it will select this layout + * @param title: Will be used in the of the page + * @param layers: The layers to show, a list of LayerDefinitions + * @param startzoom: The initial starting zoom of the map + * @param startLat:The initial starting latitude of the map + * @param startLon: the initial starting longitude of the map + * @param welcomeMessage: This message is shown in the collapsable box on the left + * @param gettingStartedPlzLogin: This is shown below the welcomemessage and wrapped in a login link. + * @param welcomeBackMessage: This is shown when the user is logged in + * @param welcomeTail: This text is shown below the login message. It is ideal for extra help + */ + function Layout(name, title, layers, startzoom, startLat, startLon, welcomeMessage, gettingStartedPlzLogin, welcomeBackMessage, welcomeTail) { + if (gettingStartedPlzLogin === void 0) { gettingStartedPlzLogin = "Please login to get started"; } + if (welcomeBackMessage === void 0) { welcomeBackMessage = "You are logged in. Welcome back!"; } + if (welcomeTail === void 0) { welcomeTail = ""; } + this.title = title; + this.startLon = startLon; + this.startLat = startLat; + this.startzoom = startzoom; + this.name = name; + this.layers = layers; + this.welcomeMessage = welcomeMessage; + this.gettingStartedPlzLogin = gettingStartedPlzLogin; + this.welcomeBackMessage = welcomeBackMessage; + this.welcomeTail = welcomeTail; + } + return Layout; +}()); +exports.Layout = Layout; diff --git a/Customizations/Layouts/All.js b/Customizations/Layouts/All.js new file mode 100644 index 0000000..b03e05a --- /dev/null +++ b/Customizations/Layouts/All.js @@ -0,0 +1,26 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.All = void 0; +var Layout_1 = require("../Layout"); +var All = /** @class */ (function (_super) { + __extends(All, _super); + function All() { + return _super.call(this, "all", "All quest layers", [], 15, 51.2, 3.2, "<h3>All quests of MapComplete</h3>" + + "This is a mixed bag. Some quests might be hard or for experts to answer only", "Please log in", "") || this; + } + return All; +}(Layout_1.Layout)); +exports.All = All; diff --git a/Customizations/Layouts/Bookcases.js b/Customizations/Layouts/Bookcases.js new file mode 100644 index 0000000..86b30ce --- /dev/null +++ b/Customizations/Layouts/Bookcases.js @@ -0,0 +1,36 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Bookcases = void 0; +var Layout_1 = require("../Layout"); +var Layer = require("../Layers/Bookcases"); +var Bookcases = /** @class */ (function (_super) { + __extends(Bookcases, _super); + function Bookcases() { + var _this = _super.call(this, "bookcases", "Open Bookcase Map", [new Layer.Bookcases()], 14, 51.2, 3.2, " <h3>Open BoekenkastjesKaart</h3>\n" + + "\n" + + "<p>" + + "Help mee met het creëeren van een volledige kaart met alle boekenruilkastjes!" + + "Een boekenruilkastje is een vaste plaats in publieke ruimte waar iedereen een boek in kan zetten of uit kan meenemen." + + "Meestal een klein kastje of doosje dat op straat staat, maar ook een oude telefooncellen of een schap in een station valt hieronder." + + "</p>", " <p>Begin met <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">het aanmaken van een account\n" + + " </a> of door je " + + " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">aan te melden</span>.</p>", "Klik op een boekenruilkastje om vragen te beantwoorden") || this; + _this.locationContains = ["Bookcases.html", "Bookcase.html", "bookcase"]; + return _this; + } + return Bookcases; +}(Layout_1.Layout)); +exports.Bookcases = Bookcases; diff --git a/Customizations/Layouts/Cyclofix.js b/Customizations/Layouts/Cyclofix.js new file mode 100644 index 0000000..80e1df7 --- /dev/null +++ b/Customizations/Layouts/Cyclofix.js @@ -0,0 +1,35 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var Layout_1 = require("../Layout"); +var BikeParkings_1 = require("../Layers/BikeParkings"); +var BikeStations_1 = require("../Layers/BikeStations"); +var GhostBike_1 = require("../Layers/GhostBike"); +var DrinkingWater_1 = require("../Layers/DrinkingWater"); +var Cyclofix = /** @class */ (function (_super) { + __extends(Cyclofix, _super); + function Cyclofix() { + return _super.call(this, "pomp", "Cyclofix bicycle infrastructure", [new GhostBike_1.GhostBike(), new BikeStations_1.default(), new BikeParkings_1.default(), new DrinkingWater_1.DrinkingWater()], 16, 50.8465573, 4.3516970, "<h3>Cyclofix bicycle infrastructure</h3>\n" + + "\n" + + "<p><b>EN></b> On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + + "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.</p>" + + "<p><b>NL></b> Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + + "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.</p>" + + "<p><b>FR></b> Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + + "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins.</p>", "", "") || this; + } + return Cyclofix; +}(Layout_1.Layout)); +exports.default = Cyclofix; diff --git a/Customizations/Layouts/GRB.js b/Customizations/Layouts/GRB.js new file mode 100644 index 0000000..b6b741c --- /dev/null +++ b/Customizations/Layouts/GRB.js @@ -0,0 +1,28 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GRB = void 0; +var Layout_1 = require("../Layout"); +var GrbToFix_1 = require("../Layers/GrbToFix"); +var GRB = /** @class */ (function (_super) { + __extends(GRB, _super); + function GRB() { + return _super.call(this, "grb", "Grb import fix tool", [new GrbToFix_1.GrbToFix()], 15, 51.2083, 3.2279, "<h3>GRB Fix tool</h3>\n" + + "\n" + + "Expert use only", "", "") || this; + } + return GRB; +}(Layout_1.Layout)); +exports.GRB = GRB; diff --git a/Customizations/Layouts/Groen.js b/Customizations/Layouts/Groen.js new file mode 100644 index 0000000..94033ee --- /dev/null +++ b/Customizations/Layouts/Groen.js @@ -0,0 +1,56 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Groen = void 0; +var NatureReserves_1 = require("../Layers/NatureReserves"); +var Park_1 = require("../Layers/Park"); +var Bos_1 = require("../Layers/Bos"); +var Layout_1 = require("../Layout"); +var Groen = /** @class */ (function (_super) { + __extends(Groen, _super); + function Groen() { + var _this = _super.call(this, "buurtnatuur", "Buurtnatuur", [new NatureReserves_1.NatureReserves(), new Park_1.Park(), new Bos_1.Bos()], 10, 50.8435, 4.3688, "\n" + + "<img src='assets/groen.svg' alt='logo-groen' class='logo'> <br />" + + "<h3>Breng jouw buurtnatuur in kaart</h3>" + + "<b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten. \n" + + "<ul>" + + "<li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li>" + + "<li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li>" + + "<li>Op welke onbekende plekjes is het zalig spelen?</li>" + + "</ul>" + + "<p>Samen kleuren we heel Vlaanderen en Brussel groen.</p>" + + "<p>Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href=\"https://www.groen.be/buurtnatuur\" target='_blank'>meld je aan voor e-mailupdates</a>.</p> \n", "<b>Begin meteen door <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">een account te maken\n" + + " te maken</a> of\n" + + " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">in te loggen</span>.</b>", "", "<h4>Tips</h4>" + + "<ul>" + + "<li>Over groen ingekleurde gebieden weten we alles wat we willen weten.</li>" + + "<li>Bij rood ingekleurde gebieden ontbreekt nog heel wat info: klik een gebied aan en beantwoord de vragen.</li>" + + "<li>Je kan altijd een vraag overslaan als je het antwoord niet weet of niet zeker bent</li>" + + "<li>Je kan altijd een foto toevoegen</li>" + + "<li>Je kan ook zelf een gebied toevoegen door op de kaart te klikken</li>" + + "</ul>" + + "<small>" + + "<p>" + + "De oorspronkelijke data komt van <b>OpenStreetMap</b> en je antwoorden worden daar bewaard.<br/> Omdat iedereen vrij kan meewerken aan dit project, kunnen we niet garanderen dat er geen fouten opduiken." + + "</p>" + + "Je privacy is belangrijk. We tellen wel hoeveel gebruikers deze website bezoeken. We plaatsen een cookie waar geen persoonlijke informatie in bewaard wordt. " + + "Als je inlogt, komt er een tweede cookie bij met je inloggegevens." + + "</small>") || this; + _this.locationContains = ["buurtnatuur.be"]; + return _this; + } + return Groen; +}(Layout_1.Layout)); +exports.Groen = Groen; diff --git a/Customizations/Layouts/MetaMap.js b/Customizations/Layouts/MetaMap.js new file mode 100644 index 0000000..65d4762 --- /dev/null +++ b/Customizations/Layouts/MetaMap.js @@ -0,0 +1,27 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MetaMap = void 0; +var Layout_1 = require("../Layout"); +var Map_1 = require("../Layers/Map"); +var MetaMap = /** @class */ (function (_super) { + __extends(MetaMap, _super); + function MetaMap() { + return _super.call(this, "metamap", "Open Map Map", [new Map_1.Map()], 1, 0, 0, " <h3>Open Map Map</h3>\n" + + "This map is a map of physical maps, as known by OpenStreetMap.") || this; + } + return MetaMap; +}(Layout_1.Layout)); +exports.MetaMap = MetaMap; diff --git a/Customizations/Layouts/Natuurpunt.js b/Customizations/Layouts/Natuurpunt.js new file mode 100644 index 0000000..51ee110 --- /dev/null +++ b/Customizations/Layouts/Natuurpunt.js @@ -0,0 +1,28 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Natuurpunt = void 0; +var Layout_1 = require("../Layout"); +var Birdhide_1 = require("../Layers/Birdhide"); +var InformationBoard_1 = require("../Layers/InformationBoard"); +var NatureReserves_1 = require("../Layers/NatureReserves"); +var Natuurpunt = /** @class */ (function (_super) { + __extends(Natuurpunt, _super); + function Natuurpunt() { + return _super.call(this, "natuurpunt", "De natuur in", [new Birdhide_1.Birdhide(), new InformationBoard_1.InformationBoard(), new NatureReserves_1.NatureReserves(true)], 12, 51.20875, 3.22435, "<h3>Natuurpuntstuff</h3>", "", "") || this; + } + return Natuurpunt; +}(Layout_1.Layout)); +exports.Natuurpunt = Natuurpunt; diff --git a/Customizations/Layouts/Statues.js b/Customizations/Layouts/Statues.js new file mode 100644 index 0000000..fbcd44e --- /dev/null +++ b/Customizations/Layouts/Statues.js @@ -0,0 +1,31 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Statues = void 0; +var Layout_1 = require("../Layout"); +var Artwork_1 = require("../Layers/Artwork"); +var Statues = /** @class */ (function (_super) { + __extends(Statues, _super); + function Statues() { + return _super.call(this, "statues", "Open Artwork Map", [new Artwork_1.Artwork()], 10, 50.8435, 4.3688, " <h3>Open Statue Map</h3>\n" + + "\n" + + "<p>" + + "Help with creating a map of all statues all over the world!", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + + " </a> or by " + + " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; + } + return Statues; +}(Layout_1.Layout)); +exports.Statues = Statues; diff --git a/Customizations/Layouts/StreetWidth.js b/Customizations/Layouts/StreetWidth.js new file mode 100644 index 0000000..3afdca1 --- /dev/null +++ b/Customizations/Layouts/StreetWidth.js @@ -0,0 +1,37 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StreetWidth = void 0; +var Layout_1 = require("../Layout"); +var Widths_1 = require("../Layers/Widths"); +var StreetWidth = /** @class */ (function (_super) { + __extends(StreetWidth, _super); + function StreetWidth() { + return _super.call(this, "width", "Straatbreedtes in Brugge", [new Widths_1.Widths(2, 1.5, 0.75)], 15, 51.20875, 3.22435, "<h3>De straat is opgebruikt</h3>" + + "<p>Er is steeds meer druk op de openbare ruimte. Voetgangers, fietsers, steps, auto's, bussen, bestelwagens, buggies, cargobikes, ... willen allemaal hun deel van de openbare ruimte.</p>" + + "" + + "<p>In deze studie nemen we Brugge onder de loep en kijken we hoe breed elke straat is én hoe breed elke straat zou moeten zijn voor een veilig én vlot verkeer.</p>" + + "Verschillende ingrepen kunnen de stad teruggeven aan de inwoners en de stad leefbaarder en levendiger maken.<br/>" + + "Denk aan:" + + "<ul>" + + "<li>De autovrije zone's uitbreiden</li>" + + "<li>De binnenstad fietszone maken</li>" + + "<li>Het aantal woonerven uitbreiden</li>" + + "<li>Grotere auto's meer belasten - ze nemen immers meer parkeerruimte in.</li>" + + "</ul>", "", "") || this; + } + return StreetWidth; +}(Layout_1.Layout)); +exports.StreetWidth = StreetWidth; diff --git a/Customizations/Layouts/Toilets.js b/Customizations/Layouts/Toilets.js new file mode 100644 index 0000000..1caedb9 --- /dev/null +++ b/Customizations/Layouts/Toilets.js @@ -0,0 +1,31 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Toilets = void 0; +var Layout_1 = require("../Layout"); +var Layer = require("../Layers/Toilets"); +var Toilets = /** @class */ (function (_super) { + __extends(Toilets, _super); + function Toilets() { + return _super.call(this, "toilets", "Open Toilet Map", [new Layer.Toilets()], 12, 51.2, 3.2, " <h3>Open Toilet Map</h3>\n" + + "\n" + + "<p>Help us to create the most complete map about <i>all</i> the toilets in the world, based on openStreetMap." + + "One can answer questions here, which help users all over the world to find an accessible toilet, close to them.</p>", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + + " </a> or by " + + " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; + } + return Toilets; +}(Layout_1.Layout)); +exports.Toilets = Toilets; diff --git a/Customizations/Layouts/WalkByBrussels.js b/Customizations/Layouts/WalkByBrussels.js new file mode 100644 index 0000000..8b4f711 --- /dev/null +++ b/Customizations/Layouts/WalkByBrussels.js @@ -0,0 +1,33 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WalkByBrussels = void 0; +var Layout_1 = require("../Layout"); +var DrinkingWater_1 = require("../Layers/DrinkingWater"); +var NatureReserves_1 = require("../Layers/NatureReserves"); +var Park_1 = require("../Layers/Park"); +var WalkByBrussels = /** @class */ (function (_super) { + __extends(WalkByBrussels, _super); + function WalkByBrussels() { + return _super.call(this, "walkbybrussels", "Drinking Water Spots", [new DrinkingWater_1.DrinkingWater(), new Park_1.Park(), new NatureReserves_1.NatureReserves()], 10, 50.8435, 4.3688, " <h3>Drinking water</h3>\n" + + "\n" + + "<p>" + + "Help with creating a map of drinking water points!", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + + " </a> or by " + + " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; + } + return WalkByBrussels; +}(Layout_1.Layout)); +exports.WalkByBrussels = WalkByBrussels; diff --git a/Customizations/OnlyShowIf.js b/Customizations/OnlyShowIf.js new file mode 100644 index 0000000..e1d4c5b --- /dev/null +++ b/Customizations/OnlyShowIf.js @@ -0,0 +1,89 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OnlyShowIfConstructor = void 0; +var TagsFilter_1 = require("../Logic/TagsFilter"); +var UIElement_1 = require("../UI/UIElement"); +var OnlyShowIfConstructor = /** @class */ (function () { + function OnlyShowIfConstructor(tagsFilter, embedded) { + this._tagsFilter = tagsFilter; + this._embedded = embedded; + } + OnlyShowIfConstructor.prototype.construct = function (dependencies) { + return new OnlyShowIf(dependencies.tags, this._embedded.construct(dependencies), this._tagsFilter); + }; + OnlyShowIfConstructor.prototype.IsKnown = function (properties) { + if (!this.Matches(properties)) { + return true; + } + return this._embedded.IsKnown(properties); + }; + OnlyShowIfConstructor.prototype.IsQuestioning = function (properties) { + if (!this.Matches(properties)) { + return false; + } + return this._embedded.IsQuestioning(properties); + }; + OnlyShowIfConstructor.prototype.Priority = function () { + return this._embedded.Priority(); + }; + OnlyShowIfConstructor.prototype.Matches = function (properties) { + return this._tagsFilter.matches(TagsFilter_1.TagUtils.proprtiesToKV(properties)); + }; + return OnlyShowIfConstructor; +}()); +exports.OnlyShowIfConstructor = OnlyShowIfConstructor; +var OnlyShowIf = /** @class */ (function (_super) { + __extends(OnlyShowIf, _super); + function OnlyShowIf(tags, embedded, filter) { + var _this = _super.call(this, tags) || this; + _this._filter = filter; + _this._embedded = embedded; + return _this; + } + OnlyShowIf.prototype.Matches = function () { + return this._filter.matches(TagsFilter_1.TagUtils.proprtiesToKV(this._source.data)); + }; + OnlyShowIf.prototype.InnerRender = function () { + if (this.Matches()) { + return this._embedded.Render(); + } + else { + return ""; + } + }; + OnlyShowIf.prototype.Priority = function () { + return this._embedded.Priority(); + }; + OnlyShowIf.prototype.IsKnown = function () { + if (!this.Matches()) { + return false; + } + return this._embedded.IsKnown(); + }; + OnlyShowIf.prototype.IsQuestioning = function () { + if (!this.Matches()) { + return false; + } + return this._embedded.IsQuestioning(); + }; + OnlyShowIf.prototype.Activate = function () { + this._embedded.Activate(); + }; + OnlyShowIf.prototype.Update = function () { + this._embedded.Update(); + }; + return OnlyShowIf; +}(UIElement_1.UIElement)); diff --git a/Customizations/Questions/AccessTag.js b/Customizations/Questions/AccessTag.js new file mode 100644 index 0000000..b8901fd --- /dev/null +++ b/Customizations/Questions/AccessTag.js @@ -0,0 +1,50 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AccessTag = void 0; +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var AccessTag = /** @class */ (function (_super) { + __extends(AccessTag, _super); + function AccessTag() { + return _super.call(this, AccessTag.options) || this; + } + AccessTag.options = { + priority: 20, + question: "Is dit gebied toegankelijk?", + primer: "Dit gebied is ", + freeform: { + key: "access:description", + template: "Iets anders: $$$", + renderTemplate: "De toegankelijkheid van dit gebied is: {access:description}", + placeholder: "Specifieer" + }, + mappings: [ + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "yes"), new TagsFilter_1.Tag("fee", "")]), txt: "publiek toegankelijk" }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "no"), new TagsFilter_1.Tag("fee", "")]), txt: "niet toegankelijk" }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "private"), new TagsFilter_1.Tag("fee", "")]), txt: "niet toegankelijk, want privegebied" }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "permissive"), new TagsFilter_1.Tag("fee", "")]), txt: "toegankelijk, maar het is privegebied" }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "guided"), new TagsFilter_1.Tag("fee", "")]), txt: "enkel met gids of op activiteit" }, + { + k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "yes"), + new TagsFilter_1.Tag("fee", "yes")]), + txt: "toegankelijk mits betaling", + priority: 10 + }, + ] + }; + return AccessTag; +}(TagRendering_1.TagRenderingOptions)); +exports.AccessTag = AccessTag; diff --git a/Customizations/Questions/DescriptionQuestion.js b/Customizations/Questions/DescriptionQuestion.js new file mode 100644 index 0000000..a88781f --- /dev/null +++ b/Customizations/Questions/DescriptionQuestion.js @@ -0,0 +1,36 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DescriptionQuestion = void 0; +var TagRendering_1 = require("../TagRendering"); +var DescriptionQuestion = /** @class */ (function (_super) { + __extends(DescriptionQuestion, _super); + function DescriptionQuestion(category) { + return _super.call(this, { + question: "Zijn er bijzonderheden die we moeten weten over dit " + category + "?<br>" + + "<span class='question-subtext'>Je hoeft niet te herhalen wat je net hebt aangeduid.<br/>" + + "Een <i>naam</i> wordt in de volgende stap gevraagd.<br/>" + + "Voel je vrij om dit veld over te slaan.</span>", + freeform: { + key: "description:0", + renderTemplate: "{description:0}", + template: "$$$" + }, + priority: 14 + }) || this; + } + return DescriptionQuestion; +}(TagRendering_1.TagRenderingOptions)); +exports.DescriptionQuestion = DescriptionQuestion; diff --git a/Customizations/Questions/FixedText.js b/Customizations/Questions/FixedText.js new file mode 100644 index 0000000..ab46069 --- /dev/null +++ b/Customizations/Questions/FixedText.js @@ -0,0 +1,30 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../TagRendering"); +var FixedText = /** @class */ (function (_super) { + __extends(FixedText, _super); + function FixedText(category) { + return _super.call(this, { + mappings: [ + { + k: null, txt: category + } + ] + }) || this; + } + return FixedText; +}(TagRendering_1.TagRenderingOptions)); +exports.default = FixedText; diff --git a/Customizations/Questions/NameInline.js b/Customizations/Questions/NameInline.js new file mode 100644 index 0000000..5192935 --- /dev/null +++ b/Customizations/Questions/NameInline.js @@ -0,0 +1,41 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NameInline = void 0; +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var NameInline = /** @class */ (function (_super) { + __extends(NameInline, _super); + function NameInline(category) { + return _super.call(this, { + question: "", + freeform: { + renderTemplate: "{name}", + template: "De naam van dit " + category + " is $$$", + key: "name", + extraTags: new TagsFilter_1.Tag("noname", "") // Remove 'noname=yes' + }, + mappings: [ + { k: new TagsFilter_1.Tag("noname", "yes"), txt: NameInline.Upper(category) + " zonder naam" }, + { k: null, txt: NameInline.Upper(category) } + ] + }) || this; + } + NameInline.Upper = function (string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }; + return NameInline; +}(TagRendering_1.TagRenderingOptions)); +exports.NameInline = NameInline; diff --git a/Customizations/Questions/NameQuestion.js b/Customizations/Questions/NameQuestion.js new file mode 100644 index 0000000..477039e --- /dev/null +++ b/Customizations/Questions/NameQuestion.js @@ -0,0 +1,48 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NameQuestion = void 0; +/** + * There are two ways to ask for names: + * One is a big 'name-question', the other is the 'edit name' in the title. + * THis one is the big question + */ +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var NameQuestion = /** @class */ (function (_super) { + __extends(NameQuestion, _super); + function NameQuestion() { + return _super.call(this, NameQuestion.options) || this; + } + NameQuestion.options = { + priority: 10, + question: "Wat is de <i>officiële</i> naam van dit gebied?<br><span class='question-subtext'>" + + "Zelf een naam bedenken wordt afgeraden.<br/>" + + "Een beschrijving van het gebied geven kan in een volgende stap.<br/>" + + "</span>", + freeform: { + key: "name", + template: "De naam is $$$", + renderTemplate: "", + placeholder: "", + extraTags: new TagsFilter_1.Tag("noname", "") + }, + mappings: [ + { k: new TagsFilter_1.Tag("noname", "yes"), txt: "Dit gebied heeft geen naam" }, + ] + }; + return NameQuestion; +}(TagRendering_1.TagRenderingOptions)); +exports.NameQuestion = NameQuestion; diff --git a/Customizations/Questions/OperatorTag.js b/Customizations/Questions/OperatorTag.js new file mode 100644 index 0000000..9f7f720 --- /dev/null +++ b/Customizations/Questions/OperatorTag.js @@ -0,0 +1,41 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OperatorTag = void 0; +var TagRendering_1 = require("../TagRendering"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var OperatorTag = /** @class */ (function (_super) { + __extends(OperatorTag, _super); + function OperatorTag() { + return _super.call(this, OperatorTag.options) || this; + } + OperatorTag.options = { + priority: 15, + question: "Wie beheert dit gebied?", + freeform: { + key: "operator", + template: "Dit gebied wordt beheerd door $$$", + renderTemplate: "Dit gebied wordt beheerd door {operator}", + placeholder: "organisatie" + }, + mappings: [ + { k: new TagsFilter_1.Tag("operator", "Natuurpunt"), txt: "Natuurpunt" }, + { k: new TagsFilter_1.Tag("operator", "Agentschap Natuur en Bos"), txt: "het Agentschap Natuur en Bos (ANB)" }, + { k: new TagsFilter_1.Tag("operator", "private"), txt: "Beheer door een privépersoon" } + ] + }; + return OperatorTag; +}(TagRendering_1.TagRenderingOptions)); +exports.OperatorTag = OperatorTag; diff --git a/Customizations/Questions/OsmLink.js b/Customizations/Questions/OsmLink.js new file mode 100644 index 0000000..e552f58 --- /dev/null +++ b/Customizations/Questions/OsmLink.js @@ -0,0 +1,40 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OsmLink = void 0; +var TagRendering_1 = require("../TagRendering"); +var Img_1 = require("../../UI/Img"); +var TagsFilter_1 = require("../../Logic/TagsFilter"); +var OsmLink = /** @class */ (function (_super) { + __extends(OsmLink, _super); + function OsmLink() { + return _super.call(this, OsmLink.options) || this; + } + OsmLink.options = { + freeform: { + key: "id", + template: "$$$", + renderTemplate: "<span class='osmlink'><a href='https://osm.org/{id}' target='_blank'>" + + Img_1.Img.osmAbstractLogo + + "</a></span>", + placeholder: "", + }, + mappings: [ + { k: new TagsFilter_1.Tag("id", "node/-1"), txt: "<span class='alert'>Uploading</span>" } + ] + }; + return OsmLink; +}(TagRendering_1.TagRenderingOptions)); +exports.OsmLink = OsmLink; diff --git a/Customizations/Questions/WikipediaLink.js b/Customizations/Questions/WikipediaLink.js new file mode 100644 index 0000000..3ae56a2 --- /dev/null +++ b/Customizations/Questions/WikipediaLink.js @@ -0,0 +1,59 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WikipediaLink = void 0; +var TagRendering_1 = require("../TagRendering"); +var WikipediaLink = /** @class */ (function (_super) { + __extends(WikipediaLink, _super); + function WikipediaLink() { + return _super.call(this, WikipediaLink.options) || this; + } + WikipediaLink.FixLink = function (value) { + if (value === undefined) { + return undefined; + } + // @ts-ignore + if (value.startsWith("https")) { + return value; + } + else { + var splitted = value.split(":"); + var language = splitted[0]; + splitted.shift(); + var page = splitted.join(":"); + return 'https://' + language + '.wikipedia.org/wiki/' + page; + } + }; + WikipediaLink.options = { + priority: 10, + // question: "Wat is het overeenstemmende wkipedia-artikel?", + tagsPreprocessor: function (tags) { + if (tags.wikipedia !== undefined) { + tags.wikipedia = WikipediaLink.FixLink(tags.wikipedia); + } + }, + freeform: { + key: "wikipedia", + template: "$$$", + renderTemplate: "<span class='wikipedialink'>" + + "<a href='{wikipedia}' target='_blank'>" + + "<img width='64px' src='./assets/wikipedia.svg' alt='wikipedia'>" + + "</a></span>", + placeholder: "" + }, + }; + return WikipediaLink; +}(TagRendering_1.TagRenderingOptions)); +exports.WikipediaLink = WikipediaLink; diff --git a/Customizations/Questions/bike/ParkingType.js b/Customizations/Questions/bike/ParkingType.js new file mode 100644 index 0000000..2b54c21 --- /dev/null +++ b/Customizations/Questions/bike/ParkingType.js @@ -0,0 +1,52 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var ParkingType = /** @class */ (function (_super) { + __extends(ParkingType, _super); + function ParkingType() { + return _super.call(this, { + priority: 5, + question: "Van welk type is deze fietsenparking?", + freeform: { + key: "bicycle_parking", + extraTags: new TagsFilter_1.Tag("fixme", "Freeform bicycle_parking= tag used: possibly a wrong value"), + template: "Iets anders: $$$", + renderTemplate: "Dit is een fietsenparking van het type: {bicycle_parking}", + placeholder: "Specifieer" + }, + mappings: [ + { k: new TagsFilter_1.Tag("bicycle_parking", "stands"), txt: ParkingType.toImgTxt(ParkingType.images.stands) }, + { k: new TagsFilter_1.Tag("bicycle_parking", "wall_loops"), txt: ParkingType.toImgTxt(ParkingType.images.wall_loops) }, + { k: new TagsFilter_1.Tag("bicycle_parking", "handlebar_holder"), txt: ParkingType.toImgTxt(ParkingType.images.handlebar_holder) }, + { k: new TagsFilter_1.Tag("bicycle_parking", "shed"), txt: ParkingType.toImgTxt(ParkingType.images.shed) }, + { k: new TagsFilter_1.Tag("bicycle_parking", "two-tier"), txt: ParkingType.toImgTxt(ParkingType.images["two-tier"]) } + ] + }) || this; + } + ParkingType.toImgTxt = function (url) { + return "<img src=" + url + ">"; + }; + ParkingType.images = { + stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", + wall_loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", + handlebar_holder: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", + shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", + "two-tier": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" + }; + return ParkingType; +}(TagRendering_1.TagRenderingOptions)); +exports.default = ParkingType; diff --git a/Customizations/Questions/bike/PumpManometer.js b/Customizations/Questions/bike/PumpManometer.js new file mode 100644 index 0000000..c79d20a --- /dev/null +++ b/Customizations/Questions/bike/PumpManometer.js @@ -0,0 +1,32 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var PumpManometer = /** @class */ (function (_super) { + __extends(PumpManometer, _super); + function PumpManometer() { + return _super.call(this, { + question: "Does the pump have a pressure indicator or manometer?", + mappings: [ + { k: new TagsFilter_1.Tag("manometer", "yes"), txt: "Yes, there is a manometer" }, + { k: new TagsFilter_1.Tag("manometer", "broken"), txt: "Yes, but it is broken" }, + { k: new TagsFilter_1.Tag("manometer", "yes"), txt: "No" } + ] + }) || this; + } + return PumpManometer; +}(TagRendering_1.TagRenderingOptions)); +exports.default = PumpManometer; diff --git a/Customizations/Questions/bike/PumpManual.js b/Customizations/Questions/bike/PumpManual.js new file mode 100644 index 0000000..bfd34cc --- /dev/null +++ b/Customizations/Questions/bike/PumpManual.js @@ -0,0 +1,32 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var PumpManual = /** @class */ (function (_super) { + __extends(PumpManual, _super); + function PumpManual() { + return _super.call(this, { + priority: 5, + question: "Is this an electric bike pump?", + mappings: [ + { k: new TagsFilter_1.Tag("manual", "yes"), txt: "Manual pump" }, + { k: new TagsFilter_1.Tag("manual", "no"), txt: "Electric pump" } + ] + }) || this; + } + return PumpManual; +}(TagRendering_1.TagRenderingOptions)); +exports.default = PumpManual; diff --git a/Customizations/Questions/bike/PumpOperational.js b/Customizations/Questions/bike/PumpOperational.js new file mode 100644 index 0000000..ffd30e9 --- /dev/null +++ b/Customizations/Questions/bike/PumpOperational.js @@ -0,0 +1,31 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var PumpOperational = /** @class */ (function (_super) { + __extends(PumpOperational, _super); + function PumpOperational() { + return _super.call(this, { + question: "Is the bicycle pump still operational?", + mappings: [ + { k: new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "broken"), txt: "This pump is broken" }, + { k: new TagsFilter_1.Tag("service:bicycle:pump:operational_status", ""), txt: "This pump is operational" } + ] + }) || this; + } + return PumpOperational; +}(TagRendering_1.TagRenderingOptions)); +exports.default = PumpOperational; diff --git a/Customizations/Questions/bike/PumpValves.js b/Customizations/Questions/bike/PumpValves.js new file mode 100644 index 0000000..65d1240 --- /dev/null +++ b/Customizations/Questions/bike/PumpValves.js @@ -0,0 +1,41 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var PumpValves = /** @class */ (function (_super) { + __extends(PumpValves, _super); + function PumpValves() { + return _super.call(this, { + question: "What valves are supported?", + mappings: [ + { + k: new TagsFilter_1.Tag("valves", " sclaverand;schrader;dunlop"), + txt: "There is a default head, so Presta, Dunlop and Auto" + }, + { k: new TagsFilter_1.Tag("valves", "dunlop"), txt: "Only dunlop" }, + { k: new TagsFilter_1.Tag("valves", "sclaverand"), txt: "Only Sclaverand (also known as Dunlop)" }, + { k: new TagsFilter_1.Tag("valves", "auto"), txt: "Only auto" }, + ], + freeform: { + key: "valves", + template: "Supported valves are $$$", + renderTemplate: "Supported valves are {valves}" + } + }) || this; + } + return PumpValves; +}(TagRendering_1.TagRenderingOptions)); +exports.default = PumpValves; diff --git a/Customizations/Questions/bike/StationBrand.js b/Customizations/Questions/bike/StationBrand.js new file mode 100644 index 0000000..b5d36bd --- /dev/null +++ b/Customizations/Questions/bike/StationBrand.js @@ -0,0 +1,44 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +/** + * Currently not used in Cyclofix because it's a little vague + */ +var BikeStationBrand = /** @class */ (function (_super) { + __extends(BikeStationBrand, _super); + function BikeStationBrand() { + var _this = this; + throw Error('BikeStationBrand disabled'); + _this = _super.call(this, BikeStationBrand.options) || this; + return _this; + } + BikeStationBrand.options = { + priority: 15, + question: "What is the brand of this bike station (name of university, shop, city...)?", + freeform: { + key: "brand", + template: "The brand of this bike station is $$$", + renderTemplate: "The brand of this bike station is {operator}", + placeholder: "brand" + }, + mappings: [ + { k: new TagsFilter_1.Tag("brand", "Velo Fix Station"), txt: "Velo Fix Station" } + ] + }; + return BikeStationBrand; +}(TagRendering_1.TagRenderingOptions)); +exports.default = BikeStationBrand; diff --git a/Customizations/Questions/bike/StationChain.js b/Customizations/Questions/bike/StationChain.js new file mode 100644 index 0000000..ca34b0e --- /dev/null +++ b/Customizations/Questions/bike/StationChain.js @@ -0,0 +1,32 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var StationChain = /** @class */ (function (_super) { + __extends(StationChain, _super); + function StationChain() { + return _super.call(this, { + priority: 5, + question: "Does this bike station have a special tool to repair your bike chain?", + mappings: [ + { k: new TagsFilter_1.Tag("service:bicycle:chain_tool", "yes"), txt: "There is a chain tool." }, + { k: new TagsFilter_1.Tag("service:bicycle:chain_tool", "no"), txt: "There is no chain tool." }, + ] + }) || this; + } + return StationChain; +}(TagRendering_1.TagRenderingOptions)); +exports.default = StationChain; diff --git a/Customizations/Questions/bike/StationOperator.js b/Customizations/Questions/bike/StationOperator.js new file mode 100644 index 0000000..02f6b09 --- /dev/null +++ b/Customizations/Questions/bike/StationOperator.js @@ -0,0 +1,41 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var BikeStationOperator = /** @class */ (function (_super) { + __extends(BikeStationOperator, _super); + function BikeStationOperator() { + return _super.call(this, { + priority: 15, + question: "Who operates this bike station (name of university, shop, city...)?", + freeform: { + key: "operator", + template: "This bike station is operated by $$$", + renderTemplate: "This bike station is operated by {operator}", + placeholder: "organisatie" + }, + mappings: [ + { k: new TagsFilter_1.Tag("operator", "KU Leuven"), txt: "KU Leuven" }, + { k: new TagsFilter_1.Tag("operator", "Stad Halle"), txt: "Stad Halle" }, + { k: new TagsFilter_1.Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis" }, + { k: new TagsFilter_1.Tag("operator", "Jette"), txt: "Jette" }, + { k: new TagsFilter_1.Tag("operator", "private"), txt: "Beheer door een privépersoon" } + ] + }) || this; + } + return BikeStationOperator; +}(TagRendering_1.TagRenderingOptions)); +exports.default = BikeStationOperator; diff --git a/Customizations/Questions/bike/StationPumpTools.js b/Customizations/Questions/bike/StationPumpTools.js new file mode 100644 index 0000000..2db65ab --- /dev/null +++ b/Customizations/Questions/bike/StationPumpTools.js @@ -0,0 +1,33 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var BikeStationPumpTools = /** @class */ (function (_super) { + __extends(BikeStationPumpTools, _super); + function BikeStationPumpTools() { + return _super.call(this, { + priority: 15, + question: "Which services are available at this bike station?", + mappings: [ + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "no"), new TagsFilter_1.Tag("service:bicycle:pump", "yes")]), txt: "There is only a pump available." }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "yes"), new TagsFilter_1.Tag("service:bicycle:pump", "no")]), txt: "There are only tools (screwdrivers, pliers...) available." }, + { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "yes"), new TagsFilter_1.Tag("service:bicycle:pump", "yes")]), txt: "There are both tools and a pump available." } + ] + }) || this; + } + return BikeStationPumpTools; +}(TagRendering_1.TagRenderingOptions)); +exports.default = BikeStationPumpTools; diff --git a/Customizations/Questions/bike/StationStand.js b/Customizations/Questions/bike/StationStand.js new file mode 100644 index 0000000..55f607a --- /dev/null +++ b/Customizations/Questions/bike/StationStand.js @@ -0,0 +1,32 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var TagRendering_1 = require("../../TagRendering"); +var TagsFilter_1 = require("../../../Logic/TagsFilter"); +var BikeStationStand = /** @class */ (function (_super) { + __extends(BikeStationStand, _super); + function BikeStationStand() { + return _super.call(this, { + priority: 10, + question: "Does this bike station have a hook to suspend your bike with or a stand to elevate it?", + mappings: [ + { k: new TagsFilter_1.Tag("service:bicycle:stand", "yes"), txt: "There is a hook or stand." }, + { k: new TagsFilter_1.Tag("service:bicycle:stand", "no"), txt: "There is no hook or stand" }, + ] + }) || this; + } + return BikeStationStand; +}(TagRendering_1.TagRenderingOptions)); +exports.default = BikeStationStand; diff --git a/Customizations/TagRendering.js b/Customizations/TagRendering.js new file mode 100644 index 0000000..d8f2f83 --- /dev/null +++ b/Customizations/TagRendering.js @@ -0,0 +1,321 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TagRenderingOptions = void 0; +var UIElement_1 = require("../UI/UIElement"); +var UIEventSource_1 = require("../UI/UIEventSource"); +var TagsFilter_1 = require("../Logic/TagsFilter"); +var FixedUiElement_1 = require("../UI/Base/FixedUiElement"); +var SaveButton_1 = require("../UI/SaveButton"); +var VariableUIElement_1 = require("../UI/Base/VariableUIElement"); +var OnlyShowIf_1 = require("./OnlyShowIf"); +var TextField_1 = require("../UI/Input/TextField"); +var InputElementWrapper_1 = require("../UI/Input/InputElementWrapper"); +var FixedInputElement_1 = require("../UI/Input/FixedInputElement"); +var RadioButton_1 = require("../UI/Input/RadioButton"); +var TagRenderingOptions = /** @class */ (function () { + function TagRenderingOptions(options) { + this.options = options; + } + TagRenderingOptions.prototype.OnlyShowIf = function (tagsFilter) { + return new OnlyShowIf_1.OnlyShowIfConstructor(tagsFilter, this); + }; + TagRenderingOptions.prototype.IsQuestioning = function (tags) { + var _a; + var tagsKV = TagsFilter_1.TagUtils.proprtiesToKV(tags); + for (var _i = 0, _b = (_a = this.options.mappings) !== null && _a !== void 0 ? _a : []; _i < _b.length; _i++) { + var oneOnOneElement = _b[_i]; + if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tagsKV)) { + return false; + } + } + if (this.options.freeform !== undefined && tags[this.options.freeform.key] !== undefined) { + return false; + } + if (this.options.question === undefined) { + return false; + } + return true; + }; + TagRenderingOptions.prototype.construct = function (dependencies) { + return new TagRendering(dependencies.tags, dependencies.changes, this.options); + }; + TagRenderingOptions.prototype.IsKnown = function (properties) { + return !this.IsQuestioning(properties); + }; + TagRenderingOptions.prototype.Priority = function () { + var _a; + return (_a = this.options.priority) !== null && _a !== void 0 ? _a : 0; + }; + return TagRenderingOptions; +}()); +exports.TagRenderingOptions = TagRenderingOptions; +var TagRendering = /** @class */ (function (_super) { + __extends(TagRendering, _super); + function TagRendering(tags, changes, options) { + var _a, _b, _c; + var _this = _super.call(this, tags) || this; + _this._questionSkipped = new UIEventSource_1.UIEventSource(false); + _this._editMode = new UIEventSource_1.UIEventSource(false); + var self = _this; + _this.ListenTo(_this._questionSkipped); + _this.ListenTo(_this._editMode); + _this._userDetails = changes.login.userDetails; + _this.ListenTo(_this._userDetails); + _this._question = options.question; + _this._priority = (_a = options.priority) !== null && _a !== void 0 ? _a : 0; + _this._primer = (_b = options.primer) !== null && _b !== void 0 ? _b : ""; + _this._tagsPreprocessor = function (properties) { + if (options.tagsPreprocessor === undefined) { + return properties; + } + var newTags = {}; + for (var k in properties) { + newTags[k] = properties[k]; + } + options.tagsPreprocessor(newTags); + return newTags; + }; + _this._mapping = []; + _this._renderMapping = []; + _this._freeform = options.freeform; + // Prepare the choices for the Radio buttons + var choices = []; + var usedChoices = []; + for (var _i = 0, _d = (_c = options.mappings) !== null && _c !== void 0 ? _c : []; _i < _d.length; _i++) { + var choice = _d[_i]; + if (choice.k === null) { + _this._mapping.push(choice); + continue; + } + var choiceSubbed = choice; + if (choice.substitute) { + choiceSubbed = { + k: choice.k.substituteValues(options.tagsPreprocessor(_this._source.data)), + txt: _this.ApplyTemplate(choice.txt), + substitute: false, + priority: choice.priority + }; + } + var txt = choiceSubbed.txt; + // Choices is what is shown in the radio buttons + if (usedChoices.indexOf(txt) < 0) { + choices.push(new FixedUiElement_1.FixedUiElement(txt)); + usedChoices.push(txt); + // This is used to convert the radio button index into tags needed to add + _this._mapping.push(choiceSubbed); + } + else { + _this._renderMapping.push(choiceSubbed); // only used while rendering + } + } + // Prepare the actual input element -> pick an appropriate implementation + _this._questionElement = _this.InputElementFor(options); + var save = function () { + var selection = self._questionElement.GetValue().data; + if (selection) { + changes.addTag(tags.data.id, selection); + } + self._editMode.setData(false); + }; + var cancel = function () { + self._questionSkipped.setData(true); + self._editMode.setData(false); + self._source.ping(); // Send a ping upstream to render the next question + }; + // Setup the save button and it's action + _this._saveButton = new SaveButton_1.SaveButton(_this._questionElement.GetValue()) + .onClick(save); + _this._editButton = new FixedUiElement_1.FixedUiElement(""); + if (_this._question !== undefined) { + _this._editButton = new FixedUiElement_1.FixedUiElement("<img class='editbutton' src='./assets/pencil.svg' alt='edit'>") + .onClick(function () { + self._questionElement.GetValue().setData(self.CurrentValue()); + self._editMode.setData(true); + }); + } + var cancelContents = _this._editMode.map(function (isEditing) { + if (isEditing) { + return "<span class='skip-button'>Annuleren</span>"; + } + else { + return "<span class='skip-button'>Overslaan (Ik weet het niet zeker...)</span>"; + } + }); + // And at last, set up the skip button + _this._skipButton = new VariableUIElement_1.VariableUiElement(cancelContents).onClick(cancel); + return _this; + } + TagRendering.prototype.InputElementFor = function (options) { + var elements = []; + if (options.mappings !== undefined) { + for (var _i = 0, _a = options.mappings; _i < _a.length; _i++) { + var mapping = _a[_i]; + elements.push(this.InputElementForMapping(mapping)); + } + } + if (options.freeform !== undefined) { + elements.push(this.InputForFreeForm(options.freeform)); + } + if (elements.length == 0) { + throw "NO TAGRENDERINGS!"; + } + if (elements.length == 1) { + return elements[0]; + } + return new RadioButton_1.RadioButton(elements, false); + }; + TagRendering.prototype.InputElementForMapping = function (mapping) { + return new FixedInputElement_1.FixedInputElement(mapping.txt, mapping.k); + }; + TagRendering.prototype.InputForFreeForm = function (freeform) { + if (freeform === undefined) { + return undefined; + } + var pickString = function (string) { + if (string === "" || string === undefined) { + return undefined; + } + var tag = new TagsFilter_1.Tag(freeform.key, string); + if (freeform.extraTags === undefined) { + return tag; + } + return new TagsFilter_1.And([ + freeform.extraTags, + tag + ]); + }; + var toString = function (tag) { + if (tag instanceof TagsFilter_1.And) { + return toString(tag.and[0]); + } + else if (tag instanceof TagsFilter_1.Tag) { + return tag.value; + } + return undefined; + }; + var inputElement; + var textField = new TextField_1.TextField({ + placeholder: this._freeform.placeholder, + fromString: pickString, + toString: toString + }); + var prepost = freeform.template.split("$$$"); + return new InputElementWrapper_1.InputElementWrapper(prepost[0], textField, prepost[1]); + }; + TagRendering.prototype.IsKnown = function () { + var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); + for (var _i = 0, _a = this._mapping.concat(this._renderMapping); _i < _a.length; _i++) { + var oneOnOneElement = _a[_i]; + if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { + return true; + } + } + return this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined; + }; + TagRendering.prototype.CurrentValue = function () { + var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); + for (var _i = 0, _a = this._mapping.concat(this._renderMapping); _i < _a.length; _i++) { + var oneOnOneElement = _a[_i]; + if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { + return oneOnOneElement.k; + } + } + if (this._freeform === undefined) { + return undefined; + } + return new TagsFilter_1.Tag(this._freeform.key, this._source.data[this._freeform.key]); + }; + TagRendering.prototype.IsQuestioning = function () { + if (this.IsKnown()) { + return false; + } + if (this._question === undefined) { + // We don't ask this question in the first place + return false; + } + if (this._questionSkipped.data) { + // We don't ask for this question anymore, skipped by user + return false; + } + return true; + }; + TagRendering.prototype.RenderAnwser = function () { + var _a; + var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); + var freeform = ""; + var freeformScore = -10; + if (this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined) { + freeform = this.ApplyTemplate(this._freeform.renderTemplate); + freeformScore = 0; + } + var highestScore = -100; + var highestTemplate = undefined; + for (var _i = 0, _b = this._mapping.concat(this._renderMapping); _i < _b.length; _i++) { + var oneOnOneElement = _b[_i]; + if (oneOnOneElement.k == null || + oneOnOneElement.k.matches(tags)) { + // We have found a matching key -> we use the template, but only if it scores better + var score = (_a = oneOnOneElement.priority) !== null && _a !== void 0 ? _a : (oneOnOneElement.k === null ? -1 : 0); + if (score > highestScore) { + highestScore = score; + highestTemplate = oneOnOneElement.txt; + } + } + } + if (freeformScore > highestScore) { + return freeform; + } + if (highestTemplate !== undefined) { + // we render the found template + return this._primer + this.ApplyTemplate(highestTemplate); + } + }; + TagRendering.prototype.InnerRender = function () { + if (this.IsQuestioning() || this._editMode.data) { + // Not yet known or questioning, we have to ask a question + return "<div class='question'>" + + "<span class='question-text'>" + this._question + "</span>" + + (this._question !== "" ? "<br/>" : "") + + this._questionElement.Render() + + this._skipButton.Render() + + this._saveButton.Render() + + "</div>"; + } + if (this.IsKnown()) { + var html = this.RenderAnwser(); + if (html == "") { + return ""; + } + var editButton = ""; + if (this._userDetails.data.loggedIn) { + editButton = this._editButton.Render(); + } + return "<span class='answer'>" + + "<span class='answer-text'>" + html + "</span>" + + editButton + + "</span>"; + } + return ""; + }; + TagRendering.prototype.Priority = function () { + return this._priority; + }; + TagRendering.prototype.ApplyTemplate = function (template) { + var tags = this._tagsPreprocessor(this._source.data); + return TagsFilter_1.TagUtils.ApplyTemplate(template, tags); + }; + return TagRendering; +}(UIElement_1.UIElement)); diff --git a/Customizations/UIElementConstructor.js b/Customizations/UIElementConstructor.js new file mode 100644 index 0000000..620512d --- /dev/null +++ b/Customizations/UIElementConstructor.js @@ -0,0 +1,25 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TagDependantUIElement = void 0; +var UIElement_1 = require("../UI/UIElement"); +var TagDependantUIElement = /** @class */ (function (_super) { + __extends(TagDependantUIElement, _super); + function TagDependantUIElement() { + return _super !== null && _super.apply(this, arguments) || this; + } + return TagDependantUIElement; +}(UIElement_1.UIElement)); +exports.TagDependantUIElement = TagDependantUIElement; diff --git a/Helpers.js b/Helpers.js new file mode 100644 index 0000000..e86bd37 --- /dev/null +++ b/Helpers.js @@ -0,0 +1,57 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Helpers = void 0; +var Helpers = /** @class */ (function () { + function Helpers() { + } + Helpers.DoEvery = function (millis, f) { + window.setTimeout(function () { + f(); + Helpers.DoEvery(millis, f); + }, millis); + }; + Helpers.SetupAutoSave = function (changes, millisTillChangesAreSaved, saveAfterXMillis) { + changes.pendingChangesES.addCallback(function () { + var c = changes.pendingChangesES.data; + if (c > 10) { + millisTillChangesAreSaved.setData(0); + changes.uploadAll(undefined); + return; + } + if (c > 0) { + millisTillChangesAreSaved.setData(saveAfterXMillis); + } + }); + millisTillChangesAreSaved.addCallback(function (time) { + if (time <= 0 && changes.pendingChangesES.data > 0) { + changes.uploadAll(undefined); + } + }); + Helpers.DoEvery(1000, function () { + millisTillChangesAreSaved + .setData(millisTillChangesAreSaved.data - 1000); + }); + }; + /* + * Registers an action that: + * -> Upload everything to OSM + * -> Asks the user not to close. The 'not to close' dialog should profide enough time to upload + * -> WHen uploading is done, the window is closed anyway + */ + Helpers.LastEffortSave = function (changes) { + window.addEventListener("beforeunload", function (e) { + // Quickly save everyting! + if (changes.pendingChangesES.data == 0) { + return ""; + } + changes.uploadAll(function () { + window.close(); + }); + var confirmationMessage = "Nog even geduld - je laatset wijzigingen worden opgeslaan!"; + (e || window.event).returnValue = confirmationMessage; //Gecko + IE + return confirmationMessage; //Webkit, Safari, Chrome + }); + }; + return Helpers; +}()); +exports.Helpers = Helpers; diff --git a/Logic/Basemap.js b/Logic/Basemap.js new file mode 100644 index 0000000..4f35171 --- /dev/null +++ b/Logic/Basemap.js @@ -0,0 +1,70 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Basemap = void 0; +var leaflet_1 = require("leaflet"); +var UIEventSource_1 = require("../UI/UIEventSource"); +// Contains all setup and baselayers for Leaflet stuff +var Basemap = /** @class */ (function () { + function Basemap(leafletElementId, location, extraAttribution) { + this.LastClickLocation = new UIEventSource_1.UIEventSource(undefined); + this.aivLucht2013Layer = leaflet_1.default.tileLayer.wms('https://geoservices.informatievlaanderen.be/raadpleegdiensten/OGW/wms?s', { + layers: "OGWRGB13_15VL", + attribution: "Luchtfoto's van © AIV Vlaanderen (2013-2015) | " + }); + this.aivLuchtLatestLayer = leaflet_1.default.tileLayer("https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&" + + "LAYER=omwrgbmrvl&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileRow={y}&tileCol={x}", { + // omwrgbmrvl + attribution: 'Luchtfoto\'s van © AIV Vlaanderen (Laatste) © AGIV', + maxZoom: 20, + minZoom: 1, + wmts: true + }); + this.osmLayer = leaflet_1.default.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { + attribution: '', + maxZoom: 19, + minZoom: 1 + }); + this.osmBeLayer = leaflet_1.default.tileLayer("https://tile.osm.be/osmbe/{z}/{x}/{y}.png", { + attribution: '<a href="https://geo6.be/">Tile hosting courtesy of Geo6</a>', + maxZoom: 18, + minZoom: 1 + }); + this.grbLayer = leaflet_1.default.tileLayer("https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=grb_bsk&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileCol={x}&tileRow={y}", { + attribution: 'Achtergrond <i>Grootschalig ReferentieBestand</i>(GRB) © AGIV', + maxZoom: 20, + minZoom: 1, + wmts: true + }); + this.baseLayers = { + "Luchtfoto Vlaanderen (recentste door AIV)": this.aivLuchtLatestLayer, + "Luchtfoto Vlaanderen (2013-2015, door AIV)": this.aivLucht2013Layer, + "Kaart van OpenStreetMap": this.osmLayer, + "Kaart Grootschalig ReferentieBestand Vlaanderen (GRB) door AIV": this.grbLayer + }; + this.map = leaflet_1.default.map(leafletElementId, { + center: [location.data.lat, location.data.lon], + zoom: location.data.zoom, + layers: [this.osmLayer], + }); + this.map.attributionControl.setPrefix(extraAttribution.Render() + " | <a href='https://osm.org'>OpenStreetMap</a>"); + this.Location = location; + var layerControl = leaflet_1.default.control.layers(this.baseLayers, null, { + position: 'bottomright', + hideSingleBase: true + }); + layerControl.addTo(this.map); + this.map.zoomControl.setPosition("bottomright"); + var self = this; + this.map.on("moveend", function () { + location.data.zoom = self.map.getZoom(); + location.data.lat = self.map.getCenter().lat; + location.data.lon = self.map.getCenter().lng; + location.ping(); + }); + this.map.on("click", function (e) { + self.LastClickLocation.setData({ lat: e.latlng.lat, lon: e.latlng.lng }); + }); + } + return Basemap; +}()); +exports.Basemap = Basemap; diff --git a/Logic/Changes.js b/Logic/Changes.js new file mode 100644 index 0000000..12edb7e --- /dev/null +++ b/Logic/Changes.js @@ -0,0 +1,222 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Changes = void 0; +var OsmObject_1 = require("./OsmObject"); +var UIEventSource_1 = require("../UI/UIEventSource"); +var TagsFilter_1 = require("./TagsFilter"); +var Changes = /** @class */ (function () { + function Changes(changesetComment, login, allElements) { + this._pendingChanges = []; // Gets reset on uploadAll + this.newElements = []; // Gets reset on uploadAll + this.pendingChangesES = new UIEventSource_1.UIEventSource(this._pendingChanges.length); + this.isSaving = new UIEventSource_1.UIEventSource(false); + this._changesetComment = changesetComment; + this.login = login; + this._allElements = allElements; + } + Changes.prototype.addTag = function (elementId, tagsFilter) { + if (tagsFilter instanceof TagsFilter_1.Tag) { + var tag = tagsFilter; + this.addChange(elementId, tag.key, tag.value); + return; + } + if (tagsFilter instanceof TagsFilter_1.And) { + var and = tagsFilter; + for (var _i = 0, _a = and.and; _i < _a.length; _i++) { + var tag = _a[_i]; + this.addTag(elementId, tag); + } + return; + } + console.log("Unsupported tagsfilter element to addTag", tagsFilter); + throw "Unsupported tagsFilter element"; + }; + /** + * Adds a change to the pending changes + * @param elementId + * @param key + * @param value + */ + Changes.prototype.addChange = function (elementId, key, value) { + console.log("Received change", key, value); + if (key === undefined || key === null) { + console.log("Invalid key"); + return; + } + if (value === undefined || value === null) { + console.log("Invalid value for ", key); + return; + } + var eventSource = this._allElements.getElement(elementId); + eventSource.data[key] = value; + eventSource.ping(); + // We get the id from the event source, as that ID might be rewritten + this._pendingChanges.push({ elementId: eventSource.data.id, key: key, value: value }); + this.pendingChangesES.setData(this._pendingChanges.length); + }; + /** + * Create a new node element at the given lat/long. + * An internal OsmObject is created to upload later on, a geojson represention is returned. + * Note that the geojson version shares the tags (properties) by pointer, but has _no_ id in properties + */ + Changes.prototype.createElement = function (basicTags, lat, lon) { + var osmNode = new OsmObject_1.OsmNode(Changes._nextId); + this.newElements.push(osmNode); + Changes._nextId--; + var id = "node/" + osmNode.id; + osmNode.lat = lat; + osmNode.lon = lon; + var properties = { id: id }; + var geojson = { + "type": "Feature", + "properties": properties, + "id": id, + "geometry": { + "type": "Point", + "coordinates": [ + lon, + lat + ] + } + }; + this._allElements.addOrGetElement(geojson); + // The basictags are COPIED, the id is included in the properties + // The tags are not yet written into the OsmObject, but this is applied onto a + for (var _i = 0, basicTags_1 = basicTags; _i < basicTags_1.length; _i++) { + var kv = basicTags_1[_i]; + this.addChange(id, kv.key, kv.value); // We use the call, to trigger all the other machinery (including updating the geojson itsel + properties[kv.key] = kv.value; + } + return geojson; + }; + Changes.prototype.uploadAll = function (optionalContinuation) { + if (optionalContinuation === void 0) { optionalContinuation = undefined; } + var self = this; + this.isSaving.setData(true); + var optionalContinuationWrapped = function () { + self.isSaving.setData(false); + if (optionalContinuation) { + optionalContinuation(); + } + }; + var pending = this._pendingChanges; + this._pendingChanges = []; + this.pendingChangesES.setData(this._pendingChanges.length); + var newElements = this.newElements; + this.newElements = []; + var knownElements = {}; // maps string --> OsmObject + function DownloadAndContinue(neededIds, continuation) { + // local function which downloads all the objects one by one + // this is one big loop, running one download, then rerunning the entire function + if (neededIds.length == 0) { + continuation(); + return; + } + var neededId = neededIds.pop(); + if (neededId in knownElements) { + DownloadAndContinue(neededIds, continuation); + return; + } + console.log("Downloading ", neededId); + OsmObject_1.OsmObject.DownloadObject(neededId, function (element) { + knownElements[neededId] = element; // assign the element for later, continue downloading the next element + DownloadAndContinue(neededIds, continuation); + }); + } + var neededIds = []; + for (var _i = 0, pending_1 = pending; _i < pending_1.length; _i++) { + var change = pending_1[_i]; + var id = change.elementId; + if (parseFloat(id.split("/")[1]) < 0) { + console.log("Detected a new element! Exciting!"); + } + else { + neededIds.push(id); + } + } + DownloadAndContinue(neededIds, function () { + // Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements' + // We apply the changes on them + for (var _i = 0, pending_2 = pending; _i < pending_2.length; _i++) { + var change = pending_2[_i]; + if (parseInt(change.elementId.split("/")[1]) < 0) { + // This is a new element - we should apply this on one of the new elements + for (var _a = 0, newElements_1 = newElements; _a < newElements_1.length; _a++) { + var newElement = newElements_1[_a]; + if (newElement.type + "/" + newElement.id === change.elementId) { + newElement.addTag(change.key, change.value); + } + } + } + else { + console.log(knownElements, change.elementId); + knownElements[change.elementId].addTag(change.key, change.value); + // note: addTag will flag changes with 'element.changed' internally + } + } + // Small sanity check for duplicate information + var changedElements = []; + for (var elementId in knownElements) { + var element = knownElements[elementId]; + if (element.changed) { + changedElements.push(element); + } + } + if (changedElements.length == 0 && newElements.length == 0) { + console.log("No changes in any object"); + return; + } + var handleMapping = function (idMapping) { + for (var oldId in idMapping) { + var newId = idMapping[oldId]; + var element = self._allElements.getElement(oldId); + element.data.id = newId; + self._allElements.addElementById(newId, element); + element.ping(); + } + }; + console.log("Beginning upload..."); + // At last, we build the changeset and upload + self.login.UploadChangeset(self._changesetComment, function (csId) { + var modifications = ""; + for (var _i = 0, changedElements_1 = changedElements; _i < changedElements_1.length; _i++) { + var element = changedElements_1[_i]; + if (!element.changed) { + continue; + } + modifications += element.ChangesetXML(csId) + "\n"; + } + var creations = ""; + for (var _a = 0, newElements_2 = newElements; _a < newElements_2.length; _a++) { + var newElement = newElements_2[_a]; + creations += newElement.ChangesetXML(csId); + } + var changes = "<osmChange version='0.6' generator='Mapcomplete 0.0.1'>"; + if (creations.length > 0) { + changes += + "<create>" + + creations + + "</create>"; + } + if (modifications.length > 0) { + changes += + "<modify>" + + modifications + + "</modify>"; + } + changes += "</osmChange>"; + return changes; + }, handleMapping, optionalContinuationWrapped); + }); + }; + Changes.prototype.asQuestions = function (qs) { + var ls = []; + for (var i in qs) { + ls.push(new Question(this, qs[i])); + } + return ls; + }; + Changes._nextId = -1; // New assined ID's are negative + return Changes; +}()); +exports.Changes = Changes; diff --git a/Logic/ElementStorage.js b/Logic/ElementStorage.js new file mode 100644 index 0000000..d8517a8 --- /dev/null +++ b/Logic/ElementStorage.js @@ -0,0 +1,51 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ElementStorage = void 0; +/** + * Keeps track of a dictionary 'elementID' -> element + */ +var UIEventSource_1 = require("../UI/UIEventSource"); +var ElementStorage = /** @class */ (function () { + function ElementStorage() { + this._elements = []; + } + ElementStorage.prototype.addElementById = function (id, eventSource) { + this._elements[id] = eventSource; + }; + ElementStorage.prototype.addElement = function (element) { + var eventSource = new UIEventSource_1.UIEventSource(element.properties); + this._elements[element.properties.id] = eventSource; + return eventSource; + }; + ElementStorage.prototype.addOrGetElement = function (element) { + var elementId = element.properties.id; + if (elementId in this._elements) { + var es = this._elements[elementId]; + var keptKeys = es.data; + // The element already exists + // We add all the new keys to the old keys + for (var k in element.properties) { + var v = element.properties[k]; + if (keptKeys[k] !== v) { + keptKeys[k] = v; + es.ping(); + } + } + return es; + } + else { + return this.addElement(element); + } + }; + ElementStorage.prototype.getElement = function (elementId) { + if (elementId in this._elements) { + return this._elements[elementId]; + } + console.log("Can not find eventsource with id ", elementId); + }; + ElementStorage.prototype.removeId = function (oldId) { + delete this._elements[oldId]; + }; + return ElementStorage; +}()); +exports.ElementStorage = ElementStorage; diff --git a/Logic/FilteredLayer.js b/Logic/FilteredLayer.js new file mode 100644 index 0000000..ec11e51 --- /dev/null +++ b/Logic/FilteredLayer.js @@ -0,0 +1,173 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FilteredLayer = void 0; +var TagsFilter_1 = require("./TagsFilter"); +var UIEventSource_1 = require("../UI/UIEventSource"); +var leaflet_1 = require("leaflet"); +var GeoOperations_1 = require("./GeoOperations"); +/*** + * A filtered layer is a layer which offers a 'set-data' function + * It is initialized with a tagfilter. + * + * When geojson-data is given to 'setData', all the geojson matching the filter, is rendered on this layer. + * If it is not rendered, it is returned in a 'leftOver'-geojson; which can be consumed by the next layer. + * + * This also makes sure that no objects are rendered twice if they are applicable on two layers + */ +var FilteredLayer = /** @class */ (function () { + function FilteredLayer(name, map, storage, changes, filters, maxAllowedOverlap, style, selectedElement, showOnPopup) { + this.isDisplayed = new UIEventSource_1.UIEventSource(true); + /** List of new elements, geojson features + */ + this._newElements = []; + this._selectedElement = selectedElement; + this._showOnPopup = showOnPopup; + if (style === undefined) { + style = function () { + return {}; + }; + } + this.name = name; + this._map = map; + this.filters = filters; + this._style = style; + this._storage = storage; + this._maxAllowedOverlap = maxAllowedOverlap; + var self = this; + this.isDisplayed.addCallback(function (isDisplayed) { + if (self._geolayer !== undefined && self._geolayer !== null) { + if (isDisplayed) { + self._geolayer.addTo(self._map.map); + } + else { + self._map.map.removeLayer(self._geolayer); + } + } + }); + } + /** + * The main function to load data into this layer. + * The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered + */ + FilteredLayer.prototype.SetApplicableData = function (geojson) { + var leftoverFeatures = []; + var selfFeatures = []; + for (var _i = 0, _a = geojson.features; _i < _a.length; _i++) { + var feature = _a[_i]; + // feature.properties contains all the properties + var tags = TagsFilter_1.TagUtils.proprtiesToKV(feature.properties); + if (this.filters.matches(tags)) { + selfFeatures.push(feature); + } + else { + leftoverFeatures.push(feature); + } + } + this.RenderLayer({ + type: "FeatureCollection", + features: selfFeatures + }); + var notShadowed = []; + for (var _b = 0, leftoverFeatures_1 = leftoverFeatures; _b < leftoverFeatures_1.length; _b++) { + var feature = leftoverFeatures_1[_b]; + if (this._maxAllowedOverlap !== undefined && this._maxAllowedOverlap > 0) { + if (GeoOperations_1.GeoOperations.featureIsContainedInAny(feature, selfFeatures, this._maxAllowedOverlap)) { + // This feature is filtered away + continue; + } + } + notShadowed.push(feature); + } + return { + type: "FeatureCollection", + features: notShadowed + }; + }; + FilteredLayer.prototype.AddNewElement = function (element) { + this._newElements.push(element); + console.log("Element added"); + this.RenderLayer(this._dataFromOverpass); // Update the layer + }; + FilteredLayer.prototype.RenderLayer = function (data) { + var self = this; + if (this._geolayer !== undefined && this._geolayer !== null) { + this._map.map.removeLayer(this._geolayer); + } + this._dataFromOverpass = data; + var fusedFeatures = []; + var idsFromOverpass = []; + for (var _i = 0, _a = data.features; _i < _a.length; _i++) { + var feature = _a[_i]; + idsFromOverpass.push(feature.properties.id); + fusedFeatures.push(feature); + } + for (var _b = 0, _c = this._newElements; _b < _c.length; _b++) { + var feature = _c[_b]; + if (idsFromOverpass.indexOf(feature.properties.id) < 0) { + // This element is not yet uploaded or not yet visible in overpass + // We include it in the layer + fusedFeatures.push(feature); + } + } + // We use a new, fused dataset + data = { + type: "FeatureCollection", + features: fusedFeatures + }; + // The data is split in two parts: the poinst and the rest + // The points get a special treatment in order to render them properly + // Note that some features might get a point representation as well + this._geolayer = leaflet_1.default.geoJSON(data, { + style: function (feature) { + return self._style(feature.properties); + }, + pointToLayer: function (feature, latLng) { + var style = self._style(feature.properties); + var marker; + if (style.icon === undefined) { + marker = leaflet_1.default.circle(latLng, { + radius: 25, + color: style.color + }); + } + else { + marker = leaflet_1.default.marker(latLng, { + icon: style.icon + }); + } + return marker; + }, + onEachFeature: function (feature, layer) { + var eventSource = self._storage.addOrGetElement(feature); + eventSource.addCallback(function () { + if (layer.setIcon) { + layer.setIcon(self._style(feature.properties).icon); + } + else { + console.log("UPdating", layer); + self._geolayer.setStyle(function (feature) { + return self._style(feature.properties); + }); + } + }); + layer.on("click", function (e) { + console.log("Selected ", feature); + self._selectedElement.setData(feature.properties); + var uiElement = self._showOnPopup(eventSource); + var popup = leaflet_1.default.popup() + .setContent(uiElement.Render()) + .setLatLng(e.latlng) + .openOn(self._map.map); + uiElement.Update(); + uiElement.Activate(); + leaflet_1.default.DomEvent.stop(e); // Marks the event as consumed + }); + } + }); + if (this.isDisplayed.data) { + this._geolayer.addTo(this._map.map); + } + }; + return FilteredLayer; +}()); +exports.FilteredLayer = FilteredLayer; diff --git a/Logic/GeoLocationHandler.js b/Logic/GeoLocationHandler.js new file mode 100644 index 0000000..75c23d4 --- /dev/null +++ b/Logic/GeoLocationHandler.js @@ -0,0 +1,117 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GeoLocationHandler = void 0; +var UIEventSource_1 = require("../UI/UIEventSource"); +var UIElement_1 = require("../UI/UIElement"); +var leaflet_1 = require("leaflet"); +var Helpers_1 = require("../Helpers"); +var GeoLocationHandler = /** @class */ (function (_super) { + __extends(GeoLocationHandler, _super); + function GeoLocationHandler(map) { + var _this = _super.call(this, undefined) || this; + _this.currentLocation = new UIEventSource_1.UIEventSource(undefined); + _this._isActive = new UIEventSource_1.UIEventSource(false); + _this._permission = new UIEventSource_1.UIEventSource(""); + _this._map = map; + _this.ListenTo(_this.currentLocation); + _this.ListenTo(_this._isActive); + _this.ListenTo(_this._permission); + var self = _this; + function onAccuratePositionProgress(e) { + console.log(e.accuracy); + console.log(e.latlng); + self.currentLocation.setData({ latlng: e.latlng, accuracy: e.accuracy }); + } + function onAccuratePositionFound(e) { + console.log(e.accuracy); + console.log(e.latlng); + self.currentLocation.setData({ latlng: e.latlng, accuracy: e.accuracy }); + } + function onAccuratePositionError(e) { + console.log("onerror", e.message); + } + map.map.on('accuratepositionprogress', onAccuratePositionProgress); + map.map.on('accuratepositionfound', onAccuratePositionFound); + map.map.on('accuratepositionerror', onAccuratePositionError); + var icon = leaflet_1.default.icon({ + iconUrl: './assets/crosshair-blue.svg', + iconSize: [40, 40], + iconAnchor: [20, 20], + }); + _this.currentLocation.addCallback(function (location) { + var newMarker = leaflet_1.default.marker(location.latlng, { icon: icon }); + newMarker.addTo(map.map); + if (self._marker !== undefined) { + map.map.removeLayer(self._marker); + } + self._marker = newMarker; + }); + navigator.permissions.query({ name: 'geolocation' }) + .then(function (status) { + console.log("Geolocation is already", status); + if (status.state === "granted") { + self.StartGeolocating(); + } + self._permission.setData(status.state); + status.onchange = function () { + self._permission.setData(status.state); + }; + }); + _this.HideOnEmpty(true); + return _this; + } + GeoLocationHandler.prototype.InnerRender = function () { + if (this.currentLocation.data) { + return "<img src='./assets/crosshair-blue.svg' alt='locate me'>"; + } + if (this._isActive.data) { + return "<img src='./assets/crosshair-blue-center.svg' alt='locate me'>"; + } + return "<img src='./assets/crosshair.svg' alt='locate me'>"; + }; + GeoLocationHandler.prototype.StartGeolocating = function () { + var self = this; + if (self._permission.data === "denied") { + return ""; + } + if (self.currentLocation.data !== undefined) { + self._map.map.flyTo(self.currentLocation.data.latlng, 18); + } + console.log("Searching location using GPS"); + self._map.map.findAccuratePosition({ + maxWait: 10000, + desiredAccuracy: 50 // defaults to 20 + }); + if (!self._isActive.data) { + self._isActive.setData(true); + Helpers_1.Helpers.DoEvery(60000, function () { + self._map.map.findAccuratePosition({ + maxWait: 10000, + desiredAccuracy: 50 // defaults to 20 + }); + }); + } + }; + GeoLocationHandler.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + var self = this; + htmlElement.onclick = function () { + self.StartGeolocating(); + }; + }; + return GeoLocationHandler; +}(UIElement_1.UIElement)); +exports.GeoLocationHandler = GeoLocationHandler; diff --git a/Logic/GeoOperations.js b/Logic/GeoOperations.js new file mode 100644 index 0000000..f3d1f4e --- /dev/null +++ b/Logic/GeoOperations.js @@ -0,0 +1,185 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GeoOperations = void 0; +var turf = require("turf"); +var GeoOperations = /** @class */ (function () { + function GeoOperations() { + } + GeoOperations.surfaceAreaInSqMeters = function (feature) { + return turf.area(feature); + }; + GeoOperations.featureIsContainedInAny = function (feature, shouldNotContain, maxOverlapPercentage) { + // Returns 'false' if no problematic intersection is found + if (feature.geometry.type === "Point") { + var coor = feature.geometry.coordinates; + for (var _i = 0, shouldNotContain_1 = shouldNotContain; _i < shouldNotContain_1.length; _i++) { + var shouldNotContainElement = shouldNotContain_1[_i]; + var shouldNotContainBBox = BBox.get(shouldNotContainElement); + var featureBBox = BBox.get(feature); + if (!featureBBox.overlapsWith(shouldNotContainBBox)) { + continue; + } + if (this.inside(coor, shouldNotContainElement)) { + return true; + } + } + return false; + } + if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") { + var poly = feature; + var featureBBox = BBox.get(feature); + var featureSurface = GeoOperations.surfaceAreaInSqMeters(poly); + for (var _a = 0, shouldNotContain_2 = shouldNotContain; _a < shouldNotContain_2.length; _a++) { + var shouldNotContainElement = shouldNotContain_2[_a]; + var shouldNotContainBBox = BBox.get(shouldNotContainElement); + var overlaps = featureBBox.overlapsWith(shouldNotContainBBox); + if (!overlaps) { + continue; + } + // Calculate the surface area of the intersection + // If it is too big, refuse + try { + var intersection = turf.intersect(poly, shouldNotContainElement); + if (intersection == null) { + continue; + } + var intersectionSize = turf.area(intersection); + var ratio = intersectionSize / featureSurface; + if (ratio * 100 >= maxOverlapPercentage) { + console.log("Refused", poly.id, " due to ", shouldNotContainElement.id, "intersection ratio is ", ratio, "which is bigger then the target ratio of ", (maxOverlapPercentage / 100)); + return true; + } + } + catch (exception) { + console.log("EXCEPTION CAUGHT WHILE INTERSECTING: ", exception); + // We assume that this failed due to an intersection + return true; + } + } + return false; // No problematic intersections found + } + return false; + }; + /** + * Simple check: that every point of the polygon is inside the container + * @param polygon + * @param container + */ + GeoOperations.isPolygonInside = function (polygon, container) { + for (var _i = 0, _a = polygon.geometry.coordinates[0]; _i < _a.length; _i++) { + var coor = _a[_i]; + if (!GeoOperations.inside(coor, container)) { + return false; + } + } + return true; + }; + /** + * Simple check: one point of the polygon is inside the container + * @param polygon + * @param container + */ + GeoOperations.isPolygonTouching = function (polygon, container) { + for (var _i = 0, _a = polygon.geometry.coordinates[0]; _i < _a.length; _i++) { + var coor = _a[_i]; + if (GeoOperations.inside(coor, container)) { + return true; + } + } + return false; + }; + GeoOperations.inside = function (pointCoordinate, feature) { + // ray-casting algorithm based on + // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + if (feature.geometry.type === "Point") { + return false; + } + var x = pointCoordinate[0]; + var y = pointCoordinate[1]; + var poly = feature.geometry.coordinates[0]; + var inside = false; + for (var i = 0, j = poly.length - 1; i < poly.length; j = i++) { + var coori = poly[i]; + var coorj = poly[j]; + var xi = coori[0]; + var yi = coori[1]; + var xj = coorj[0]; + var yj = coorj[1]; + var intersect = ((yi > y) != (yj > y)) + && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); + if (intersect) { + inside = !inside; + } + } + return inside; + }; + ; + return GeoOperations; +}()); +exports.GeoOperations = GeoOperations; +var BBox = /** @class */ (function () { + function BBox(coordinates) { + this.maxLat = Number.MIN_VALUE; + this.maxLon = Number.MIN_VALUE; + this.minLat = Number.MAX_VALUE; + this.minLon = Number.MAX_VALUE; + for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) { + var coordinate = coordinates_1[_i]; + this.maxLon = Math.max(this.maxLon, coordinate[0]); + this.maxLat = Math.max(this.maxLat, coordinate[1]); + this.minLon = Math.min(this.minLon, coordinate[0]); + this.minLat = Math.min(this.minLat, coordinate[1]); + } + this.check(); + } + BBox.prototype.check = function () { + if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { + console.log(this); + throw "BBOX has NAN"; + } + }; + BBox.prototype.overlapsWith = function (other) { + this.check(); + other.check(); + if (this.maxLon < other.minLon) { + return false; + } + if (this.maxLat < other.minLat) { + return false; + } + if (this.minLon > other.maxLon) { + return false; + } + if (this.minLat > other.maxLat) { + return false; + } + return true; + }; + BBox.get = function (feature) { + if (feature.bbox === undefined) { + if (feature.geometry.type === "MultiPolygon") { + var coordinates = []; + for (var _i = 0, _a = feature.geometry.coordinates; _i < _a.length; _i++) { + var coorlist = _a[_i]; + coordinates = coordinates.concat(coorlist[0]); + } + feature.bbox = new BBox(coordinates); + } + else if (feature.geometry.type === "Polygon") { + feature.bbox = new BBox(feature.geometry.coordinates[0]); + } + else if (feature.geometry.type === "LineString") { + feature.bbox = new BBox(feature.geometry.coordinates); + } + else if (feature.geometry.type === "Point") { + // Point + feature.bbox = new BBox([feature.geometry.coordinates]); + } + else { + throw "Cannot calculate bbox, unknown type " + feature.geometry.type; + } + } + return feature.bbox; + }; + return BBox; +}()); diff --git a/Logic/Geocoding.js b/Logic/Geocoding.js new file mode 100644 index 0000000..afde3b4 --- /dev/null +++ b/Logic/Geocoding.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Geocoding = void 0; +var $ = require("jquery"); +var Geocoding = /** @class */ (function () { + function Geocoding() { + } + Geocoding.Search = function (query, basemap, handleResult, onFail) { + var b = basemap.map.getBounds(); + console.log(b); + $.getJSON(Geocoding.host + "format=json&limit=1&viewbox=" + + (b.getEast() + "," + b.getNorth() + "," + b.getWest() + "," + b.getSouth()) + + "&accept-language=nl&q=" + query, function (data) { + handleResult(data); + }).fail(function () { + onFail(); + }); + }; + Geocoding.host = "https://nominatim.openstreetmap.org/search?"; + return Geocoding; +}()); +exports.Geocoding = Geocoding; diff --git a/Logic/ImageSearcher.js b/Logic/ImageSearcher.js new file mode 100644 index 0000000..e107489 --- /dev/null +++ b/Logic/ImageSearcher.js @@ -0,0 +1,185 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageSearcher = void 0; +var UIEventSource_1 = require("../UI/UIEventSource"); +var Wikimedia_1 = require("./Wikimedia"); +var WikimediaImage_1 = require("../UI/Image/WikimediaImage"); +var SimpleImageElement_1 = require("../UI/Image/SimpleImageElement"); +var ImgurImage_1 = require("../UI/Image/ImgurImage"); +/** + * There are multiple way to fetch images for an object + * 1) There is an image tag + * 2) There is an image tag, the image tag contains multiple ';'-seperated URLS + * 3) there are multiple image tags, e.g. 'image', 'image:0', 'image:1', and 'image_0', 'image_1' - however, these are pretty rare so we are gonna ignore them + * 4) There is a wikimedia_commons-tag, which either has a 'File': or a 'category:' containing images + * 5) There is a wikidata-tag, and the wikidata item either has an 'image' attribute or has 'a link to a wikimedia commons category' + * 6) There is a wikipedia article, from which we can deduct the wikidata item + * + * For some images, author and license should be shown + */ +/** + * Class which search for all the possible locations for images and which builds a list of UI-elements for it. + * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel + */ +var ImageSearcher = /** @class */ (function (_super) { + __extends(ImageSearcher, _super); + function ImageSearcher(tags, changes) { + var _this = _super.call(this, []) || this; + _this._wdItem = new UIEventSource_1.UIEventSource(""); + _this._commons = new UIEventSource_1.UIEventSource(""); + _this._activated = false; + _this._deletedImages = new UIEventSource_1.UIEventSource([]); + _this._tags = tags; + _this._changes = changes; + var self = _this; + _this._wdItem.addCallback(function () { + // Load the wikidata item, then detect usage on 'commons' + var wikidataId = self._wdItem.data; + // @ts-ignore + if (wikidataId.startsWith("Q")) { + wikidataId = wikidataId.substr(1); + } + Wikimedia_1.Wikimedia.GetWikiData(parseInt(wikidataId), function (wd) { + self.AddImage(wd.image); + Wikimedia_1.Wikimedia.GetCategoryFiles(wd.commonsWiki, function (images) { + for (var _i = 0, _a = images.images; _i < _a.length; _i++) { + var image = _a[_i]; + // @ts-ignore + if (image.startsWith("File:")) { + self.AddImage(image); + } + } + }); + }); + }); + _this._commons.addCallback(function () { + var commons = self._commons.data; + // @ts-ignore + if (commons.startsWith("Category:")) { + Wikimedia_1.Wikimedia.GetCategoryFiles(commons, function (images) { + for (var _i = 0, _a = images.images; _i < _a.length; _i++) { + var image = _a[_i]; + // @ts-ignore + if (image.startsWith("File:")) { + self.AddImage(image); + } + } + }); + } + else { // @ts-ignore + if (commons.startsWith("File:")) { + self.AddImage(commons); + } + } + }); + return _this; + } + ImageSearcher.prototype.AddImage = function (url) { + if (url === undefined || url === null || url === "") { + return; + } + for (var _i = 0, _a = this.data; _i < _a.length; _i++) { + var el = _a[_i]; + if (el === url) { + return; + } + } + this.data.push(url); + this.ping(); + }; + ImageSearcher.prototype.ImageKey = function (url) { + var tgs = this._tags.data; + for (var key in tgs) { + if (tgs[key] === url) { + return key; + } + } + return undefined; + }; + ImageSearcher.prototype.IsDeletable = function (url) { + return this.ImageKey(url) !== undefined; + }; + ImageSearcher.prototype.Delete = function (url) { + var key = this.ImageKey(url); + if (key === undefined) { + return; + } + console.log("Deleting image...", key, " --> ", url); + this._changes.addChange(this._tags.data.id, key, ""); + this._deletedImages.data.push(url); + this._deletedImages.ping(); + }; + ImageSearcher.prototype.Activate = function () { + if (this._activated) { + return; + } + this._activated = true; + this.LoadImages(); + var self = this; + this._tags.addCallback(function () { return self.LoadImages(); }); + }; + ImageSearcher.prototype.LoadImages = function () { + if (!this._activated) { + return; + } + var imageTag = this._tags.data.image; + if (imageTag !== undefined) { + var bareImages = imageTag.split(";"); + for (var _i = 0, bareImages_1 = bareImages; _i < bareImages_1.length; _i++) { + var bareImage = bareImages_1[_i]; + this.AddImage(bareImage); + } + } + for (var key in this._tags.data) { + // @ts-ignore + if (key.startsWith("image:")) { + var url = this._tags.data[key]; + this.AddImage(url); + } + } + var wdItem = this._tags.data.wikidata; + if (wdItem !== undefined) { + this._wdItem.setData(wdItem); + } + var commons = this._tags.data.wikimedia_commons; + if (commons !== undefined) { + this._commons.setData(commons); + } + }; + /*** + * Creates either a 'simpleimage' or a 'wikimediaimage' based on the string + * @param url + * @constructor + */ + ImageSearcher.CreateImageElement = function (url) { + // @ts-ignore + if (url.startsWith("File:")) { + return new WikimediaImage_1.WikimediaImage(url); + } + else if (url.startsWith("https://commons.wikimedia.org/wiki/")) { + var commons = url.substr("https://commons.wikimedia.org/wiki/".length); + return new WikimediaImage_1.WikimediaImage(commons); + } + else if (url.startsWith("https://i.imgur.com/")) { + return new ImgurImage_1.ImgurImage(url); + } + else { + return new SimpleImageElement_1.SimpleImageElement(new UIEventSource_1.UIEventSource(url)); + } + }; + return ImageSearcher; +}(UIEventSource_1.UIEventSource)); +exports.ImageSearcher = ImageSearcher; diff --git a/Logic/Imgur.js b/Logic/Imgur.js new file mode 100644 index 0000000..3577753 --- /dev/null +++ b/Logic/Imgur.js @@ -0,0 +1,90 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Imgur = void 0; +var jquery_1 = require("jquery"); +var Wikimedia_1 = require("./Wikimedia"); +var Imgur = /** @class */ (function () { + function Imgur() { + } + Imgur.uploadMultiple = function (title, description, blobs, handleSuccessfullUpload, allDone, offset) { + if (offset === void 0) { offset = 0; } + if (blobs.length == offset) { + allDone(); + return; + } + var blob = blobs.item(offset); + var self = this; + this.uploadImage(title, description, blob, function (imageUrl) { + handleSuccessfullUpload(imageUrl); + self.uploadMultiple(title, description, blobs, handleSuccessfullUpload, allDone, offset + 1); + }); + }; + Imgur.getDescriptionOfImage = function (url, handleDescription) { + var hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]; + var apiUrl = 'https://api.imgur.com/3/image/' + hash; + var apiKey = '7070e7167f0a25a'; + var settings = { + async: true, + crossDomain: true, + processData: false, + contentType: false, + type: 'GET', + url: apiUrl, + headers: { + Authorization: 'Client-ID ' + apiKey, + Accept: 'application/json', + }, + }; + jquery_1.default.ajax(settings).done(function (response) { + var descr = response.data.description; + var data = {}; + for (var _i = 0, _a = descr.split("\n"); _i < _a.length; _i++) { + var tag = _a[_i]; + var kv = tag.split(":"); + var k = kv[0]; + var v = kv[1].replace("\r", ""); + data[k] = v; + } + console.log(data); + var licenseInfo = new Wikimedia_1.LicenseInfo(); + licenseInfo.licenseShortName = data.license; + licenseInfo.artist = data.author; + handleDescription(licenseInfo); + }).fail(function (reason) { + console.log("Getting metadata from to IMGUR failed", reason); + }); + }; + Imgur.uploadImage = function (title, description, blob, handleSuccessfullUpload) { + var apiUrl = 'https://api.imgur.com/3/image'; + var apiKey = '7070e7167f0a25a'; + var settings = { + async: true, + crossDomain: true, + processData: false, + contentType: false, + type: 'POST', + url: apiUrl, + headers: { + Authorization: 'Client-ID ' + apiKey, + Accept: 'application/json', + }, + mimeType: 'multipart/form-data', + }; + var formData = new FormData(); + formData.append('image', blob); + formData.append("title", title); + formData.append("description", description); + // @ts-ignore + settings.data = formData; + // Response contains stringified JSON + // Image URL available at response.data.link + jquery_1.default.ajax(settings).done(function (response) { + response = JSON.parse(response); + handleSuccessfullUpload(response.data.link); + }).fail(function (reason) { + console.log("Uploading to IMGUR failed", reason); + }); + }; + return Imgur; +}()); +exports.Imgur = Imgur; diff --git a/Logic/LayerUpdater.js b/Logic/LayerUpdater.js new file mode 100644 index 0000000..583cf31 --- /dev/null +++ b/Logic/LayerUpdater.js @@ -0,0 +1,99 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LayerUpdater = void 0; +var Overpass_1 = require("./Overpass"); +var TagsFilter_1 = require("./TagsFilter"); +var UIEventSource_1 = require("../UI/UIEventSource"); +var LayerUpdater = /** @class */ (function () { + /** + * The most important layer should go first, as that one gets first pick for the questions + * @param map + * @param minzoom + * @param layers + */ + function LayerUpdater(map, minzoom, layers) { + this.runningQuery = new UIEventSource_1.UIEventSource(false); + this._map = map; + this._layers = layers; + this._minzoom = minzoom; + var filters = []; + for (var _i = 0, layers_1 = layers; _i < layers_1.length; _i++) { + var layer = layers_1[_i]; + filters.push(layer.filters); + } + this._overpass = new Overpass_1.Overpass(new TagsFilter_1.Or(filters)); + var self = this; + map.Location.addCallback(function () { + self.update(); + }); + } + LayerUpdater.prototype.handleData = function (geojson) { + this.runningQuery.setData(false); + for (var _i = 0, _a = this._layers; _i < _a.length; _i++) { + var layer = _a[_i]; + geojson = layer.SetApplicableData(geojson); + } + if (geojson.features.length > 0) { + console.log("Got some leftovers: ", geojson); + } + }; + LayerUpdater.prototype.handleFail = function (reason) { + console.log("QUERY FAILED", reason); + console.log("Retrying in 1s"); + this.previousBounds = undefined; + var self = this; + window.setTimeout(function () { self.update(); }, 1000); + }; + LayerUpdater.prototype.update = function () { + if (this.IsInBounds()) { + return; + } + console.log("Zoom level: ", this._map.map.getZoom(), "Least needed zoom:", this._minzoom); + if (this._map.map.getZoom() < this._minzoom || this._map.Location.data.zoom < this._minzoom) { + console.log("Not running query: zoom not sufficient"); + return; + } + if (this.runningQuery.data) { + console.log("Still running a query, skip"); + } + var bbox = this.buildBboxFor(); + this.runningQuery.setData(true); + var self = this; + this._overpass.queryGeoJson(bbox, function (data) { + self.handleData(data); + }, function (reason) { + self.handleFail(reason); + }); + }; + LayerUpdater.prototype.buildBboxFor = function () { + var b = this._map.map.getBounds(); + var diff = 0.07; + var n = b.getNorth() + diff; + var e = b.getEast() + diff; + var s = b.getSouth() - diff; + var w = b.getWest() - diff; + this.previousBounds = { north: n, east: e, south: s, west: w }; + return "[bbox:" + s + "," + w + "," + n + "," + e + "]"; + }; + LayerUpdater.prototype.IsInBounds = function () { + if (this.previousBounds === undefined) { + return false; + } + var b = this._map.map.getBounds(); + if (b.getSouth() < this.previousBounds.south) { + return false; + } + if (b.getNorth() > this.previousBounds.north) { + return false; + } + if (b.getEast() > this.previousBounds.east) { + return false; + } + if (b.getWest() < this.previousBounds.west) { + return false; + } + return true; + }; + return LayerUpdater; +}()); +exports.LayerUpdater = LayerUpdater; diff --git a/Logic/OsmConnection.js b/Logic/OsmConnection.js new file mode 100644 index 0000000..f56f9cf --- /dev/null +++ b/Logic/OsmConnection.js @@ -0,0 +1,256 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OsmConnection = exports.UserDetails = void 0; +// @ts-ignore +var osm_auth_1 = require("osm-auth"); +var UIEventSource_1 = require("../UI/UIEventSource"); +var UserDetails = /** @class */ (function () { + function UserDetails() { + this.loggedIn = false; + this.name = "Not logged in"; + this.csCount = 0; + this.unreadMessages = 0; + this.totalMessages = 0; + } + return UserDetails; +}()); +exports.UserDetails = UserDetails; +var OsmConnection = /** @class */ (function () { + function OsmConnection(dryRun) { + this.auth = new osm_auth_1.default({ + oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', + oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', + auto: true // show a login form if the user is not authenticated and + // you try to do a call + }); + this.preferences = new UIEventSource_1.UIEventSource({}); + this.preferenceSources = {}; + this.userDetails = new UIEventSource_1.UIEventSource(new UserDetails()); + this.userDetails.data.osmConnection = this; + this.userDetails.data.dryRun = dryRun; + this._dryRun = dryRun; + if (this.auth.authenticated()) { + this.AttemptLogin(); // Also updates the user badge + } + else { + console.log("Not authenticated"); + } + if (dryRun) { + console.log("DRYRUN ENABLED"); + } + } + OsmConnection.prototype.LogOut = function () { + this.auth.logout(); + this.userDetails.data.loggedIn = false; + this.userDetails.ping(); + console.log("Logged out"); + }; + OsmConnection.prototype.AttemptLogin = function () { + var self = this; + this.auth.xhr({ + method: 'GET', + path: '/api/0.6/user/details' + }, function (err, details) { + var _a; + if (err != null) { + console.log(err); + self.auth.logout(); + self.userDetails.data.loggedIn = false; + self.userDetails.ping(); + } + if (details == null) { + return; + } + self.UpdatePreferences(); + // details is an XML DOM of user details + var userInfo = details.getElementsByTagName("user")[0]; + // let moreDetails = new DOMParser().parseFromString(userInfo.innerHTML, "text/xml"); + var data = self.userDetails.data; + data.loggedIn = true; + console.log("Login completed, userinfo is ", userInfo); + data.name = userInfo.getAttribute('display_name'); + data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count"); + data.img = undefined; + var imgEl = userInfo.getElementsByTagName("img"); + if (imgEl !== undefined && imgEl[0] !== undefined) { + data.img = imgEl[0].getAttribute("href"); + } + data.img = (_a = data.img) !== null && _a !== void 0 ? _a : "./assets/osm-logo.svg"; + var homeEl = userInfo.getElementsByTagName("home"); + if (homeEl !== undefined && homeEl[0] !== undefined) { + var lat = parseFloat(homeEl[0].getAttribute("lat")); + var lon = parseFloat(homeEl[0].getAttribute("lon")); + data.home = { lat: lat, lon: lon }; + } + var messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0]; + data.unreadMessages = parseInt(messages.getAttribute("unread")); + data.totalMessages = parseInt(messages.getAttribute("count")); + self.userDetails.ping(); + }); + }; + /** + * All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate + */ + OsmConnection.prototype.registerActivateOsmAUthenticationClass = function () { + var self = this; + var authElements = document.getElementsByClassName("activate-osm-authentication"); + for (var i = 0; i < authElements.length; i++) { + var element = authElements.item(i); + // @ts-ignore + element.onclick = function () { + self.AttemptLogin(); + }; + } + }; + OsmConnection.prototype.GetPreference = function (key) { + var _this = this; + if (this.preferenceSources[key] !== undefined) { + return this.preferenceSources[key]; + } + if (this.userDetails.data.loggedIn) { + this.UpdatePreferences(); + } + var pref = new UIEventSource_1.UIEventSource(this.preferences.data[key]); + pref.addCallback(function (v) { + _this.SetPreference(key, v); + }); + this.preferences.addCallback(function (prefs) { + if (prefs[key] !== undefined) { + pref.setData(prefs[key]); + } + }); + this.preferenceSources[key] = pref; + return pref; + }; + OsmConnection.prototype.UpdatePreferences = function () { + var self = this; + this.auth.xhr({ + method: 'GET', + path: '/api/0.6/user/preferences' + }, function (error, value) { + if (error) { + console.log("Could not load preferences", error); + return; + } + var prefs = value.getElementsByTagName("preference"); + for (var i = 0; i < prefs.length; i++) { + var pref = prefs[i]; + var k = pref.getAttribute("k"); + var v = pref.getAttribute("v"); + self.preferences.data[k] = v; + } + self.preferences.ping(); + }); + }; + OsmConnection.prototype.SetPreference = function (k, v) { + if (!this.userDetails.data.loggedIn) { + console.log("Not saving preference: user not logged in"); + return; + } + if (this.preferences.data[k] === v) { + console.log("Not updating preference", k, " to ", v, "not changed"); + return; + } + console.log("Updating preference", k, " to ", v); + this.preferences.data[k] = v; + this.preferences.ping(); + this.auth.xhr({ + method: 'PUT', + path: '/api/0.6/user/preferences/' + k, + options: { header: { 'Content-Type': 'text/plain' } }, + content: v + }, function (error, result) { + if (error) { + console.log("Could not set preference", error); + return; + } + console.log("Preference written!", result == "" ? "OK" : result); + }); + }; + OsmConnection.parseUploadChangesetResponse = function (response) { + var nodes = response.getElementsByTagName("node"); + var mapping = {}; + // @ts-ignore + for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) { + var node = nodes_1[_i]; + var oldId = parseInt(node.attributes.old_id.value); + var newId = parseInt(node.attributes.new_id.value); + if (oldId !== undefined && newId !== undefined && + !isNaN(oldId) && !isNaN(newId)) { + mapping["node/" + oldId] = "node/" + newId; + } + } + return mapping; + }; + OsmConnection.prototype.UploadChangeset = function (comment, generateChangeXML, handleMapping, continuation) { + if (this._dryRun) { + console.log("NOT UPLOADING as dryrun is true"); + var changesetXML = generateChangeXML("123456"); + console.log(changesetXML); + continuation(); + return; + } + var self = this; + this.OpenChangeset(comment, function (csId) { + var changesetXML = generateChangeXML(csId); + self.AddChange(csId, changesetXML, function (csId, mapping) { + self.CloseChangeset(csId, continuation); + handleMapping(mapping); + }); + }); + this.userDetails.data.csCount++; + this.userDetails.ping(); + }; + OsmConnection.prototype.OpenChangeset = function (comment, continuation) { + this.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/create', + options: { header: { 'Content-Type': 'text/xml' } }, + content: '<osm><changeset>' + + '<tag k="created_by" v="MapComplete 0.0.0" />' + + '<tag k="comment" v="' + comment + '"/>' + + '</changeset></osm>' + }, function (err, response) { + if (response === undefined) { + console.log("err", err); + return; + } + else { + continuation(response); + } + }); + }; + OsmConnection.prototype.AddChange = function (changesetId, changesetXML, continuation) { + this.auth.xhr({ + method: 'POST', + options: { header: { 'Content-Type': 'text/xml' } }, + path: '/api/0.6/changeset/' + changesetId + '/upload', + content: changesetXML + }, function (err, response) { + if (response == null) { + console.log("err", err); + return; + } + var mapping = OsmConnection.parseUploadChangesetResponse(response); + console.log("Uplaoded changeset ", changesetId); + continuation(changesetId, mapping); + }); + }; + OsmConnection.prototype.CloseChangeset = function (changesetId, continuation) { + console.log("closing"); + this.auth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/' + changesetId + '/close', + }, function (err, response) { + if (response == null) { + console.log("err", err); + } + console.log("Closed changeset ", changesetId); + if (continuation !== undefined) { + continuation(); + } + }); + }; + return OsmConnection; +}()); +exports.OsmConnection = OsmConnection; diff --git a/Logic/OsmImageUploadHandler.js b/Logic/OsmImageUploadHandler.js new file mode 100644 index 0000000..0af13f5 --- /dev/null +++ b/Logic/OsmImageUploadHandler.js @@ -0,0 +1,56 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OsmImageUploadHandler = void 0; +var ImageUploadFlow_1 = require("../UI/ImageUploadFlow"); +var OsmImageUploadHandler = /** @class */ (function () { + function OsmImageUploadHandler(tags, userdetails, preferedLicense, changeHandler, slideShow) { + this._slideShow = slideShow; // To move the slideshow (if any) to the last, just added element + if (tags === undefined || userdetails === undefined || changeHandler === undefined) { + throw "Something is undefined"; + } + this._tags = tags; + this._changeHandler = changeHandler; + this._userdetails = userdetails; + this._preferedLicense = preferedLicense; + } + OsmImageUploadHandler.prototype.generateOptions = function (license) { + var _a; + var tags = this._tags.data; + var self = this; + var title = (_a = tags.name) !== null && _a !== void 0 ? _a : "Unknown area"; + var description = [ + "author:" + this._userdetails.data.name, + "license:" + license, + "wikidata:" + tags.wikidata, + "osmid:" + tags.id, + "name:" + tags.name + ].join("\n"); + var changes = this._changeHandler; + return { + title: title, + description: description, + handleURL: function (url) { + var freeIndex = 0; + while (tags["image:" + freeIndex] !== undefined) { + freeIndex++; + } + console.log("Adding image:" + freeIndex, url); + changes.addChange(tags.id, "image:" + freeIndex, url); + self._slideShow.MoveTo(-1); // set the last (thus newly added) image) to view + }, + allDone: function () { + changes.uploadAll(function () { + console.log("Writing changes..."); + }); + } + }; + }; + OsmImageUploadHandler.prototype.getUI = function () { + var self = this; + return new ImageUploadFlow_1.ImageUploadFlow(this._userdetails, this._preferedLicense, function (license) { + return self.generateOptions(license); + }); + }; + return OsmImageUploadHandler; +}()); +exports.OsmImageUploadHandler = OsmImageUploadHandler; diff --git a/Logic/OsmObject.js b/Logic/OsmObject.js new file mode 100644 index 0000000..e9d4948 --- /dev/null +++ b/Logic/OsmObject.js @@ -0,0 +1,160 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.OsmRelation = exports.OsmWay = exports.OsmNode = exports.OsmObject = void 0; +var $ = require("jquery"); +var OsmObject = /** @class */ (function () { + function OsmObject(type, id) { + this.tags = {}; + this.changed = false; + this.id = id; + this.type = type; + } + OsmObject.DownloadObject = function (id, continuation) { + var splitted = id.split("/"); + var type = splitted[0]; + var idN = splitted[1]; + switch (type) { + case ("node"): + return new OsmNode(idN).Download(continuation); + case ("way"): + return new OsmWay(idN).Download(continuation); + case ("relation"): + return new OsmRelation(idN).Download(continuation); + } + }; + /** + * Replaces all '"' (double quotes) by '"' + * Bugfix where names containing '"' were not uploaded, such as '"Het Zwin" nature reserve' + * @param string + * @constructor + */ + OsmObject.prototype.Escape = function (string) { + while (string.indexOf('"') >= 0) { + string = string.replace('"', '"'); + } + return string; + }; + /** + * Generates the changeset-XML for tags + * @constructor + */ + OsmObject.prototype.TagsXML = function () { + var tags = ""; + for (var key in this.tags) { + var v = this.tags[key]; + if (v !== "") { + tags += ' <tag k="' + this.Escape(key) + '" v="' + this.Escape(this.tags[key]) + '"/>\n'; + } + } + return tags; + }; + OsmObject.prototype.Download = function (continuation) { + var self = this; + $.getJSON("https://www.openstreetmap.org/api/0.6/" + this.type + "/" + this.id, function (data) { + var element = data.elements[0]; + self.tags = element.tags; + self.version = element.version; + self.SaveExtraData(element); + continuation(self); + }); + return this; + }; + OsmObject.prototype.addTag = function (k, v) { + if (k in this.tags) { + var oldV = this.tags[k]; + if (oldV == v) { + return; + } + console.log("WARNING: overwriting ", oldV, " with ", v, " for key ", k); + } + this.tags[k] = v; + this.changed = true; + }; + OsmObject.prototype.VersionXML = function () { + if (this.version === undefined) { + return ""; + } + return 'version="' + this.version + '"'; + }; + return OsmObject; +}()); +exports.OsmObject = OsmObject; +var OsmNode = /** @class */ (function (_super) { + __extends(OsmNode, _super); + function OsmNode(id) { + return _super.call(this, "node", id) || this; + } + OsmNode.prototype.ChangesetXML = function (changesetId) { + var tags = this.TagsXML(); + var change = ' <node id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + ' lat="' + this.lat + '" lon="' + this.lon + '">\n' + + tags + + ' </node>\n'; + return change; + }; + OsmNode.prototype.SaveExtraData = function (element) { + this.lat = element.lat; + this.lon = element.lon; + }; + return OsmNode; +}(OsmObject)); +exports.OsmNode = OsmNode; +var OsmWay = /** @class */ (function (_super) { + __extends(OsmWay, _super); + function OsmWay(id) { + return _super.call(this, "way", id) || this; + } + OsmWay.prototype.ChangesetXML = function (changesetId) { + var tags = this.TagsXML(); + var nds = ""; + for (var node in this.nodes) { + nds += ' <nd ref="' + this.nodes[node] + '"/>\n'; + } + var change = ' <way id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + '>\n' + + nds + + tags + + ' </way>\n'; + return change; + }; + OsmWay.prototype.SaveExtraData = function (element) { + this.nodes = element.nodes; + }; + return OsmWay; +}(OsmObject)); +exports.OsmWay = OsmWay; +var OsmRelation = /** @class */ (function (_super) { + __extends(OsmRelation, _super); + function OsmRelation(id) { + return _super.call(this, "relation", id) || this; + } + OsmRelation.prototype.ChangesetXML = function (changesetId) { + var members = ""; + for (var memberI in this.members) { + var member = this.members[memberI]; + members += ' <member type="' + member.type + '" ref="' + member.ref + '" role="' + member.role + '"/>\n'; + } + var tags = this.TagsXML(); + var change = ' <relation id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + '>\n' + + members + + tags + + ' </relation>\n'; + return change; + }; + OsmRelation.prototype.SaveExtraData = function (element) { + this.members = element.members; + }; + return OsmRelation; +}(OsmObject)); +exports.OsmRelation = OsmRelation; diff --git a/Logic/Overpass.js b/Logic/Overpass.js new file mode 100644 index 0000000..0a0881c --- /dev/null +++ b/Logic/Overpass.js @@ -0,0 +1,47 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Overpass = void 0; +var OsmToGeoJson = require("osmtogeojson"); +var $ = require("jquery"); +/** + * Interfaces overpass to get all the latest data + */ +var Overpass = /** @class */ (function () { + function Overpass(filter) { + this._filter = filter; + } + Overpass.prototype.buildQuery = function (bbox) { + var filters = this._filter.asOverpass(); + var filter = ""; + for (var _i = 0, filters_1 = filters; _i < filters_1.length; _i++) { + var filterOr = filters_1[_i]; + filter += 'nwr' + filterOr + ';'; + } + var query = '[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;'; + console.log(query); + return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query); + }; + Overpass.prototype.queryGeoJson = function (bbox, continuation, onFail) { + var query = this.buildQuery(bbox); + if (Overpass.testUrl !== null) { + console.log("Using testing URL"); + query = Overpass.testUrl; + } + $.getJSON(query, function (json, status) { + if (status !== "success") { + console.log("Query failed"); + onFail(status); + } + if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { + console.log("Timeout or other runtime error"); + return; + } + // @ts-ignore + var geojson = OsmToGeoJson.default(json); + continuation(geojson); + }).fail(onFail); + }; + Overpass.testUrl = null; + return Overpass; +}()); +exports.Overpass = Overpass; diff --git a/Logic/StrayClickHandler.js b/Logic/StrayClickHandler.js new file mode 100644 index 0000000..d93fb09 --- /dev/null +++ b/Logic/StrayClickHandler.js @@ -0,0 +1,42 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.StrayClickHandler = void 0; +var leaflet_1 = require("leaflet"); +/** + * The stray-click-hanlders adds a marker to the map if no feature was clicked. + * Shows the given uiToShow-element in the messagebox + */ +var StrayClickHandler = /** @class */ (function () { + function StrayClickHandler(basemap, selectElement, leftMessage, uiToShow) { + var _this = this; + this._basemap = basemap; + this._leftMessage = leftMessage; + this._uiToShow = uiToShow; + var self = this; + var map = basemap.map; + basemap.LastClickLocation.addCallback(function (lastClick) { + selectElement.setData(undefined); + if (self._lastMarker !== undefined) { + map.removeLayer(self._lastMarker); + } + self._lastMarker = leaflet_1.default.marker([lastClick.lat, lastClick.lon]); + var uiElement = uiToShow(); + var popup = leaflet_1.default.popup().setContent(uiElement.Render()); + uiElement.Activate(); + uiElement.Update(); + self._lastMarker.addTo(map); + self._lastMarker.bindPopup(popup).openPopup(); + self._lastMarker.on("click", function () { + leftMessage.setData(self._uiToShow); + }); + }); + selectElement.addCallback(function () { + if (self._lastMarker !== undefined) { + map.removeLayer(self._lastMarker); + _this._lastMarker = undefined; + } + }); + } + return StrayClickHandler; +}()); +exports.StrayClickHandler = StrayClickHandler; diff --git a/Logic/TagsFilter.js b/Logic/TagsFilter.js new file mode 100644 index 0000000..5c6aac1 --- /dev/null +++ b/Logic/TagsFilter.js @@ -0,0 +1,240 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TagUtils = exports.Not = exports.And = exports.Or = exports.Tag = exports.Regex = exports.TagsFilter = void 0; +var TagsFilter = /** @class */ (function () { + function TagsFilter() { + } + TagsFilter.prototype.matchesProperties = function (properties) { + return this.matches(TagUtils.proprtiesToKV(properties)); + }; + return TagsFilter; +}()); +exports.TagsFilter = TagsFilter; +var Regex = /** @class */ (function (_super) { + __extends(Regex, _super); + function Regex(k, r) { + var _this = _super.call(this) || this; + _this._k = k; + _this._r = r; + return _this; + } + Regex.prototype.asOverpass = function () { + return ["['" + this._k + "'~'" + this._r + "']"]; + }; + Regex.prototype.matches = function (tags) { + var _a; + if (!(tags instanceof Array)) { + throw "You used 'matches' on something that is not a list. Did you mean to use 'matchesProperties'?"; + } + for (var _i = 0, tags_1 = tags; _i < tags_1.length; _i++) { + var tag = tags_1[_i]; + if (tag.k === this._k) { + if (tag.v === "") { + // This tag has been removed + return false; + } + if (this._r === "*") { + // Any is allowed + return true; + } + var matchCount = (_a = tag.v.match(this._r)) === null || _a === void 0 ? void 0 : _a.length; + return (matchCount !== null && matchCount !== void 0 ? matchCount : 0) > 0; + } + } + return false; + }; + Regex.prototype.substituteValues = function (tags) { + throw "Substituting values is not supported on regex tags"; + }; + return Regex; +}(TagsFilter)); +exports.Regex = Regex; +var Tag = /** @class */ (function (_super) { + __extends(Tag, _super); + function Tag(key, value) { + var _this = _super.call(this) || this; + _this.key = key; + _this.value = value; + return _this; + } + Tag.prototype.matches = function (tags) { + for (var _i = 0, tags_2 = tags; _i < tags_2.length; _i++) { + var tag = tags_2[_i]; + if (tag.k === this.key) { + if (tag.v === "") { + // This tag has been removed + return this.value === ""; + } + if (this.value === "*") { + // Any is allowed + return true; + } + return this.value === tag.v; + } + } + if (this.value === "") { + return true; + } + return false; + }; + Tag.prototype.asOverpass = function () { + if (this.value === "*") { + return ['["' + this.key + '"]']; + } + if (this.value === "") { + // NOT having this key + return ['[!"' + this.key + '"]']; + } + return ['["' + this.key + '"="' + this.value + '"]']; + }; + Tag.prototype.substituteValues = function (tags) { + return new Tag(this.key, TagUtils.ApplyTemplate(this.value, tags)); + }; + return Tag; +}(TagsFilter)); +exports.Tag = Tag; +var Or = /** @class */ (function (_super) { + __extends(Or, _super); + function Or(or) { + var _this = _super.call(this) || this; + _this.or = or; + return _this; + } + Or.prototype.matches = function (tags) { + for (var _i = 0, _a = this.or; _i < _a.length; _i++) { + var tagsFilter = _a[_i]; + if (tagsFilter.matches(tags)) { + return true; + } + } + return false; + }; + Or.prototype.asOverpass = function () { + var choices = []; + for (var _i = 0, _a = this.or; _i < _a.length; _i++) { + var tagsFilter = _a[_i]; + var subChoices = tagsFilter.asOverpass(); + for (var _b = 0, subChoices_1 = subChoices; _b < subChoices_1.length; _b++) { + var subChoice = subChoices_1[_b]; + choices.push(subChoice); + } + } + return choices; + }; + Or.prototype.substituteValues = function (tags) { + var newChoices = []; + for (var _i = 0, _a = this.or; _i < _a.length; _i++) { + var c = _a[_i]; + newChoices.push(c.substituteValues(tags)); + } + return new Or(newChoices); + }; + return Or; +}(TagsFilter)); +exports.Or = Or; +var And = /** @class */ (function (_super) { + __extends(And, _super); + function And(and) { + var _this = _super.call(this) || this; + _this.and = and; + return _this; + } + And.prototype.matches = function (tags) { + for (var _i = 0, _a = this.and; _i < _a.length; _i++) { + var tagsFilter = _a[_i]; + if (!tagsFilter.matches(tags)) { + return false; + } + } + return true; + }; + And.prototype.combine = function (filter, choices) { + var values = []; + for (var _i = 0, choices_1 = choices; _i < choices_1.length; _i++) { + var or = choices_1[_i]; + values.push(filter + or); + } + return values; + }; + And.prototype.asOverpass = function () { + var allChoices = null; + for (var _i = 0, _a = this.and; _i < _a.length; _i++) { + var andElement = _a[_i]; + var andElementFilter = andElement.asOverpass(); + if (allChoices === null) { + allChoices = andElementFilter; + continue; + } + var newChoices = []; + for (var _b = 0, allChoices_1 = allChoices; _b < allChoices_1.length; _b++) { + var choice = allChoices_1[_b]; + newChoices.push(this.combine(choice, andElementFilter)); + } + allChoices = newChoices; + } + return allChoices; + }; + And.prototype.substituteValues = function (tags) { + var newChoices = []; + for (var _i = 0, _a = this.and; _i < _a.length; _i++) { + var c = _a[_i]; + newChoices.push(c.substituteValues(tags)); + } + return new And(newChoices); + }; + return And; +}(TagsFilter)); +exports.And = And; +var Not = /** @class */ (function (_super) { + __extends(Not, _super); + function Not(not) { + var _this = _super.call(this) || this; + _this.not = not; + return _this; + } + Not.prototype.asOverpass = function () { + throw "Not supported yet"; + }; + Not.prototype.matches = function (tags) { + return !this.not.matches(tags); + }; + Not.prototype.substituteValues = function (tags) { + return new Not(this.not.substituteValues(tags)); + }; + return Not; +}(TagsFilter)); +exports.Not = Not; +var TagUtils = /** @class */ (function () { + function TagUtils() { + } + TagUtils.proprtiesToKV = function (properties) { + var result = []; + for (var k in properties) { + result.push({ k: k, v: properties[k] }); + } + return result; + }; + TagUtils.ApplyTemplate = function (template, tags) { + for (var k in tags) { + while (template.indexOf("{" + k + "}") >= 0) { + template = template.replace("{" + k + "}", tags[k]); + } + } + return template; + }; + return TagUtils; +}()); +exports.TagUtils = TagUtils; diff --git a/Logic/Wikimedia.js b/Logic/Wikimedia.js new file mode 100644 index 0000000..b9f880f --- /dev/null +++ b/Logic/Wikimedia.js @@ -0,0 +1,135 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LicenseInfo = exports.ImagesInCategory = exports.Wikidata = exports.Wikimedia = void 0; +var $ = require("jquery"); +/** + * This module provides endpoints for wikipedia/wikimedia and others + */ +var Wikimedia = /** @class */ (function () { + function Wikimedia() { + } + Wikimedia.ImageNameToUrl = function (filename, width, height) { + if (width === void 0) { width = 500; } + if (height === void 0) { height = 200; } + filename = encodeURIComponent(filename); + return "https://commons.wikimedia.org/wiki/Special:FilePath/" + filename + "?width=" + width + "&height=" + height; + }; + Wikimedia.LicenseData = function (filename, handle) { + if (filename in this.knownLicenses) { + return this.knownLicenses[filename]; + } + if (filename === "") { + return; + } + var url = "https://en.wikipedia.org/w/" + + "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + + "titles=" + filename + + "&format=json&origin=*"; + $.getJSON(url, function (data, status) { + var _a, _b, _c, _d, _e, _f, _g, _h; + var licenseInfo = new LicenseInfo(); + var license = data.query.pages[-1].imageinfo[0].extmetadata; + licenseInfo.artist = (_a = license.Artist) === null || _a === void 0 ? void 0 : _a.value; + licenseInfo.license = (_b = license.License) === null || _b === void 0 ? void 0 : _b.value; + licenseInfo.copyrighted = (_c = license.Copyrighted) === null || _c === void 0 ? void 0 : _c.value; + licenseInfo.attributionRequired = (_d = license.AttributionRequired) === null || _d === void 0 ? void 0 : _d.value; + licenseInfo.usageTerms = (_e = license.UsageTerms) === null || _e === void 0 ? void 0 : _e.value; + licenseInfo.licenseShortName = (_f = license.LicenseShortName) === null || _f === void 0 ? void 0 : _f.value; + licenseInfo.credit = (_g = license.Credit) === null || _g === void 0 ? void 0 : _g.value; + licenseInfo.description = (_h = license.ImageDescription) === null || _h === void 0 ? void 0 : _h.value; + Wikimedia.knownLicenses[filename] = licenseInfo; + handle(licenseInfo); + }); + }; + Wikimedia.GetCategoryFiles = function (categoryName, handleCategory, alreadyLoaded, continueParameter) { + var _this = this; + if (alreadyLoaded === void 0) { alreadyLoaded = 0; } + if (continueParameter === void 0) { continueParameter = undefined; } + if (categoryName === undefined || categoryName === null || categoryName === "") { + return; + } + // @ts-ignore + if (!categoryName.startsWith("Category:")) { + categoryName = "Category:" + categoryName; + } + var url = "https://commons.wikimedia.org/w/api.php?" + + "action=query&list=categorymembers&format=json&" + + "&origin=*" + + "&cmtitle=" + encodeURIComponent(categoryName); + if (continueParameter !== undefined) { + url = url + "&" + continueParameter.k + "=" + continueParameter.param; + } + $.getJSON(url, function (response) { + var _a; + var imageOverview = new ImagesInCategory(); + var members = (_a = response.query) === null || _a === void 0 ? void 0 : _a.categorymembers; + if (members === undefined) { + members = []; + } + for (var _i = 0, members_1 = members; _i < members_1.length; _i++) { + var member = members_1[_i]; + imageOverview.images.push(member.title); + } + if (response.continue === undefined || alreadyLoaded > 30) { + handleCategory(imageOverview); + } + else { + console.log("Recursive load for ", categoryName); + _this.GetCategoryFiles(categoryName, function (recursiveImages) { + for (var _i = 0, _a = imageOverview.images; _i < _a.length; _i++) { + var image = _a[_i]; + recursiveImages.images.push(image); + } + handleCategory(recursiveImages); + }, alreadyLoaded + 10, { k: "cmcontinue", param: response.continue.cmcontinue }); + } + }); + }; + Wikimedia.GetWikiData = function (id, handleWikidata) { + var url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; + $.getJSON(url, function (response) { + var _a, _b, _c, _d; + var entity = response.entities["Q" + id]; + var commons = entity.sitelinks.commonswiki; + var wd = new Wikidata(); + wd.commonsWiki = commons === null || commons === void 0 ? void 0 : commons.title; + // P18 is the claim 'depicted in this image' + var image = (_d = (_c = (_b = (_a = entity.claims.P18) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.mainsnak) === null || _c === void 0 ? void 0 : _c.datavalue) === null || _d === void 0 ? void 0 : _d.value; + if (image) { + wd.image = "File:" + image; + } + handleWikidata(wd); + }); + }; + Wikimedia.knownLicenses = {}; + return Wikimedia; +}()); +exports.Wikimedia = Wikimedia; +var Wikidata = /** @class */ (function () { + function Wikidata() { + } + return Wikidata; +}()); +exports.Wikidata = Wikidata; +var ImagesInCategory = /** @class */ (function () { + function ImagesInCategory() { + // Filenames of relevant images + this.images = []; + } + return ImagesInCategory; +}()); +exports.ImagesInCategory = ImagesInCategory; +var LicenseInfo = /** @class */ (function () { + function LicenseInfo() { + this.artist = ""; + this.license = ""; + this.licenseShortName = ""; + this.usageTerms = ""; + this.attributionRequired = false; + this.copyrighted = false; + this.credit = ""; + this.description = ""; + } + return LicenseInfo; +}()); +exports.LicenseInfo = LicenseInfo; diff --git a/Quests.js b/Quests.js new file mode 100644 index 0000000..4c68f4b --- /dev/null +++ b/Quests.js @@ -0,0 +1,40 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Quests = void 0; +var Question_1 = require("./Logic/Question"); +var Quests = /** @class */ (function () { + function Quests() { + } + Quests.nameOf = function (name) { + return Question_1.QuestionDefinition.noNameOrNameQuestion("<b>Wat is de <i>officiële</i> naam van dit " + name + "?</b><br />" + + "Veel gebieden hebben geen naam. Duid dit dan ook zo aan.", "Dit " + name + " heeft geen naam", 20); + }; + Quests.hasFee = Question_1.QuestionDefinition.radioQuestionSimple("Moet men betalen om deze toiletten te gebruiken?", 10, "fee", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]); + Quests.toiletsWheelChairs = Question_1.QuestionDefinition.radioQuestionSimple("Zijn deze toiletten rolstoeltoegankelijk?", 20, "wheelchair", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]).addUnrequiredTag("toilets:position", "urinals"); + Quests.toiletsChangingTable = Question_1.QuestionDefinition.radioQuestionSimple("Is er een luiertafel beschikbaar?", 20, "changing_table", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]) + // Urinals are often a pitlatrine/something very poor where no changing table is + .addUnrequiredTag("toilets:position", "urinals").addUnrequiredTag("toilets:position", "urinal"); + Quests.toiletsChangingTableLocation = Question_1.QuestionDefinition.radioAndTextQuestion("Waar bevindt de luiertafel zich?", 5, "changing_table", [{ text: "In de vrouwentoiletten", value: "female_toilet" }, + { text: "In de mannentoiletten", value: "male_toilet" }, + { text: "In de rolstoeltoegangkelijke toiletten", value: "wheelchair_toilet" }, + { text: "In de aparte, speciaal voorziene ruimte", value: "dedicated_room" }, + { text: "In de genderneutrale toiletten", value: "unisex_toilet" }]) + .addRequiredTag("changing_table", "yes"); + Quests.toiletsPosition = Question_1.QuestionDefinition.radioQuestionSimple("Wat voor toiletten zijn dit?", 1, "toilets:position", [{ text: "Enkel urinoirs", value: "urinals" }, + { text: "Enkel 'gewone' toiletten waar men op gaat zitten", value: "seated" }, + { text: "Er zijn zowel urinoirs als zittoiletten", value: "seated;urinals" }]); + Quests.accessNatureReserve = Question_1.QuestionDefinition.radioQuestionSimple("Is dit gebied toegankelijk voor het publiek?", 10, "access", [ + { text: "Nee, dit is afgesloten", value: "no" }, + { text: "Nee, dit is een privaat terrein", value: "no" }, + { text: "Hoewel het een privebos is, kan men er toch in", value: "permissive" }, + { text: "Enkel tijdens activiteiten of met een gids", value: "guided" }, + { text: "Ja, het is gewoon toegankelijk", value: "yes" } + ]).addUnrequiredTag("seamark:type", "restricted_area"); + Quests.operator = Question_1.QuestionDefinition.radioAndTextQuestion("Wie is de beheerder van dit gebied?", 1, "operator", [{ text: "Natuurpunt", value: "Natuurpunt" }, + { text: "Het Agenschap voor Natuur en Bos", value: "Agentschap Natuur en Bos" }, + { text: "Een prive-eigenaar", value: "private" } + ]).addUnrequiredTag("access", "private") + .addUnrequiredTag("access", "no"); + return Quests; +}()); +exports.Quests = Quests; diff --git a/UI/AddButton.js b/UI/AddButton.js new file mode 100644 index 0000000..68ce701 --- /dev/null +++ b/UI/AddButton.js @@ -0,0 +1,129 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AddButton = void 0; +var UIEventSource_1 = require("./UIEventSource"); +var UIElement_1 = require("./UIElement"); +var AddButton = /** @class */ (function (_super) { + __extends(AddButton, _super); + function AddButton(basemap, changes, options) { + var _this = _super.call(this, undefined) || this; + _this.curentAddSelection = new UIEventSource_1.UIEventSource(""); + _this.SELECTING_POI = "selecting_POI"; + _this.PLACING_POI = "placing_POI"; + /*State is one of: + * "": the default stated + * "select_POI": show a 'select which POI to add' query (skipped if only one option exists) + * "placing_point": shown while adding a point + * "" + */ + _this.state = new UIEventSource_1.UIEventSource(""); + _this.zoomlevel = basemap.Location; + _this.ListenTo(_this.zoomlevel); + _this._options = options; + _this.ListenTo(_this.curentAddSelection); + _this.ListenTo(_this.state); + _this.state.setData(_this.SELECTING_POI); + _this.changes = changes; + var self = _this; + basemap.map.on("click", function (e) { + var location = e.latlng; + console.log("Clicked at ", location); + self.HandleClick(location.lat, location.lng); + }); + basemap.map.on("mousemove", function () { + if (self.state.data === self.PLACING_POI) { + var icon = "crosshair"; + for (var _i = 0, _a = self._options; _i < _a.length; _i++) { + var option = _a[_i]; + if (option.name === self.curentAddSelection.data && option.icon !== undefined) { + icon = 'url("' + option.icon + '") 32 32 ,crosshair'; + console.log("Cursor icon: ", icon); + } + } + document.getElementById('leafletDiv').style.cursor = icon; + } + else { + // @ts-ignore + document.getElementById('leafletDiv').style.cursor = ''; + } + }); + return _this; + } + AddButton.prototype.HandleClick = function (lat, lon) { + this.state.setData(this.SELECTING_POI); + console.log("Handling click", lat, lon, this.curentAddSelection.data); + for (var _i = 0, _a = this._options; _i < _a.length; _i++) { + var option = _a[_i]; + if (this.curentAddSelection.data === option.name) { + console.log("PLACING a ", option); + var feature = this.changes.createElement(option.tags, lat, lon); + option.layerToAddTo.AddNewElement(feature); + return; + } + } + }; + AddButton.prototype.InnerRender = function () { + if (this.zoomlevel.data.zoom < 19) { + return "Zoom in om een punt toe te voegen"; + } + if (this.state.data === this.SELECTING_POI) { + var html = "<form>"; + for (var _i = 0, _a = this._options; _i < _a.length; _i++) { + var option = _a[_i]; + // <button type='button'> looks SO retarded + // the default type of button is 'submit', which performs a POST and page reload + html += "<button type='button' class='addPOIoption' value='" + option.name + "'>Voeg een " + option.name + " toe</button><br/>"; + } + html += "</form>"; + return html; + } + if (this.state.data === this.PLACING_POI) { + return "<div id='clickOnMapInstruction'>Klik op de kaart om een nieuw punt toe te voegen<div>" + + "<div id='cancelInstruction'>Klik hier om toevoegen te annuleren</div>"; + } + if (this.curentAddSelection.data === "") { + return "<span onclick>Voeg een punt toe...</span>"; + } + return "Annuleer"; + }; + AddButton.prototype.InnerUpdate = function (htmlElement) { + var self = this; + htmlElement.onclick = function (event) { + // @ts-ignore + if (event.consumed) { + return; + } + if (self.state.data === self.PLACING_POI) { + self.state.setData(self.SELECTING_POI); + } + }; + var buttons = htmlElement.getElementsByClassName('addPOIoption'); + var _loop_1 = function (button) { + button.onclick = function (event) { + self.curentAddSelection.setData(button.value); + self.state.setData(self.PLACING_POI); + event.consumed = true; + }; + }; + // @ts-ignore + for (var _i = 0, buttons_1 = buttons; _i < buttons_1.length; _i++) { + var button = buttons_1[_i]; + _loop_1(button); + } + }; + return AddButton; +}(UIElement_1.UIElement)); +exports.AddButton = AddButton; diff --git a/UI/Base/Button.js b/UI/Base/Button.js new file mode 100644 index 0000000..c9b0b4a --- /dev/null +++ b/UI/Base/Button.js @@ -0,0 +1,49 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Button = void 0; +var UIElement_1 = require("../UIElement"); +var Button = /** @class */ (function (_super) { + __extends(Button, _super); + function Button(text, onclick, clss) { + if (clss === void 0) { clss = ""; } + var _this = _super.call(this, undefined) || this; + _this._text = text; + _this._onclick = onclick; + if (clss !== "") { + _this._clss = "class='" + clss + "'"; + } + else { + _this._clss = ""; + } + return _this; + } + Button.prototype.InnerRender = function () { + return "<form>" + + "<button id='button-" + this.id + "' type='button' " + this._clss + ">" + this._text.Render() + "</button>" + + "</form>"; + }; + Button.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + var self = this; + console.log("Update for ", htmlElement); + document.getElementById("button-" + this.id).onclick = function () { + console.log("Clicked"); + self._onclick(); + }; + }; + return Button; +}(UIElement_1.UIElement)); +exports.Button = Button; diff --git a/UI/Base/CollapseButton.js b/UI/Base/CollapseButton.js new file mode 100644 index 0000000..e82b9fb --- /dev/null +++ b/UI/Base/CollapseButton.js @@ -0,0 +1,57 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CollapseButton = void 0; +var UIElement_1 = require("../UIElement"); +var UIEventSource_1 = require("../UIEventSource"); +var CollapseButton = /** @class */ (function (_super) { + __extends(CollapseButton, _super); + function CollapseButton(idToCollapse) { + var _this = _super.call(this, undefined) || this; + _this.isCollapsed = new UIEventSource_1.UIEventSource(false); + _this.ListenTo(_this.isCollapsed); + _this.isCollapsed.addCallback(function (collapse) { + var el = document.getElementById(idToCollapse); + if (el === undefined || el === null) { + console.log("Element not found"); + return; + } + if (collapse) { + el.style.height = "3.5em"; + el.style.width = "15em"; + } + else { + el.style.height = "auto"; + el.style.width = "auto"; + } + }); + var self = _this; + _this.onClick(function () { + self.isCollapsed.setData(!self.isCollapsed.data); + }); + return _this; + } + CollapseButton.prototype.InnerRender = function () { + var up = './assets/arrow-up.svg'; + var down = './assets/arrow-down.svg'; + var arrow = up; + if (this.isCollapsed.data) { + arrow = down; + } + return "<img class='collapse-button' src='" + arrow + "' alt='collapse'>"; + }; + return CollapseButton; +}(UIElement_1.UIElement)); +exports.CollapseButton = CollapseButton; diff --git a/UI/Base/FixedUiElement.js b/UI/Base/FixedUiElement.js new file mode 100644 index 0000000..4029e7f --- /dev/null +++ b/UI/Base/FixedUiElement.js @@ -0,0 +1,30 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FixedUiElement = void 0; +var UIElement_1 = require("../UIElement"); +var FixedUiElement = /** @class */ (function (_super) { + __extends(FixedUiElement, _super); + function FixedUiElement(html) { + var _this = _super.call(this, undefined) || this; + _this._html = html !== null && html !== void 0 ? html : ""; + return _this; + } + FixedUiElement.prototype.InnerRender = function () { + return this._html; + }; + return FixedUiElement; +}(UIElement_1.UIElement)); +exports.FixedUiElement = FixedUiElement; diff --git a/UI/Base/VariableUIElement.js b/UI/Base/VariableUIElement.js new file mode 100644 index 0000000..8f2dd28 --- /dev/null +++ b/UI/Base/VariableUIElement.js @@ -0,0 +1,38 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VariableUiElement = void 0; +var UIElement_1 = require("../UIElement"); +var VariableUiElement = /** @class */ (function (_super) { + __extends(VariableUiElement, _super); + function VariableUiElement(html, innerUpdate) { + if (innerUpdate === void 0) { innerUpdate = undefined; } + var _this = _super.call(this, html) || this; + _this._html = html; + _this._innerUpdate = innerUpdate; + return _this; + } + VariableUiElement.prototype.InnerRender = function () { + return this._html.data; + }; + VariableUiElement.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + if (this._innerUpdate !== undefined) { + this._innerUpdate(htmlElement); + } + }; + return VariableUiElement; +}(UIElement_1.UIElement)); +exports.VariableUiElement = VariableUiElement; diff --git a/UI/Base/VerticalCombine.js b/UI/Base/VerticalCombine.js new file mode 100644 index 0000000..3207e90 --- /dev/null +++ b/UI/Base/VerticalCombine.js @@ -0,0 +1,45 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.VerticalCombine = void 0; +var UIElement_1 = require("../UIElement"); +var VerticalCombine = /** @class */ (function (_super) { + __extends(VerticalCombine, _super); + function VerticalCombine(elements, className) { + if (className === void 0) { className = undefined; } + var _this = _super.call(this, undefined) || this; + _this._elements = elements; + _this._className = className; + return _this; + } + VerticalCombine.prototype.InnerRender = function () { + var html = ""; + for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { + var element = _a[_i]; + if (!element.IsEmpty()) { + html += "<div>" + element.Render() + "</div>"; + } + } + if (html === "") { + return ""; + } + if (this._className === undefined) { + return html; + } + return "<div class='" + this._className + "'>" + html + "</div>"; + }; + return VerticalCombine; +}(UIElement_1.UIElement)); +exports.VerticalCombine = VerticalCombine; diff --git a/UI/CenterMessageBox.js b/UI/CenterMessageBox.js new file mode 100644 index 0000000..4def75c --- /dev/null +++ b/UI/CenterMessageBox.js @@ -0,0 +1,72 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CenterMessageBox = void 0; +var UIElement_1 = require("./UIElement"); +var UIEventSource_1 = require("./UIEventSource"); +var CenterMessageBox = /** @class */ (function (_super) { + __extends(CenterMessageBox, _super); + function CenterMessageBox(startZoom, centermessage, osmConnection, location, queryRunning) { + var _this = _super.call(this, centermessage) || this; + _this._zoomInMore = new UIEventSource_1.UIEventSource(true); + _this._centermessage = centermessage; + _this._location = location; + _this._osmConnection = osmConnection; + _this._queryRunning = queryRunning; + _this.ListenTo(queryRunning); + var self = _this; + location.addCallback(function () { + self._zoomInMore.setData(location.data.zoom < startZoom); + }); + _this.ListenTo(_this._zoomInMore); + return _this; + } + CenterMessageBox.prototype.InnerRender = function () { + if (this._centermessage.data != "") { + return this._centermessage.data; + } + if (this._queryRunning.data) { + return "Data wordt geladen..."; + } + else if (this._zoomInMore.data) { + return "Zoom in om de data te zien en te bewerken"; + } + return "Klaar!"; + }; + CenterMessageBox.prototype.ShouldShowSomething = function () { + if (this._queryRunning.data) { + return true; + } + return this._zoomInMore.data; + }; + CenterMessageBox.prototype.InnerUpdate = function (htmlElement) { + var pstyle = htmlElement.parentElement.style; + if (this._centermessage.data != "") { + pstyle.opacity = "1"; + pstyle.pointerEvents = "all"; + this._osmConnection.registerActivateOsmAUthenticationClass(); + return; + } + pstyle.pointerEvents = "none"; + if (this.ShouldShowSomething()) { + pstyle.opacity = "0.5"; + } + else { + pstyle.opacity = "0"; + } + }; + return CenterMessageBox; +}(UIElement_1.UIElement)); +exports.CenterMessageBox = CenterMessageBox; diff --git a/UI/ConfirmDialog.js b/UI/ConfirmDialog.js new file mode 100644 index 0000000..8e2a46f --- /dev/null +++ b/UI/ConfirmDialog.js @@ -0,0 +1,67 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ConfirmDialog = void 0; +var UIElement_1 = require("./UIElement"); +var UIEventSource_1 = require("./UIEventSource"); +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var VariableUIElement_1 = require("./Base/VariableUIElement"); +var ConfirmDialog = /** @class */ (function (_super) { + __extends(ConfirmDialog, _super); + function ConfirmDialog(show, question, optionA, optionB, executeA, executeB, classA, classB) { + if (classA === void 0) { classA = ""; } + if (classB === void 0) { classB = ""; } + var _this = _super.call(this, show) || this; + _this._showOptions = new UIEventSource_1.UIEventSource(false); + _this.ListenTo(_this._showOptions); + var self = _this; + show.addCallback(function () { + self._showOptions.setData(false); + }); + _this._question = new FixedUiElement_1.FixedUiElement("<span class='ui-question'>" + question + "</span>") + .onClick(function () { + self._showOptions.setData(!self._showOptions.data); + }); + _this._optionA = new VariableUIElement_1.VariableUiElement(_this._showOptions.map(function (show) { return show ? "<div class='" + classA + "'>" + optionA + "</div>" : ""; })) + .onClick(function () { + self._showOptions.setData(false); + executeA(); + }); + _this._optionB = new VariableUIElement_1.VariableUiElement(_this._showOptions.map(function (show) { + return show ? "<div class='" + classB + "'>" + optionB + "</div>" : ""; + })) + .onClick(function () { + self._showOptions.setData(false); + executeB(); + }); + return _this; + } + ConfirmDialog.prototype.InnerRender = function () { + if (!this._source.data) { + return ""; + } + return this._question.Render() + + this._optionA.Render() + + this._optionB.Render(); + }; + ConfirmDialog.prototype.Update = function () { + _super.prototype.Update.call(this); + this._question.Update(); + this._optionA.Update(); + this._optionB.Update(); + }; + return ConfirmDialog; +}(UIElement_1.UIElement)); +exports.ConfirmDialog = ConfirmDialog; diff --git a/UI/FeatureInfoBox.js b/UI/FeatureInfoBox.js new file mode 100644 index 0000000..768b4e3 --- /dev/null +++ b/UI/FeatureInfoBox.js @@ -0,0 +1,89 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FeatureInfoBox = void 0; +var UIElement_1 = require("./UIElement"); +var VerticalCombine_1 = require("./Base/VerticalCombine"); +var TagRendering_1 = require("../Customizations/TagRendering"); +var OsmLink_1 = require("../Customizations/Questions/OsmLink"); +var WikipediaLink_1 = require("../Customizations/Questions/WikipediaLink"); +var TagsFilter_1 = require("../Logic/TagsFilter"); +var FeatureInfoBox = /** @class */ (function (_super) { + __extends(FeatureInfoBox, _super); + function FeatureInfoBox(tagsES, title, elementsToShow, changes, userDetails) { + var _this = _super.call(this, tagsES) || this; + _this._tagsES = tagsES; + _this._changes = changes; + _this._userDetails = userDetails; + _this.ListenTo(userDetails); + var deps = { tags: _this._tagsES, changes: _this._changes }; + _this._infoboxes = []; + elementsToShow = elementsToShow !== null && elementsToShow !== void 0 ? elementsToShow : []; + for (var _i = 0, elementsToShow_1 = elementsToShow; _i < elementsToShow_1.length; _i++) { + var tagRenderingOption = elementsToShow_1[_i]; + _this._infoboxes.push(tagRenderingOption.construct(deps)); + } + title = title !== null && title !== void 0 ? title : new TagRendering_1.TagRenderingOptions({ + mappings: [{ k: new TagsFilter_1.And([]), txt: "" }] + }); + _this._title = new TagRendering_1.TagRenderingOptions(title.options).construct(deps); + _this._osmLink = new OsmLink_1.OsmLink().construct(deps); + _this._wikipedialink = new WikipediaLink_1.WikipediaLink().construct(deps); + return _this; + } + FeatureInfoBox.prototype.InnerRender = function () { + var info = []; + var questions = []; + for (var _i = 0, _a = this._infoboxes; _i < _a.length; _i++) { + var infobox = _a[_i]; + if (infobox.IsKnown()) { + info.push(infobox); + } + else if (infobox.IsQuestioning()) { + questions.push(infobox); + } + } + var questionsHtml = ""; + if (this._userDetails.data.loggedIn && questions.length > 0) { + // We select the most important question and render that one + var mostImportantQuestion = void 0; + var score = -1000; + for (var _b = 0, questions_1 = questions; _b < questions_1.length; _b++) { + var question = questions_1[_b]; + if (mostImportantQuestion === undefined || question.Priority() > score) { + mostImportantQuestion = question; + score = question.Priority(); + } + } + questionsHtml = mostImportantQuestion.Render(); + } + return "<div class='featureinfobox'>" + + "<div class='featureinfoboxtitle'>" + + "<span>" + + this._title.Render() + + "</span>" + + this._wikipedialink.Render() + + this._osmLink.Render() + + "</div>" + + "<div class='infoboxcontents'>" + + new VerticalCombine_1.VerticalCombine(info, "infobox-information ").Render() + + questionsHtml + + "</div>" + + "" + + "</div>"; + }; + return FeatureInfoBox; +}(UIElement_1.UIElement)); +exports.FeatureInfoBox = FeatureInfoBox; diff --git a/UI/Image/ImageCarousel.js b/UI/Image/ImageCarousel.js new file mode 100644 index 0000000..9009daf --- /dev/null +++ b/UI/Image/ImageCarousel.js @@ -0,0 +1,112 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageCarousel = exports.ImageCarouselConstructor = void 0; +var ImageSearcher_1 = require("../../Logic/ImageSearcher"); +var SlideShow_1 = require("../SlideShow"); +var FixedUiElement_1 = require("../Base/FixedUiElement"); +var VariableUIElement_1 = require("../Base/VariableUIElement"); +var ConfirmDialog_1 = require("../ConfirmDialog"); +var UIElementConstructor_1 = require("../../Customizations/UIElementConstructor"); +var ImageCarouselConstructor = /** @class */ (function () { + function ImageCarouselConstructor() { + } + ImageCarouselConstructor.prototype.IsKnown = function (properties) { + return true; + }; + ImageCarouselConstructor.prototype.IsQuestioning = function (properties) { + return false; + }; + ImageCarouselConstructor.prototype.Priority = function () { + return 0; + }; + ImageCarouselConstructor.prototype.construct = function (tags, changes) { + return new ImageCarousel(tags, changes); + }; + return ImageCarouselConstructor; +}()); +exports.ImageCarouselConstructor = ImageCarouselConstructor; +var ImageCarousel = /** @class */ (function (_super) { + __extends(ImageCarousel, _super); + function ImageCarousel(tags, changes) { + var _this = _super.call(this, tags) || this; + _this._userDetails = changes.login.userDetails; + var self = _this; + _this.searcher = new ImageSearcher_1.ImageSearcher(tags, changes); + _this._uiElements = _this.searcher.map(function (imageURLS) { + var uiElements = []; + for (var _i = 0, imageURLS_1 = imageURLS; _i < imageURLS_1.length; _i++) { + var url = imageURLS_1[_i]; + var image = ImageSearcher_1.ImageSearcher.CreateImageElement(url); + uiElements.push(image); + } + return uiElements; + }); + _this.slideshow = new SlideShow_1.SlideShow(_this._uiElements, new FixedUiElement_1.FixedUiElement("")).HideOnEmpty(true); + var showDeleteButton = _this.slideshow._currentSlide.map(function (i) { + if (!self._userDetails.data.loggedIn) { + return false; + } + return self.searcher.IsDeletable(self.searcher.data[i]); + }, [_this.searcher, _this._userDetails]); + _this.slideshow._currentSlide.addCallback(function () { + showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons + }); + var deleteCurrent = function () { + self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]); + }; + _this._deleteButton = new ConfirmDialog_1.ConfirmDialog(showDeleteButton, "<img src='assets/delete.svg' alt='Afbeelding verwijderen' class='delete-image'>", "<span>Afbeelding verwijderen</span>", "<span>Terug</span>", deleteCurrent, function () { }, 'delete-image-confirm', 'delete-image-cancel'); + var mapping = _this.slideshow._currentSlide.map(function (i) { + if (_this.searcher._deletedImages.data.indexOf(_this.searcher.data[i]) >= 0) { + return "<div class='image-is-removed'>Deze afbeelding is verwijderd</div>"; + } + return ""; + }); + _this._isDeleted = new VariableUIElement_1.VariableUiElement(mapping); + _this.searcher._deletedImages.addCallback(function () { + _this.slideshow._currentSlide.ping(); + }); + return _this; + } + ImageCarousel.prototype.InnerRender = function () { + return "<span class='image-carousel-container'>" + + "<div class='image-delete-container'>" + + this._deleteButton.Render() + + this._isDeleted.Render() + + "</div>" + + this.slideshow.Render() + + "</span>"; + }; + ImageCarousel.prototype.IsKnown = function () { + return true; + }; + ImageCarousel.prototype.IsQuestioning = function () { + return false; + }; + ImageCarousel.prototype.Priority = function () { + return 0; + }; + ImageCarousel.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + this._deleteButton.Update(); + this._isDeleted.Update(); + }; + ImageCarousel.prototype.Activate = function () { + _super.prototype.Activate.call(this); + this.searcher.Activate(); + }; + return ImageCarousel; +}(UIElementConstructor_1.TagDependantUIElement)); +exports.ImageCarousel = ImageCarousel; diff --git a/UI/Image/ImageCarouselWithUpload.js b/UI/Image/ImageCarouselWithUpload.js new file mode 100644 index 0000000..3138457 --- /dev/null +++ b/UI/Image/ImageCarouselWithUpload.js @@ -0,0 +1,74 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageCarouselWithUploadConstructor = void 0; +var UIElementConstructor_1 = require("../../Customizations/UIElementConstructor"); +var ImageCarousel_1 = require("./ImageCarousel"); +var OsmImageUploadHandler_1 = require("../../Logic/OsmImageUploadHandler"); +var ImageCarouselWithUploadConstructor = /** @class */ (function () { + function ImageCarouselWithUploadConstructor() { + } + ImageCarouselWithUploadConstructor.prototype.IsKnown = function (properties) { + return true; + }; + ImageCarouselWithUploadConstructor.prototype.IsQuestioning = function (properties) { + return false; + }; + ImageCarouselWithUploadConstructor.prototype.Priority = function () { + return 0; + }; + ImageCarouselWithUploadConstructor.prototype.construct = function (dependencies) { + return new ImageCarouselWithUpload(dependencies); + }; + return ImageCarouselWithUploadConstructor; +}()); +exports.ImageCarouselWithUploadConstructor = ImageCarouselWithUploadConstructor; +var ImageCarouselWithUpload = /** @class */ (function (_super) { + __extends(ImageCarouselWithUpload, _super); + function ImageCarouselWithUpload(dependencies) { + var _this = _super.call(this, dependencies.tags) || this; + var tags = dependencies.tags; + var changes = dependencies.changes; + _this._imageElement = new ImageCarousel_1.ImageCarousel(tags, changes); + var userDetails = changes.login.userDetails; + var license = changes.login.GetPreference("mapcomplete-pictures-license"); + _this._pictureUploader = new OsmImageUploadHandler_1.OsmImageUploadHandler(tags, userDetails, license, changes, _this._imageElement.slideshow).getUI(); + return _this; + } + ImageCarouselWithUpload.prototype.InnerRender = function () { + return this._imageElement.Render() + + this._pictureUploader.Render(); + }; + ImageCarouselWithUpload.prototype.Activate = function () { + _super.prototype.Activate.call(this); + this._imageElement.Activate(); + this._pictureUploader.Activate(); + }; + ImageCarouselWithUpload.prototype.Update = function () { + _super.prototype.Update.call(this); + this._imageElement.Update(); + this._pictureUploader.Update(); + }; + ImageCarouselWithUpload.prototype.IsKnown = function () { + return true; + }; + ImageCarouselWithUpload.prototype.IsQuestioning = function () { + return false; + }; + ImageCarouselWithUpload.prototype.Priority = function () { + return 0; + }; + return ImageCarouselWithUpload; +}(UIElementConstructor_1.TagDependantUIElement)); diff --git a/UI/Image/ImgurImage.js b/UI/Image/ImgurImage.js new file mode 100644 index 0000000..ae98025 --- /dev/null +++ b/UI/Image/ImgurImage.js @@ -0,0 +1,59 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImgurImage = void 0; +var UIEventSource_1 = require("../UIEventSource"); +var UIElement_1 = require("../UIElement"); +var Imgur_1 = require("../../Logic/Imgur"); +var ImgurImage = /** @class */ (function (_super) { + __extends(ImgurImage, _super); + function ImgurImage(source) { + var _this = _super.call(this, undefined) || this; + _this._imageLocation = source; + if (ImgurImage.allLicenseInfos[source] !== undefined) { + _this._imageMeta = ImgurImage.allLicenseInfos[source]; + } + else { + _this._imageMeta = new UIEventSource_1.UIEventSource(null); + ImgurImage.allLicenseInfos[source] = _this._imageMeta; + var self_1 = _this; + Imgur_1.Imgur.getDescriptionOfImage(source, function (license) { + self_1._imageMeta.setData(license); + }); + } + _this.ListenTo(_this._imageMeta); + return _this; + } + ImgurImage.prototype.InnerRender = function () { + var _a, _b; + var image = "<img src='" + this._imageLocation + "' " + "alt='' >"; + if (this._imageMeta.data === null) { + return image; + } + var attribution = "<span class='attribution-author'><b>" + ((_a = this._imageMeta.data.artist) !== null && _a !== void 0 ? _a : "") + "</b></span>" + " <span class='license'>" + ((_b = this._imageMeta.data.licenseShortName) !== null && _b !== void 0 ? _b : "") + "</span>"; + return "<div class='imgWithAttr'>" + + image + + "<div class='attribution'>" + + attribution + + "</div>" + + "</div>"; + }; + /*** + * Dictionary from url to alreayd known license info + */ + ImgurImage.allLicenseInfos = {}; + return ImgurImage; +}(UIElement_1.UIElement)); +exports.ImgurImage = ImgurImage; diff --git a/UI/Image/SimpleImageElement.js b/UI/Image/SimpleImageElement.js new file mode 100644 index 0000000..946e8a0 --- /dev/null +++ b/UI/Image/SimpleImageElement.js @@ -0,0 +1,28 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SimpleImageElement = void 0; +var UIElement_1 = require("../UIElement"); +var SimpleImageElement = /** @class */ (function (_super) { + __extends(SimpleImageElement, _super); + function SimpleImageElement(source) { + return _super.call(this, source) || this; + } + SimpleImageElement.prototype.InnerRender = function () { + return "<img src='" + this._source.data + "' alt='img'>"; + }; + return SimpleImageElement; +}(UIElement_1.UIElement)); +exports.SimpleImageElement = SimpleImageElement; diff --git a/UI/Image/WikimediaImage.js b/UI/Image/WikimediaImage.js new file mode 100644 index 0000000..7a25772 --- /dev/null +++ b/UI/Image/WikimediaImage.js @@ -0,0 +1,59 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WikimediaImage = void 0; +var UIEventSource_1 = require("../UIEventSource"); +var UIElement_1 = require("../UIElement"); +var Wikimedia_1 = require("../../Logic/Wikimedia"); +var WikimediaImage = /** @class */ (function (_super) { + __extends(WikimediaImage, _super); + function WikimediaImage(source) { + var _this = _super.call(this, undefined) || this; + _this._imageLocation = source; + if (WikimediaImage.allLicenseInfos[source] !== undefined) { + _this._imageMeta = WikimediaImage.allLicenseInfos[source]; + } + else { + _this._imageMeta = new UIEventSource_1.UIEventSource(new Wikimedia_1.LicenseInfo()); + WikimediaImage.allLicenseInfos[source] = _this._imageMeta; + var self_1 = _this; + Wikimedia_1.Wikimedia.LicenseData(source, function (info) { + self_1._imageMeta.setData(info); + }); + } + _this.ListenTo(_this._imageMeta); + return _this; + } + WikimediaImage.prototype.InnerRender = function () { + var _a, _b; + var url = Wikimedia_1.Wikimedia.ImageNameToUrl(this._imageLocation, 500, 400); + url = url.replace(/'/g, '%27'); + var wikimediaLink = "<a href='https://commons.wikimedia.org/wiki/" + this._imageLocation + "' target='_blank'>" + + "<img class='wikimedia-link' src='./assets/wikimedia-commons-white.svg' alt='Wikimedia Commons Logo'/>" + + "</a> "; + var attribution = "<span class='attribution-author'>" + ((_a = this._imageMeta.data.artist) !== null && _a !== void 0 ? _a : "") + "</span>" + " <span class='license'>" + ((_b = this._imageMeta.data.licenseShortName) !== null && _b !== void 0 ? _b : "") + "</span>"; + var image = "<img src='" + url + "' " + "alt='" + this._imageMeta.data.description + "' >"; + return "<div class='imgWithAttr'>" + + image + + "<div class='attribution'>" + + wikimediaLink + + attribution + + "</div>" + + "</div>"; + }; + WikimediaImage.allLicenseInfos = {}; + return WikimediaImage; +}(UIElement_1.UIElement)); +exports.WikimediaImage = WikimediaImage; diff --git a/UI/ImageUploadFlow.js b/UI/ImageUploadFlow.js new file mode 100644 index 0000000..469fc59 --- /dev/null +++ b/UI/ImageUploadFlow.js @@ -0,0 +1,107 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ImageUploadFlow = void 0; +var UIElement_1 = require("./UIElement"); +var UIEventSource_1 = require("./UIEventSource"); +var jquery_1 = require("jquery"); +var Imgur_1 = require("../Logic/Imgur"); +var DropDown_1 = require("./Input/DropDown"); +var VariableUIElement_1 = require("./Base/VariableUIElement"); +var ImageUploadFlow = /** @class */ (function (_super) { + __extends(ImageUploadFlow, _super); + function ImageUploadFlow(userInfo, preferedLicense, uploadOptions) { + var _this = _super.call(this, undefined) || this; + _this._isUploading = new UIEventSource_1.UIEventSource(0); + _this._userdetails = userInfo; + _this.ListenTo(userInfo); + _this._uploadOptions = uploadOptions; + _this.ListenTo(_this._isUploading); + var licensePicker = new DropDown_1.DropDown("Jouw foto wordt gepubliceerd ", [ + { value: "CC0", shown: "in het publiek domein" }, + { value: "CC-BY-SA 4.0", shown: "onder een CC-BY-SA-licentie" }, + { value: "CC-BY 4.0", shown: "onder een CC-BY-licentie" } + ], preferedLicense); + _this._licensePicker = licensePicker; + _this._selectedLicence = licensePicker.selectedElement; + var licenseExplanations = { + "CC-BY-SA 4.0": "<b>Creative Commonse met naamsvermelding en gelijk delen</b><br/>" + + "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden én ze afgeleide werken met deze licentie en attributie delen.", + "CC-BY 4.0": "<b>Creative Commonse met naamsvermelding</b> <br/>" + + "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden", + "CC0": "<b>Geen copyright</b><br/> Je foto mag door iedereen voor alles gebruikt worden" + }; + _this._licenseExplanation = new VariableUIElement_1.VariableUiElement(_this._selectedLicence.map(function (license) { + return licenseExplanations[license]; + })); + return _this; + } + ImageUploadFlow.prototype.InnerRender = function () { + if (!this._userdetails.data.loggedIn) { + return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden</div>"; + } + if (this._isUploading.data == 1) { + return "<b>Bezig met een foto te uploaden...</b>"; + } + if (this._isUploading.data > 0) { + return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>"; + } + return "" + + "<div class='imageflow'>" + + "<label for='fileselector-" + this.id + "'>" + + "<div class='imageflow-file-input-wrapper'>" + + "<img src='./assets/camera-plus.svg' alt='upload image'/> " + + "<span class='imageflow-add-picture'>Voeg foto toe</span>" + + "<div class='break'></div>" + + "</div>" + + this._licensePicker.Render() + + "</label>" + + "<input id='fileselector-" + this.id + "' " + + "type='file' " + + "class='imageflow-file-input' " + + "accept='image/*' name='picField' size='24' multiple='multiple' alt=''" + + "/>" + + "</div>"; + }; + ImageUploadFlow.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + var user = this._userdetails.data; + htmlElement.onclick = function () { + if (!user.loggedIn) { + user.osmConnection.AttemptLogin(); + } + }; + this._licensePicker.Update(); + var selector = document.getElementById('fileselector-' + this.id); + var self = this; + if (selector != null) { + selector.onchange = function () { + var files = jquery_1.default(this).get(0).files; + self._isUploading.setData(files.length); + var opts = self._uploadOptions(self._selectedLicence.data); + Imgur_1.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"); + opts.allDone(); + }); + }; + } + }; + return ImageUploadFlow; +}(UIElement_1.UIElement)); +exports.ImageUploadFlow = ImageUploadFlow; diff --git a/UI/Img.js b/UI/Img.js new file mode 100644 index 0000000..a88b7ed --- /dev/null +++ b/UI/Img.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Img = void 0; +var Img = /** @class */ (function () { + function Img() { + } + Img.osmAbstractLogo = "<svg class='osm-logo' xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" width=\"24px\" version=\"1.1\" viewBox=\"0 0 66 64\">" + + " <g transform=\"translate(-0.849, -61)\">\n" + + " <path d=\"M0.849,61 L6.414,75.609 L0.849,90.217 L6.414,104.826 L0.849,119.435 L4.266,120.739 L22.831,102.183 L26.162,102.696 L30.205,98.652 C27.819,95.888 26.033,92.59 25.057,88.948 L26.953,87.391 C26.627,85.879 26.449,84.313 26.449,82.704 C26.449,74.67 30.734,67.611 37.136,63.696 L30.066,61 L15.457,66.565 L0.849,61 z\"></path>" + + " <path d=\"M48.71,64.617 C48.406,64.617 48.105,64.629 47.805,64.643 C47.52,64.657 47.234,64.677 46.953,64.704 C46.726,64.726 46.499,64.753 46.275,64.783 C46.039,64.814 45.811,64.847 45.579,64.887 C45.506,64.9 45.434,64.917 45.362,64.93 C45.216,64.958 45.072,64.987 44.927,65.017 C44.812,65.042 44.694,65.06 44.579,65.087 C44.442,65.119 44.307,65.156 44.17,65.191 C43.943,65.25 43.716,65.315 43.492,65.383 C43.323,65.433 43.155,65.484 42.988,65.539 C42.819,65.595 42.65,65.652 42.483,65.713 C42.475,65.716 42.466,65.719 42.457,65.722 C35.819,68.158 31.022,74.369 30.649,81.774 C30.633,82.083 30.622,82.391 30.622,82.704 C30.622,83.014 30.631,83.321 30.649,83.626 C30.649,83.629 30.648,83.632 30.649,83.635 C30.662,83.862 30.681,84.088 30.701,84.313 C31.466,93.037 38.377,99.948 47.101,100.713 C47.326,100.733 47.552,100.754 47.779,100.765 C47.782,100.765 47.785,100.765 47.788,100.765 C48.093,100.783 48.399,100.791 48.709,100.791 C53.639,100.791 58.096,98.833 61.353,95.652 C61.532,95.477 61.712,95.304 61.883,95.122 C61.913,95.09 61.941,95.058 61.97,95.026 C61.98,95.015 61.987,95.002 61.996,94.991 C62.132,94.845 62.266,94.698 62.396,94.548 C62.449,94.487 62.501,94.426 62.553,94.365 C62.594,94.316 62.634,94.267 62.675,94.217 C62.821,94.04 62.961,93.861 63.101,93.678 C63.279,93.444 63.456,93.199 63.622,92.956 C63.956,92.471 64.267,91.97 64.553,91.452 C64.661,91.257 64.757,91.06 64.857,90.861 C64.89,90.796 64.93,90.735 64.962,90.67 C64.98,90.633 64.996,90.594 65.014,90.556 C65.125,90.324 65.234,90.09 65.336,89.852 C65.349,89.82 65.365,89.789 65.379,89.756 C65.48,89.517 65.575,89.271 65.666,89.026 C65.678,88.994 65.689,88.962 65.701,88.93 C65.792,88.679 65.881,88.43 65.962,88.174 C65.97,88.148 65.98,88.122 65.988,88.096 C66.069,87.832 66.144,87.564 66.214,87.296 C66.219,87.275 66.226,87.255 66.231,87.235 C66.301,86.962 66.365,86.686 66.423,86.409 C66.426,86.391 66.428,86.374 66.431,86.356 C66.445,86.291 66.453,86.223 66.466,86.156 C66.511,85.925 66.552,85.695 66.588,85.461 C66.632,85.169 66.671,84.878 66.701,84.583 C66.701,84.574 66.701,84.565 66.701,84.556 C66.731,84.258 66.755,83.955 66.77,83.652 C66.77,83.646 66.77,83.641 66.77,83.635 C66.786,83.326 66.797,83.017 66.797,82.704 C66.797,72.69 58.723,64.617 48.71,64.617 z\"></path>" + + " <path d=\"M62.936,99.809 C59.074,103.028 54.115,104.965 48.71,104.965 C47.101,104.965 45.535,104.787 44.023,104.461 L42.466,106.357 C39.007,105.43 35.855,103.781 33.179,101.574 L28.996,105.765 L29.51,108.861 L13.953,124.426 L15.457,125 L30.066,119.435 L44.675,125 L59.283,119.435 L64.849,104.826 L62.936,99.809 z\"></path>" + + " </g>" + + "</svg>"; + return Img; +}()); +exports.Img = Img; diff --git a/UI/Input/DropDown.js b/UI/Input/DropDown.js new file mode 100644 index 0000000..0914165 --- /dev/null +++ b/UI/Input/DropDown.js @@ -0,0 +1,69 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DropDown = void 0; +var UIEventSource_1 = require("../UIEventSource"); +var UIElement_1 = require("../UIElement"); +var DropDown = /** @class */ (function (_super) { + __extends(DropDown, _super); + function DropDown(label, values, selectedElement) { + if (selectedElement === void 0) { selectedElement = undefined; } + var _this = _super.call(this, undefined) || this; + _this._label = label; + _this._values = values; + _this.selectedElement = selectedElement !== null && selectedElement !== void 0 ? selectedElement : new UIEventSource_1.UIEventSource(values[0].value); + if (selectedElement.data === undefined) { + _this.selectedElement.setData(values[0].value); + } + var self = _this; + _this.selectedElement.addCallback(function () { + self.InnerUpdate(); + }); + return _this; + } + DropDown.prototype.InnerRender = function () { + var options = ""; + for (var _i = 0, _a = this._values; _i < _a.length; _i++) { + var value = _a[_i]; + options += "<option value='" + value.value + "'>" + value.shown + "</option>"; + } + return "<form>" + + "<label for='dropdown-" + this.id + "'>" + this._label + "</label>" + + "<select name='dropdown-" + this.id + "' id='dropdown-" + this.id + "'>" + + options + + "</select>" + + "</form>"; + }; + DropDown.prototype.InnerUpdate = function () { + var self = this; + var e = document.getElementById("dropdown-" + this.id); + if (e === null) { + return; + } + // @ts-ignore + if (this.selectedElement.data !== e.value) { + // @ts-ignore + e.value = this.selectedElement.data; + } + e.onchange = function () { + // @ts-ignore + var selectedValue = e.options[e.selectedIndex].value; + console.log("Putting data", selectedValue); + self.selectedElement.setData(selectedValue); + }; + }; + return DropDown; +}(UIElement_1.UIElement)); +exports.DropDown = DropDown; diff --git a/UI/Input/FixedInputElement.js b/UI/Input/FixedInputElement.js new file mode 100644 index 0000000..8b88a6c --- /dev/null +++ b/UI/Input/FixedInputElement.js @@ -0,0 +1,39 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FixedInputElement = void 0; +var InputElement_1 = require("./InputElement"); +var UIEventSource_1 = require("../UIEventSource"); +var FixedUiElement_1 = require("../Base/FixedUiElement"); +var FixedInputElement = /** @class */ (function (_super) { + __extends(FixedInputElement, _super); + function FixedInputElement(rendering, value) { + var _this = _super.call(this, undefined) || this; + _this.value = new UIEventSource_1.UIEventSource(value); + _this.rendering = typeof (rendering) === 'string' ? new FixedUiElement_1.FixedUiElement(rendering) : rendering; + return _this; + } + FixedInputElement.prototype.GetValue = function () { + return this.value; + }; + FixedInputElement.prototype.InnerRender = function () { + return this.rendering.Render(); + }; + FixedInputElement.prototype.IsValid = function (t) { + return t === this.value.data; + }; + return FixedInputElement; +}(InputElement_1.InputElement)); +exports.FixedInputElement = FixedInputElement; diff --git a/UI/Input/InputElement.js b/UI/Input/InputElement.js new file mode 100644 index 0000000..f8715c7 --- /dev/null +++ b/UI/Input/InputElement.js @@ -0,0 +1,25 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InputElement = void 0; +var UIElement_1 = require("../UIElement"); +var InputElement = /** @class */ (function (_super) { + __extends(InputElement, _super); + function InputElement() { + return _super !== null && _super.apply(this, arguments) || this; + } + return InputElement; +}(UIElement_1.UIElement)); +exports.InputElement = InputElement; diff --git a/UI/Input/InputElementWrapper.js b/UI/Input/InputElementWrapper.js new file mode 100644 index 0000000..68547c3 --- /dev/null +++ b/UI/Input/InputElementWrapper.js @@ -0,0 +1,39 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.InputElementWrapper = void 0; +var InputElement_1 = require("./InputElement"); +var UIElement_1 = require("../UIElement"); +var InputElementWrapper = /** @class */ (function (_super) { + __extends(InputElementWrapper, _super); + function InputElementWrapper(pre, input, post) { + var _this = _super.call(this, undefined) || this; + _this.post = UIElement_1.UIElement.Fix(post); + _this.input = input; + _this.pre = UIElement_1.UIElement.Fix(pre); + return _this; + } + InputElementWrapper.prototype.GetValue = function () { + return this.input.GetValue(); + }; + InputElementWrapper.prototype.InnerRender = function () { + return this.pre.Render() + this.input.Render() + this.post.Render(); + }; + InputElementWrapper.prototype.IsValid = function (t) { + return this.input.IsValid(t); + }; + return InputElementWrapper; +}(InputElement_1.InputElement)); +exports.InputElementWrapper = InputElementWrapper; diff --git a/UI/Input/RadioButton.js b/UI/Input/RadioButton.js new file mode 100644 index 0000000..accbb18 --- /dev/null +++ b/UI/Input/RadioButton.js @@ -0,0 +1,122 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RadioButton = void 0; +var UIEventSource_1 = require("../UIEventSource"); +var InputElement_1 = require("./InputElement"); +var RadioButton = /** @class */ (function (_super) { + __extends(RadioButton, _super); + function RadioButton(elements, selectFirstAsDefault) { + if (selectFirstAsDefault === void 0) { selectFirstAsDefault = true; } + var _this = _super.call(this, undefined) || this; + _this._selectedElementIndex = new UIEventSource_1.UIEventSource(null); + _this._elements = elements; + _this._selectFirstAsDefault = selectFirstAsDefault; + var self = _this; + _this.value = + UIEventSource_1.UIEventSource.flatten(_this._selectedElementIndex.map(function (selectedIndex) { + if (selectedIndex !== undefined && selectedIndex !== null) { + return elements[selectedIndex].GetValue(); + } + }), elements.map(function (e) { return e.GetValue(); })); + _this.value.addCallback(function (t) { + self.SetCorrectValue(t); + }); + var _loop_1 = function (i) { + // If an element is clicked, the radio button corresponding with it should be selected as well + elements[i].onClick(function () { + self._selectedElementIndex.setData(i); + }); + }; + for (var i = 0; i < elements.length; i++) { + _loop_1(i); + } + return _this; + } + RadioButton.prototype.IsValid = function (t) { + for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { + var inputElement = _a[_i]; + if (inputElement.IsValid(t)) { + return true; + } + } + return false; + }; + RadioButton.prototype.GetValue = function () { + return this.value; + }; + RadioButton.prototype.IdFor = function (i) { + return 'radio-' + this.id + '-' + i; + }; + RadioButton.prototype.InnerRender = function () { + var body = ""; + var i = 0; + for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { + var el = _a[_i]; + var htmlElement = '<input type="radio" id="' + this.IdFor(i) + '" name="radiogroup-' + this.id + '">' + + '<label for="' + this.IdFor(i) + '">' + el.Render() + '</label>' + + '<br>'; + body += htmlElement; + i++; + } + return "<form id='" + this.id + "-form'>" + body + "</form>"; + }; + RadioButton.prototype.SetCorrectValue = function (t) { + if (t === undefined) { + return; + } + // We check that what is selected matches the previous rendering + for (var i = 0; i < this._elements.length; i++) { + var e = this._elements[i]; + if (e.IsValid(t)) { + this._selectedElementIndex.setData(i); + e.GetValue().setData(t); + // @ts-ignore + document.getElementById(this.IdFor(i)).checked = true; + return; + } + } + }; + RadioButton.prototype.InnerUpdate = function (htmlElement) { + var self = this; + function checkButtons() { + for (var i = 0; i < self._elements.length; i++) { + var el_1 = document.getElementById(self.IdFor(i)); + // @ts-ignore + if (el_1.checked) { + self._selectedElementIndex.setData(i); + } + } + } + var el = document.getElementById(this.id); + el.addEventListener("change", function () { + checkButtons(); + }); + if (this._selectFirstAsDefault) { + this.SetCorrectValue(this.value.data); + if (this._selectedElementIndex.data === null || this._selectedElementIndex.data === undefined) { + var el_2 = document.getElementById(this.IdFor(0)); + if (el_2) { + // @ts-ignore + el_2.checked = true; + checkButtons(); + } + } + } + }; + ; + return RadioButton; +}(InputElement_1.InputElement)); +exports.RadioButton = RadioButton; diff --git a/UI/Input/TextField.js b/UI/Input/TextField.js new file mode 100644 index 0000000..3ab7557 --- /dev/null +++ b/UI/Input/TextField.js @@ -0,0 +1,94 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TextField = void 0; +var UIEventSource_1 = require("../UIEventSource"); +var InputElement_1 = require("./InputElement"); +var FixedUiElement_1 = require("../Base/FixedUiElement"); +var TextField = /** @class */ (function (_super) { + __extends(TextField, _super); + function TextField(options) { + var _a, _b, _c, _d; + var _this = _super.call(this, undefined) || this; + /** + * Pings and has the value data + */ + _this.enterPressed = new UIEventSource_1.UIEventSource(undefined); + _this.value = new UIEventSource_1.UIEventSource(""); + _this.mappedValue = (_a = options === null || options === void 0 ? void 0 : options.value) !== null && _a !== void 0 ? _a : new UIEventSource_1.UIEventSource(undefined); + // @ts-ignore + _this._fromString = (_b = options.fromString) !== null && _b !== void 0 ? _b : (function (str) { return (str); }); + _this.value.addCallback(function (str) { return _this.mappedValue.setData(options.fromString(str)); }); + _this.mappedValue.addCallback(function (t) { return _this.value.setData(options.toString(t)); }); + _this._placeholder = + typeof (options.placeholder) === "string" ? new FixedUiElement_1.FixedUiElement(options.placeholder) : + ((_c = options.placeholder) !== null && _c !== void 0 ? _c : new FixedUiElement_1.FixedUiElement("")); + _this._toString = (_d = options.toString) !== null && _d !== void 0 ? _d : (function (t) { return ("" + t); }); + var self = _this; + _this.mappedValue.addCallback(function (t) { + if (t === undefined && t === null) { + return; + } + var field = document.getElementById('text-' + _this.id); + if (field === undefined || field === null) { + return; + } + // @ts-ignore + field.value = options.toString(t); + }); + return _this; + } + TextField.prototype.GetValue = function () { + return this.mappedValue; + }; + TextField.prototype.InnerRender = function () { + return "<form onSubmit='return false' class='form-text-field'>" + + "<input type='text' placeholder='" + this._placeholder.InnerRender() + "' id='text-" + this.id + "'>" + + "</form>"; + }; + TextField.prototype.InnerUpdate = function (htmlElement) { + var field = document.getElementById('text-' + this.id); + if (field === null) { + return; + } + var self = this; + field.oninput = function () { + // @ts-ignore + self.value.setData(field.value); + }; + field.addEventListener("keyup", function (event) { + if (event.key === "Enter") { + // @ts-ignore + self.enterPressed.setData(field.value); + } + }); + }; + TextField.prototype.IsValid = function (t) { + if (t === undefined || t === null) { + return false; + } + var result = this._toString(t); + return result !== undefined && result !== null; + }; + TextField.prototype.Clear = function () { + var field = document.getElementById('text-' + this.id); + if (field !== undefined) { + // @ts-ignore + field.value = ""; + } + }; + return TextField; +}(InputElement_1.InputElement)); +exports.TextField = TextField; diff --git a/UI/MessageBoxHandler.js b/UI/MessageBoxHandler.js new file mode 100644 index 0000000..1048e00 --- /dev/null +++ b/UI/MessageBoxHandler.js @@ -0,0 +1,56 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MessageBoxHandler = void 0; +/** + * Keeps 'messagebox' and 'messageboxmobile' in sync, shows a 'close' button on the latter one + */ +var UIEventSource_1 = require("./UIEventSource"); +var VariableUIElement_1 = require("./Base/VariableUIElement"); +var MessageBoxHandler = /** @class */ (function () { + function MessageBoxHandler(uielement, onClear) { + this._uielement = uielement; + this.listenTo(uielement); + this.update(); + window.onhashchange = function () { + if (location.hash === "") { + // No more element: back to the map! + uielement.setData(undefined); + onClear(); + } + }; + new VariableUIElement_1.VariableUiElement(new UIEventSource_1.UIEventSource("<h2>Naar de kaart</h2>"), function () { + document.getElementById("to-the-map").onclick = function () { + uielement.setData(undefined); + onClear(); + }; + }).AttachTo("to-the-map"); + } + MessageBoxHandler.prototype.listenTo = function (uiEventSource) { + var self = this; + uiEventSource.addCallback(function () { + self.update(); + }); + }; + MessageBoxHandler.prototype.update = function () { + var _a, _b, _c; + var wrapper = document.getElementById("messagesboxmobilewrapper"); + var gen = this._uielement.data; + console.log("Generator: ", gen); + if (gen === undefined) { + wrapper.classList.add("hidden"); + if (location.hash !== "") { + location.hash = ""; + } + return; + } + location.hash = "#element"; + wrapper.classList.remove("hidden"); + /* gen() + ?.HideOnEmpty(true) + ?.AttachTo("messagesbox") + ?.Activate();*/ + (_c = (_b = (_a = gen()) === null || _a === void 0 ? void 0 : _a.HideOnEmpty(true)) === null || _b === void 0 ? void 0 : _b.AttachTo("messagesboxmobile")) === null || _c === void 0 ? void 0 : _c.Activate(); + }; + return MessageBoxHandler; +}()); +exports.MessageBoxHandler = MessageBoxHandler; diff --git a/UI/PendingChanges.js b/UI/PendingChanges.js new file mode 100644 index 0000000..b96e03b --- /dev/null +++ b/UI/PendingChanges.js @@ -0,0 +1,49 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PendingChanges = void 0; +var UIElement_1 = require("./UIElement"); +var PendingChanges = /** @class */ (function (_super) { + __extends(PendingChanges, _super); + function PendingChanges(changes, countdown) { + var _this = _super.call(this, changes.pendingChangesES) || this; + _this.ListenTo(changes.isSaving); + _this.ListenTo(countdown); + _this._pendingChangesCount = changes.pendingChangesES; + _this._countdown = countdown; + _this._isSaving = changes.isSaving; + _this.onClick(function () { + changes.uploadAll(); + }); + return _this; + } + PendingChanges.prototype.InnerRender = function () { + if (this._isSaving.data) { + return "<span class='alert'>Saving</span>"; + } + if (this._pendingChangesCount.data == 0) { + return ""; + } + var restingSeconds = this._countdown.data / 1000; + var dots = ""; + while (restingSeconds > 0) { + dots += "."; + restingSeconds = restingSeconds - 1; + } + return "Saving " + this._pendingChangesCount.data; + }; + return PendingChanges; +}(UIElement_1.UIElement)); +exports.PendingChanges = PendingChanges; diff --git a/UI/QuestionPicker.js b/UI/QuestionPicker.js new file mode 100644 index 0000000..877172b --- /dev/null +++ b/UI/QuestionPicker.js @@ -0,0 +1,51 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.QuestionPicker = void 0; +var UIElement_1 = require("./UIElement"); +var QuestionPicker = /** @class */ (function (_super) { + __extends(QuestionPicker, _super); + function QuestionPicker(questions, tags) { + var _this = _super.call(this, tags) || this; + _this._questions = questions; + _this.tags = tags.data; + _this.source = tags; + return _this; + } + QuestionPicker.prototype.InnerRender = function () { + var t = this.tags; + var highestPriority = Number.MIN_VALUE; + var highestQ; + for (var _i = 0, _a = this._questions; _i < _a.length; _i++) { + var q = _a[_i]; + if (!q.Applicable(t)) { + continue; + } + var priority = q.question.severity; + if (priority > highestPriority) { + highestPriority = priority; + highestQ = q; + } + } + if (highestQ === undefined) { + return "Er zijn geen vragen meer!"; + } + return "<div class='question'>" + + highestQ.CreateHtml(this.source).Render() + + "</div>"; + }; + return QuestionPicker; +}(UIElement_1.UIElement)); +exports.QuestionPicker = QuestionPicker; diff --git a/UI/SaveButton.js b/UI/SaveButton.js new file mode 100644 index 0000000..14f6e42 --- /dev/null +++ b/UI/SaveButton.js @@ -0,0 +1,38 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SaveButton = void 0; +var UIElement_1 = require("./UIElement"); +var SaveButton = /** @class */ (function (_super) { + __extends(SaveButton, _super); + function SaveButton(value) { + var _this = _super.call(this, value) || this; + if (value === undefined) { + throw "No event source for savebutton, something is wrong"; + } + _this._value = value; + return _this; + } + SaveButton.prototype.InnerRender = function () { + if (this._value.data === undefined || + this._value.data === null + || this._value.data === "") { + return "<span class='save-non-active'>Opslaan</span>"; + } + return "<span class='save'>Opslaan</span>"; + }; + return SaveButton; +}(UIElement_1.UIElement)); +exports.SaveButton = SaveButton; diff --git a/UI/SearchAndGo.js b/UI/SearchAndGo.js new file mode 100644 index 0000000..69ef5ed --- /dev/null +++ b/UI/SearchAndGo.js @@ -0,0 +1,83 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SearchAndGo = void 0; +var UIElement_1 = require("./UIElement"); +var TextField_1 = require("./Input/TextField"); +var UIEventSource_1 = require("./UIEventSource"); +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var Geocoding_1 = require("../Logic/Geocoding"); +var SearchAndGo = /** @class */ (function (_super) { + __extends(SearchAndGo, _super); + function SearchAndGo(map) { + var _this = _super.call(this, undefined) || this; + _this._placeholder = new UIEventSource_1.UIEventSource("Zoek naar een locatie..."); + _this._searchField = new TextField_1.TextField({ + placeholder: _this._placeholder + }); + _this._foundEntries = new UIEventSource_1.UIEventSource([]); + _this._goButton = new FixedUiElement_1.FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>"); + _this._map = map; + _this.ListenTo(_this._foundEntries); + var self = _this; + _this._searchField.enterPressed.addCallback(function () { + self.RunSearch(); + }); + _this._goButton.onClick(function () { + self.RunSearch(); + }); + return _this; + } + // Triggered by 'enter' or onclick + SearchAndGo.prototype.RunSearch = function () { + var _this = this; + var searchString = this._searchField.GetValue().data; + this._searchField.Clear(); + this._placeholder.setData("Bezig met zoeken..."); + var self = this; + Geocoding_1.Geocoding.Search(searchString, this._map, function (result) { + if (result.length == 0) { + _this._placeholder.setData("Niets gevonden"); + return; + } + var bb = result[0].boundingbox; + var bounds = [ + [bb[0], bb[2]], + [bb[1], bb[3]] + ]; + self._map.map.fitBounds(bounds); + _this._placeholder.setData("Zoek naar een locatie..."); + }, function () { + _this._placeholder.setData("Niets gevonden: er ging iets mis"); + }); + }; + SearchAndGo.prototype.InnerRender = function () { + // "<img class='search' src='./assets/search.svg' alt='Search'> " + + return this._searchField.Render() + + this._goButton.Render(); + }; + SearchAndGo.prototype.Update = function () { + _super.prototype.Update.call(this); + this._searchField.Update(); + this._goButton.Update(); + }; + SearchAndGo.prototype.Activate = function () { + _super.prototype.Activate.call(this); + this._searchField.Activate(); + this._goButton.Activate(); + }; + return SearchAndGo; +}(UIElement_1.UIElement)); +exports.SearchAndGo = SearchAndGo; diff --git a/UI/SimpleAddUI.js b/UI/SimpleAddUI.js new file mode 100644 index 0000000..83a5064 --- /dev/null +++ b/UI/SimpleAddUI.js @@ -0,0 +1,84 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SimpleAddUI = void 0; +var UIElement_1 = require("./UIElement"); +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var Button_1 = require("./Base/Button"); +/** + * Asks to add a feature at the last clicked location, at least if zoom is sufficient + */ +var SimpleAddUI = /** @class */ (function (_super) { + __extends(SimpleAddUI, _super); + function SimpleAddUI(zoomlevel, lastClickLocation, changes, selectedElement, dataIsLoading, userDetails, addButtons) { + var _this = _super.call(this, zoomlevel) || this; + _this._zoomlevel = zoomlevel; + _this._lastClickLocation = lastClickLocation; + _this._changes = changes; + _this._selectedElement = selectedElement; + _this._dataIsLoading = dataIsLoading; + _this._userDetails = userDetails; + _this.ListenTo(userDetails); + _this.ListenTo(dataIsLoading); + _this._addButtons = []; + for (var _i = 0, addButtons_1 = addButtons; _i < addButtons_1.length; _i++) { + var option = addButtons_1[_i]; + // <button type='button'> looks SO retarded + // the default type of button is 'submit', which performs a POST and page reload + var button = new Button_1.Button(new FixedUiElement_1.FixedUiElement("Voeg hier een " + option.name + " toe"), _this.CreatePoint(option)); + _this._addButtons.push(button); + } + return _this; + } + SimpleAddUI.prototype.CreatePoint = function (option) { + var self = this; + return function () { + console.log("Creating a new ", option.name, " at last click location"); + var loc = self._lastClickLocation.data; + var feature = self._changes.createElement(option.tags, loc.lat, loc.lon); + option.layerToAddTo.AddNewElement(feature); + self._selectedElement.setData(feature.properties); + }; + }; + SimpleAddUI.prototype.InnerRender = function () { + var header = "<h2>Geen selectie</h2>" + + "Je klikte ergens waar er nog geen gezochte data is.<br/>"; + if (!this._userDetails.data.loggedIn) { + return header + "<a class='activate-osm-authentication'>Gelieve je aan te melden om een nieuw punt toe te voegen</a>"; + } + if (this._zoomlevel.data.zoom < 19) { + return header + "Zoom verder in om een element toe te voegen."; + } + if (this._dataIsLoading.data) { + return header + "De data is nog aan het laden. Nog even geduld, dan kan je een punt toevoegen"; + } + var html = ""; + for (var _i = 0, _a = this._addButtons; _i < _a.length; _i++) { + var button = _a[_i]; + html += button.Render(); + } + return header + html; + }; + SimpleAddUI.prototype.InnerUpdate = function (htmlElement) { + _super.prototype.InnerUpdate.call(this, htmlElement); + for (var _i = 0, _a = this._addButtons; _i < _a.length; _i++) { + var button = _a[_i]; + button.Update(); + } + this._userDetails.data.osmConnection.registerActivateOsmAUthenticationClass(); + }; + return SimpleAddUI; +}(UIElement_1.UIElement)); +exports.SimpleAddUI = SimpleAddUI; diff --git a/UI/SlideShow.js b/UI/SlideShow.js new file mode 100644 index 0000000..e49f78e --- /dev/null +++ b/UI/SlideShow.js @@ -0,0 +1,92 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SlideShow = void 0; +var UIElement_1 = require("./UIElement"); +var UIEventSource_1 = require("./UIEventSource"); +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var SlideShow = /** @class */ (function (_super) { + __extends(SlideShow, _super); + function SlideShow(embeddedElements, noImages) { + var _this = _super.call(this, embeddedElements) || this; + _this._currentSlide = new UIEventSource_1.UIEventSource(0); + _this._embeddedElements = embeddedElements; + _this.ListenTo(_this._currentSlide); + _this._noimages = noImages; + var self = _this; + _this._prev = new FixedUiElement_1.FixedUiElement("<div class='prev-button'>" + + "<div class='vspan'></div>" + + "<img src='assets/arrow-left-smooth.svg' alt='Prev'/>" + + "</div>") + .onClick(function () { + var current = self._currentSlide.data; + self.MoveTo(current - 1); + }); + _this._next = new FixedUiElement_1.FixedUiElement("<div class='next-button'>" + + "<div class='vspan'></div>" + + "<img src='assets/arrow-right-smooth.svg' alt='Next'/>" + + "</div>") + .onClick(function () { + var current = self._currentSlide.data; + self.MoveTo(current + 1); + }); + return _this; + } + SlideShow.prototype.InnerRender = function () { + if (this._embeddedElements.data.length == 0) { + return this._noimages.Render(); + } + if (this._embeddedElements.data.length == 1) { + return "<div class='image-slideshow'><div class='slides'><div class='slide'>" + + this._embeddedElements.data[0].Render() + + "</div></div></div>"; + } + var slides = ""; + for (var i = 0; i < this._embeddedElements.data.length; i++) { + var embeddedElement = this._embeddedElements.data[i]; + var state = "hidden"; + if (this._currentSlide.data === i) { + state = "active-slide"; + } + slides += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n"; + } + return "<div class='image-slideshow'>" + + this._prev.Render() + + "<div class='slides'>" + slides + "</div>" + + this._next.Render() + + "</div>"; + }; + SlideShow.prototype.MoveTo = function (index) { + if (index < 0) { + index = this._embeddedElements.data.length - 1; + } + index = index % this._embeddedElements.data.length; + this._currentSlide.setData(index); + }; + SlideShow.prototype.InnerUpdate = function (htmlElement) { + this._next.Update(); + this._prev.Update(); + }; + SlideShow.prototype.Activate = function () { + for (var _i = 0, _a = this._embeddedElements.data; _i < _a.length; _i++) { + var embeddedElement = _a[_i]; + embeddedElement.Activate(); + } + this._next.Update(); + this._prev.Update(); + }; + return SlideShow; +}(UIElement_1.UIElement)); +exports.SlideShow = SlideShow; diff --git a/UI/UIElement.js b/UI/UIElement.js new file mode 100644 index 0000000..ba51902 --- /dev/null +++ b/UI/UIElement.js @@ -0,0 +1,115 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UIElement = void 0; +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var UIElement = /** @class */ (function () { + function UIElement(source) { + this._hideIfEmpty = false; + this.id = "ui-element-" + UIElement.nextId; + this._source = source; + UIElement.nextId++; + this.ListenTo(source); + } + UIElement.prototype.ListenTo = function (source) { + if (source === undefined) { + return; + } + var self = this; + source.addCallback(function () { + self.Update(); + }); + }; + UIElement.prototype.onClick = function (f) { + this._onClick = f; + this.Update(); + return this; + }; + UIElement.Fix = function (element) { + if (typeof (element) === 'string') { + return new FixedUiElement_1.FixedUiElement(element); + } + return element; + }; + UIElement.prototype.Update = function () { + var element = document.getElementById(this.id); + if (element === null || element === undefined) { + // The element is not painted + return; + } + element.innerHTML = this.InnerRender(); + if (this._hideIfEmpty) { + if (element.innerHTML === "") { + element.parentElement.style.display = "none"; + } + else { + element.parentElement.style.display = undefined; + } + } + if (this._onClick !== undefined) { + var self_1 = this; + element.onclick = function () { + self_1._onClick(); + }; + element.style.pointerEvents = "all"; + element.style.cursor = "pointer"; + } + this.InnerUpdate(element); + for (var i in this) { + var child = this[i]; + if (child instanceof UIElement) { + child.Update(); + } + else if (child instanceof Array) { + for (var _i = 0, child_1 = child; _i < child_1.length; _i++) { + var ch = child_1[_i]; + if (ch instanceof UIElement) { + ch.Update(); + } + } + } + } + }; + UIElement.prototype.HideOnEmpty = function (hide) { + this._hideIfEmpty = hide; + this.Update(); + return this; + }; + // Called after the HTML has been replaced. Can be used for css tricks + UIElement.prototype.InnerUpdate = function (htmlElement) { }; + UIElement.prototype.Render = function () { + return "<span class='uielement' id='" + this.id + "'>" + this.InnerRender() + "</span>"; + }; + UIElement.prototype.AttachTo = function (divId) { + var element = document.getElementById(divId); + if (element === null) { + console.log("SEVERE: could not attach UIElement to ", divId); + return; + } + element.innerHTML = this.Render(); + this.Update(); + return this; + }; + UIElement.prototype.Activate = function () { + for (var i in this) { + var child = this[i]; + if (child instanceof UIElement) { + child.Activate(); + } + else if (child instanceof Array) { + for (var _i = 0, child_2 = child; _i < child_2.length; _i++) { + var ch = child_2[_i]; + if (ch instanceof UIElement) { + ch.Activate(); + } + } + } + } + }; + ; + UIElement.prototype.IsEmpty = function () { + return this.InnerRender() === ""; + }; + UIElement.nextId = 0; + return UIElement; +}()); +exports.UIElement = UIElement; diff --git a/UI/UIEventSource.js b/UI/UIEventSource.js new file mode 100644 index 0000000..b81478c --- /dev/null +++ b/UI/UIEventSource.js @@ -0,0 +1,58 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UIEventSource = void 0; +var UIEventSource = /** @class */ (function () { + function UIEventSource(data) { + this._callbacks = []; + this.data = data; + } + UIEventSource.prototype.addCallback = function (callback) { + this._callbacks.push(callback); + return this; + }; + UIEventSource.prototype.setData = function (t) { + if (this.data === t) { + return; + } + this.data = t; + this.ping(); + }; + UIEventSource.prototype.ping = function () { + for (var _i = 0, _a = this._callbacks; _i < _a.length; _i++) { + var callback = _a[_i]; + callback(this.data); + } + }; + UIEventSource.flatten = function (source, possibleSources) { + var _a; + var sink = new UIEventSource((_a = source.data) === null || _a === void 0 ? void 0 : _a.data); + source.addCallback(function (latestData) { + sink.setData(latestData === null || latestData === void 0 ? void 0 : latestData.data); + }); + for (var _i = 0, possibleSources_1 = possibleSources; _i < possibleSources_1.length; _i++) { + var possibleSource = possibleSources_1[_i]; + possibleSource.addCallback(function () { + var _a; + sink.setData((_a = source.data) === null || _a === void 0 ? void 0 : _a.data); + }); + } + return sink; + }; + UIEventSource.prototype.map = function (f, extraSources) { + if (extraSources === void 0) { extraSources = []; } + var self = this; + var update = function () { + newSource.setData(f(self.data)); + newSource.ping(); + }; + this.addCallback(update); + for (var _i = 0, extraSources_1 = extraSources; _i < extraSources_1.length; _i++) { + var extraSource = extraSources_1[_i]; + extraSource.addCallback(update); + } + var newSource = new UIEventSource(f(this.data)); + return newSource; + }; + return UIEventSource; +}()); +exports.UIEventSource = UIEventSource; diff --git a/UI/UserBadge.js b/UI/UserBadge.js new file mode 100644 index 0000000..d6c8536 --- /dev/null +++ b/UI/UserBadge.js @@ -0,0 +1,112 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserBadge = void 0; +var UIElement_1 = require("./UIElement"); +var leaflet_1 = require("leaflet"); +var FixedUiElement_1 = require("./Base/FixedUiElement"); +var VariableUIElement_1 = require("./Base/VariableUIElement"); +/** + * Handles and updates the user badge + */ +var UserBadge = /** @class */ (function (_super) { + __extends(UserBadge, _super); + function UserBadge(userDetails, pendingChanges, basemap) { + var _this = _super.call(this, userDetails) || this; + _this._userDetails = userDetails; + _this._pendingChanges = pendingChanges; + _this._basemap = basemap; + _this._logout = new FixedUiElement_1.FixedUiElement("<img src='assets/logout.svg' class='small-userbadge-icon' alt='logout'>") + .onClick(function () { + userDetails.data.osmConnection.LogOut(); + }); + userDetails.addCallback(function () { + var profilePic = document.getElementById("profile-pic"); + if (profilePic) { + profilePic.onload = function () { + profilePic.style.opacity = "1"; + }; + } + }); + _this._homeButton = new VariableUIElement_1.VariableUiElement(userDetails.map(function (userinfo) { + if (userinfo.home) { + return "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'> "; + } + return ""; + })).onClick(function () { + var _a; + var home = (_a = userDetails.data) === null || _a === void 0 ? void 0 : _a.home; + if (home === undefined) { + return; + } + basemap.map.flyTo([home.lat, home.lon], 18); + }); + return _this; + } + UserBadge.prototype.InnerRender = function () { + var user = this._userDetails.data; + if (!user.loggedIn) { + return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>"; + } + var messageSpan = "<span id='messages'>" + + " <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'>" + + user.totalMessages + + "</a></span>"; + if (user.unreadMessages > 0) { + messageSpan = "<span id='messages' class='alert'>" + + " <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'/>" + + " " + + "" + + user.unreadMessages.toString() + + "</a></span>"; + } + var dryrun = ""; + if (user.dryRun) { + dryrun = " <span class='alert'>TESTING</span>"; + } + if (user.home !== undefined) { + var icon = leaflet_1.default.icon({ + iconUrl: 'assets/home.svg', + iconSize: [20, 20], + iconAnchor: [10, 10] + }); + leaflet_1.default.marker([user.home.lat, user.home.lon], { icon: icon }).addTo(this._basemap.map); + } + var settings = "<a href='https://www.openstreetmap.org/user/" + encodeURIComponent(user.name) + "/account' target='_blank'>" + + "<img class='small-userbadge-icon' src='./assets/gear.svg' alt='settings'>" + + "</a> "; + return "<a href='https://www.openstreetmap.org/user/" + encodeURIComponent(user.name) + "' target='_blank'>" + + "<img id='profile-pic' src='" + user.img + "' alt='profile-pic'/> " + + "</a>" + + "<div id='usertext'>" + + "<p id='username'>" + + "<a href='https://www.openstreetmap.org/user/" + user.name + "' target='_blank'>" + user.name + "</a>" + + dryrun + + "</p> " + + "<p id='userstats'>" + + this._homeButton.Render() + + settings + + messageSpan + + "<span id='csCount'> " + + " <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='small-userbadge-icon' src='./assets/star.svg' alt='star'/> " + user.csCount + + "</a></span> " + + this._logout.Render() + + this._pendingChanges.Render() + + "</p>" + + "</div>"; + }; + return UserBadge; +}(UIElement_1.UIElement)); +exports.UserBadge = UserBadge; diff --git a/index.js b/index.js new file mode 100644 index 0000000..8bab00e --- /dev/null +++ b/index.js @@ -0,0 +1,187 @@ +"use strict"; +var _a; +Object.defineProperty(exports, "__esModule", { value: true }); +var OsmConnection_1 = require("./Logic/OsmConnection"); +var Changes_1 = require("./Logic/Changes"); +var ElementStorage_1 = require("./Logic/ElementStorage"); +var UIEventSource_1 = require("./UI/UIEventSource"); +var UserBadge_1 = require("./UI/UserBadge"); +var Basemap_1 = require("./Logic/Basemap"); +var PendingChanges_1 = require("./UI/PendingChanges"); +var CenterMessageBox_1 = require("./UI/CenterMessageBox"); +var Helpers_1 = require("./Helpers"); +var TagsFilter_1 = require("./Logic/TagsFilter"); +var LayerUpdater_1 = require("./Logic/LayerUpdater"); +var MessageBoxHandler_1 = require("./UI/MessageBoxHandler"); +var FeatureInfoBox_1 = require("./UI/FeatureInfoBox"); +var GeoLocationHandler_1 = require("./Logic/GeoLocationHandler"); +var StrayClickHandler_1 = require("./Logic/StrayClickHandler"); +var SimpleAddUI_1 = require("./UI/SimpleAddUI"); +var VariableUIElement_1 = require("./UI/Base/VariableUIElement"); +var SearchAndGo_1 = require("./UI/SearchAndGo"); +var CollapseButton_1 = require("./UI/Base/CollapseButton"); +var AllKnownLayouts_1 = require("./Customizations/AllKnownLayouts"); +// --------------------- Read the URL parameters ----------------- +// @ts-ignore +if (location.href.startsWith("http://buurtnatuur.be")) { + // Reload the https version. This is important for the 'locate me' button + window.location.replace("https://buurtnatuur.be"); +} +var dryRun = false; +if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { + // Set to true if testing and changes should NOT be saved + dryRun = true; + // If you have a testfile somewhere, enable this to spoof overpass + // This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules + //Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson"; +} +// ----------------- SELECT THE RIGHT QUESTSET ----------------- +var defaultLayout = "buurtnatuur"; +// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default +for (var k in AllKnownLayouts_1.AllKnownLayouts.allSets) { + var layout = AllKnownLayouts_1.AllKnownLayouts.allSets[k]; + var possibleParts = (_a = layout.locationContains) !== null && _a !== void 0 ? _a : []; + for (var _i = 0, possibleParts_1 = possibleParts; _i < possibleParts_1.length; _i++) { + var locationMatch = possibleParts_1[_i]; + if (locationMatch === "") { + continue; + } + if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) { + defaultLayout = layout.name; + } + } +} +// Read the query string to grap settings +var paramDict = {}; +if (window.location.search) { + var params = window.location.search.substr(1).split("&"); + for (var _b = 0, params_1 = params; _b < params_1.length; _b++) { + var param = params_1[_b]; + var kv = param.split("="); + paramDict[kv[0]] = kv[1]; + } +} +if (paramDict.layout) { + defaultLayout = paramDict.layout; +} +if (paramDict.test) { + dryRun = paramDict.test === "true"; +} +var layoutToUse = AllKnownLayouts_1.AllKnownLayouts.allSets[defaultLayout]; +console.log("Using layout: ", layoutToUse.name); +document.title = layoutToUse.title; +// ----------------- Setup a few event sources ------------- +// The message that should be shown at the center of the screen +var centerMessage = new UIEventSource_1.UIEventSource(""); +// The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource +var secondsTillChangesAreSaved = new UIEventSource_1.UIEventSource(0); +var leftMessage = new UIEventSource_1.UIEventSource(undefined); +var selectedElement = new UIEventSource_1.UIEventSource(undefined); +var locationControl = new UIEventSource_1.UIEventSource({ + zoom: layoutToUse.startzoom, + lat: layoutToUse.startLat, + lon: layoutToUse.startLon +}); +// ----------------- Prepare the important objects ----------------- +var saveTimeout = 30000; // After this many milliseconds without changes, saves are sent of to OSM +var allElements = new ElementStorage_1.ElementStorage(); +var osmConnection = new OsmConnection_1.OsmConnection(dryRun); +var changes = new Changes_1.Changes("Beantwoorden van vragen met #MapComplete voor vragenset #" + layoutToUse.name, osmConnection, allElements); +var bm = new Basemap_1.Basemap("leafletDiv", locationControl, new VariableUIElement_1.VariableUiElement(locationControl.map(function (location) { + var mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " + + " " + + "<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug' class='small-userbadge-icon'></a>"; + var editHere = ""; + if (location !== undefined) { + editHere = " | " + + "<a href='https://www.openstreetmap.org/edit?editor=id#map=" + location.zoom + "/" + location.lat + "/" + location.lon + "' target='_blank'>" + + "<img src='./assets/pencil.svg' alt='edit here' class='small-userbadge-icon'>" + + "</a>"; + } + return mapComplete + editHere; +}))); +// ------------- Setup the layers ------------------------------- +var controls = {}; +var addButtons = []; +var flayers = []; +var minZoom = 0; +var _loop_1 = function (layer) { + var generateInfo = function (tagsES) { + return new FeatureInfoBox_1.FeatureInfoBox(tagsES, layer.title, layer.elementsToShow, changes, osmConnection.userDetails); + }; + minZoom = Math.max(minZoom, layer.minzoom); + var flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); + controls[layer.name] = flayer.isDisplayed; + var addButton = { + name: layer.name, + icon: layer.icon, + tags: layer.newElementTags, + layerToAddTo: flayer + }; + addButtons.push(addButton); + flayers.push(flayer); +}; +for (var _c = 0, _d = layoutToUse.layers; _c < _d.length; _c++) { + var layer = _d[_c]; + _loop_1(layer); +} +var layerUpdater = new LayerUpdater_1.LayerUpdater(bm, minZoom, flayers); +// ------------------ Setup various UI elements ------------ +new StrayClickHandler_1.StrayClickHandler(bm, selectedElement, leftMessage, function () { + return new SimpleAddUI_1.SimpleAddUI(bm.Location, bm.LastClickLocation, changes, selectedElement, layerUpdater.runningQuery, osmConnection.userDetails, addButtons); +}); +/** + * Show the questions and information for the selected element on the leftMessage + */ +selectedElement.addCallback(function (data) { + var _loop_2 = function (layer) { + var applicable = layer.overpassFilter.matches(TagsFilter_1.TagUtils.proprtiesToKV(data)); + if (applicable) { + // This layer is the layer that gives the questions + leftMessage.setData(function () { + return new FeatureInfoBox_1.FeatureInfoBox(allElements.getElement(data.id), layer.title, layer.elementsToShow, changes, osmConnection.userDetails); + }); + return "break"; + } + }; + // Which is the applicable set? + for (var _i = 0, _a = layoutToUse.layers; _i < _a.length; _i++) { + var layer = _a[_i]; + var state_1 = _loop_2(layer); + if (state_1 === "break") + break; + } +}); +var pendingChanges = new PendingChanges_1.PendingChanges(changes, secondsTillChangesAreSaved); +new UserBadge_1.UserBadge(osmConnection.userDetails, pendingChanges, bm) + .AttachTo('userbadge'); +new SearchAndGo_1.SearchAndGo(bm).AttachTo("searchbox"); +new CollapseButton_1.CollapseButton("messagesbox") + .AttachTo("collapseButton"); +var welcomeMessage = function () { + return new VariableUIElement_1.VariableUiElement(osmConnection.userDetails.map(function (userdetails) { + var login = layoutToUse.gettingStartedPlzLogin; + if (userdetails.loggedIn) { + login = layoutToUse.welcomeBackMessage; + } + return "<div id='welcomeMessage'>" + + layoutToUse.welcomeMessage + login + layoutToUse.welcomeTail + + "</div>"; + }), function () { + osmConnection.registerActivateOsmAUthenticationClass(); + }); +}; +leftMessage.setData(welcomeMessage); +welcomeMessage().AttachTo("messagesbox"); +var messageBox = new MessageBoxHandler_1.MessageBoxHandler(leftMessage, function () { + selectedElement.setData(undefined); +}); +new CenterMessageBox_1.CenterMessageBox(minZoom, centerMessage, osmConnection, locationControl, layerUpdater.runningQuery) + .AttachTo("centermessage"); +Helpers_1.Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved, saveTimeout); +Helpers_1.Helpers.LastEffortSave(changes); +osmConnection.registerActivateOsmAUthenticationClass(); +new GeoLocationHandler_1.GeoLocationHandler(bm).AttachTo("geolocate-button"); +// --------------- Send a ping to start various action -------- +locationControl.ping(); +messageBox.update(); diff --git a/test.js b/test.js new file mode 100644 index 0000000..9cf2691 --- /dev/null +++ b/test.js @@ -0,0 +1,12 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var TextField_1 = require("./UI/Input/TextField"); +var FixedInputElement_1 = require("./UI/Input/FixedInputElement"); +var RadioButton_1 = require("./UI/Input/RadioButton"); +var buttons = new RadioButton_1.RadioButton([new FixedInputElement_1.FixedInputElement("Five", 5), + new FixedInputElement_1.FixedInputElement("Ten", 10), new TextField_1.TextField({ + fromString: function (str) { return parseInt(str); }, + toString: function (i) { return ("" + i); }, + }) +], false).AttachTo("maindiv"); +buttons.GetValue().addCallback(console.log); From 7d250889812f8a570a41c9487b7a32f5ba4f494c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 16:16:22 +0200 Subject: [PATCH 06/29] Add new icons --- Customizations/Layers/BikeParkings.ts | 3 +- Customizations/Layers/BikeStations.ts | 4 +- Customizations/Layers/DrinkingWater.ts | 5 +- Customizations/Layouts/Cyclofix.ts | 4 +- assets/bike/parking.svg | 83 ++------------------------ assets/bike/pump_broken.svg | 29 --------- 6 files changed, 14 insertions(+), 114 deletions(-) delete mode 100644 assets/bike/pump_broken.svg diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index ccd737e..cd5b344 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -36,7 +36,8 @@ export default class BikeParkings extends LayerDefinition { color: "#00bb00", icon: L.icon({ iconUrl: self.icon, - iconSize: [50, 50] + iconSize: [50, 50], + iconAnchor: [25,50] }) }; }; diff --git a/Customizations/Layers/BikeStations.ts b/Customizations/Layers/BikeStations.ts index 89f79b3..995ce64 100644 --- a/Customizations/Layers/BikeStations.ts +++ b/Customizations/Layers/BikeStations.ts @@ -80,7 +80,9 @@ export default class BikeStations extends LayerDefinition { color: "#00bb00", icon: L.icon({ iconUrl: iconUrl, - iconSize: [50, 50] + iconSize: [50, 50], + iconAnchor: [25,50] + }) }; }; diff --git a/Customizations/Layers/DrinkingWater.ts b/Customizations/Layers/DrinkingWater.ts index 92e6338..7808b24 100644 --- a/Customizations/Layers/DrinkingWater.ts +++ b/Customizations/Layers/DrinkingWater.ts @@ -11,7 +11,7 @@ export class DrinkingWater extends LayerDefinition { constructor() { super(); this.name = "drinking_water"; - this.icon = "./assets/bug.svg"; + this.icon = "./assets/bike/drinking_water.svg"; this.overpassFilter = new Or([ new And([ @@ -52,7 +52,8 @@ export class DrinkingWater extends LayerDefinition { color: "#00bb00", icon: new L.icon({ iconUrl: self.icon, - iconSize: [40, 40] + iconSize: [50, 50], + iconAnchor: [25,50] }) }; }; diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 548981b..b64cf18 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -2,7 +2,7 @@ import {Layout} from "../Layout"; import BikeParkings from "../Layers/BikeParkings"; import BikeServices from "../Layers/BikeStations"; import {GhostBike} from "../Layers/GhostBike"; -import {DrinkingWater, DrinkingWaterLayer} from "../Layers/DrinkingWater"; +import {DrinkingWater} from "../Layers/DrinkingWater"; export default class Cyclofix extends Layout { @@ -10,7 +10,7 @@ export default class Cyclofix extends Layout { super( "pomp", "Cyclofix bicycle infrastructure", - [new GhostBike(), new BikeServices(), new BikeParkings(), new DrinkingWater()], + [ new BikeServices(), new BikeParkings(), new DrinkingWater()], 16, 50.8465573, 4.3516970, diff --git a/assets/bike/parking.svg b/assets/bike/parking.svg index b095bc1..e5b95cd 100644 --- a/assets/bike/parking.svg +++ b/assets/bike/parking.svg @@ -1,86 +1,11 @@ <svg width="97" height="123" viewBox="0 0 97 123" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" fill="#5675DF"/> -<ellipse cx="48.5" cy="47.5" rx="48.5" ry="47.5" fill="#5675DF"/> +<path d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" fill="#1647F8"/> +<ellipse cx="48.5" cy="47.5" rx="48.5" ry="47.5" fill="#1647F8"/> <g filter="url(#filter0_d)"> -<circle cx="39" cy="66" r="2" stroke="white" stroke-width="2"/> +<path d="M42.2812 53.1875V71H36.2812V25.5H53.0625C58.0417 25.5 61.9375 26.7708 64.75 29.3125C67.5833 31.8542 69 35.2188 69 39.4062C69 43.8229 67.6146 47.2292 64.8438 49.625C62.0938 52 58.1458 53.1875 53 53.1875H42.2812ZM42.2812 48.2812H53.0625C56.2708 48.2812 58.7292 47.5312 60.4375 46.0312C62.1458 44.5104 63 42.3229 63 39.4688C63 36.7604 62.1458 34.5938 60.4375 32.9688C58.7292 31.3438 56.3854 30.5 53.4062 30.4375H42.2812V48.2812Z" fill="white"/> </g> -<g filter="url(#filter1_d)"> -<path d="M37.375 67H25L31.75 51.4H38.5H43.375H48.625M48.625 51.4L46.75 47H49.375H52M48.625 51.4L49.375 53.4L50.875 56.6L55 67M48.625 51.4L45.0625 57.4L42.925 61M41.5 63.4L42.925 61M34.375 55.8L38.125 64.6L30.625 47L32.875 51.8M40.375 65.4L42.925 61" stroke="white" stroke-width="2"/> -</g> -<g filter="url(#filter2_d)"> -<circle cx="23" cy="67" r="9" stroke="white" stroke-width="2"/> -</g> -<g filter="url(#filter3_d)"> -<circle cx="55" cy="67" r="9" stroke="white" stroke-width="2"/> -</g> -<path d="M61 77V59.0476V55.9524L62.4814 54.4851C64.4301 52.5549 67.5699 52.5549 69.5186 54.4851L71 55.9524V58.4286V77" stroke="white" stroke-width="2"/> -<line x1="66" y1="53" x2="66" y2="16" stroke="white" stroke-width="2"/> -<g filter="url(#filter4_d)"> -<circle cx="66" cy="23" r="13" fill="white"/> -</g> -<g filter="url(#filter5_d)"> -<circle cx="66" cy="23" r="11" fill="#496DEB"/> -</g> -<g filter="url(#filter6_d)"> -<path d="M64.1729 24.9902V30H62.4854V17.2031H67.2051C68.6055 17.2031 69.7012 17.5605 70.4922 18.2754C71.2891 18.9902 71.6875 19.9365 71.6875 21.1143C71.6875 22.3564 71.2979 23.3145 70.5186 23.9883C69.7451 24.6562 68.6348 24.9902 67.1875 24.9902H64.1729ZM64.1729 23.6104H67.2051C68.1074 23.6104 68.7988 23.3994 69.2793 22.9775C69.7598 22.5498 70 21.9346 70 21.1318C70 20.3701 69.7598 19.7607 69.2793 19.3037C68.7988 18.8467 68.1396 18.6094 67.3018 18.5918H64.1729V23.6104Z" fill="white"/> -</g> -<line x1="43" y1="78" x2="72" y2="78" stroke="white" stroke-width="2"/> <defs> -<filter id="filter0_d" x="32" y="63" width="14" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter1_d" x="19.4777" y="46" width="40.4518" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter2_d" x="9" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter3_d" x="41" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter4_d" x="49" y="10" width="34" height="34" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter5_d" x="51" y="12" width="30" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<filter id="filter6_d" x="58.4854" y="17.2031" width="17.2021" height="20.7969" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<filter id="filter0_d" x="32.2812" y="25.5" width="40.7188" height="53.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feFlood flood-opacity="0" result="BackgroundImageFix"/> <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> <feOffset dy="4"/> diff --git a/assets/bike/pump_broken.svg b/assets/bike/pump_broken.svg deleted file mode 100644 index 2dcc14c..0000000 --- a/assets/bike/pump_broken.svg +++ /dev/null @@ -1,29 +0,0 @@ -<svg width="104" height="123" viewBox="0 0 104 123" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M60.0445 113.094C58.2614 116.981 52.7386 116.981 50.9555 113.094L20.2124 46.085C18.6928 42.7729 21.1129 39 24.7569 39L86.2431 39C89.8871 39 92.3072 42.7729 90.7876 46.085L60.0445 113.094Z" fill="#171615"/> -<circle cx="55" cy="49" r="49" fill="#171615"/> -<g clip-path="url(#clip0)" filter="url(#filter0_d)"> -<path d="M59.2519 79.0738C60.5075 79.0738 61.7117 78.5751 62.5995 77.6872C63.4874 76.7994 63.9862 76.2998 63.9862 75.0442C63.9862 73.7886 63.4874 73.3516 62.5996 72.4637C61.7117 71.5759 60.5075 71.0771 59.2519 71.0771L53.3555 71.0815V69.4784L54.8823 69.4739C56.1376 69.4729 57.3412 68.9738 58.2288 68.0862C59.1164 67.1986 60.2656 66.2038 60.2666 64.9485L60.3118 33.5699C60.3119 33.2223 60.3118 32.5699 60.401 32.2506C60.5341 31.9295 60.547 31.5699 60.725 31.3919C60.9708 31.1462 61.1038 30.9511 61.4249 30.8181C61.746 30.6852 62.6524 30.5698 63 30.5699C63.3475 30.5699 63.9907 30.5669 64.3118 30.7C64.6329 30.833 64.9889 31.2478 65.3118 31.5699C65.5575 31.8156 65.5753 31.9295 65.7083 32.2506C65.8412 32.5717 65.9096 32.9158 65.9096 33.2634L65.9169 59.1411L64.8659 59.1411C64.5926 59.1418 64.3306 59.2497 64.136 59.4415C63.9414 59.6334 63.8298 59.8938 63.8251 60.167L63.7587 64.9175C63.7491 65.609 63.8771 66.2955 64.1351 66.9371C64.393 67.5787 64.776 68.1626 65.2616 68.6549C65.7472 69.1473 66.3258 69.5382 66.9638 69.805C67.6017 70.0718 68.2864 70.2091 68.9779 70.2091L72.1237 70.2091C72.4005 70.209 72.666 70.0991 72.8617 69.9033C73.0574 69.7076 73.1674 69.4422 73.1674 69.1654L73.1674 64.7367C73.1674 64.5979 73.1396 64.4605 73.0857 64.3326C73.0319 64.2046 72.9531 64.0887 72.8539 63.9916C72.7547 63.8944 72.6372 63.8181 72.5081 63.7669C72.3791 63.7158 72.2411 63.6909 72.1023 63.6938L70.1412 63.7358L70.091 60.1693C70.0863 59.8961 69.9747 59.6356 69.7801 59.4438C69.5855 59.2519 69.3235 59.1441 69.0502 59.1433L68.3794 59.1411L68.7232 33.5699C68.7232 32.3143 68.1997 30.4577 67.3118 29.5699C66.424 28.682 65.5 28.0699 63 28.0699C60.7903 28.0699 59.6997 28.682 58.8118 29.5699C57.924 30.4577 57.5395 32.0115 57.5395 33.2671L57.5395 64.7441C57.5383 65.4457 57.2591 66.1183 56.7629 66.6144C56.2668 67.1105 55.5943 67.3898 54.8926 67.391L53.3555 67.391V35.2151C53.3555 34.9383 53.2455 34.6729 53.0498 34.4771C52.854 34.2814 52.5886 34.1715 52.3118 34.1714L51.4973 34.1714L51.4973 25.1871L60.7872 25.1871C61.064 25.187 61.3294 25.0771 61.5252 24.8814C61.7209 24.6856 61.8309 24.4202 61.8309 24.1434V18.9766C61.8308 18.6998 61.7209 18.4344 61.5252 18.2386C61.3294 18.0429 61.064 17.933 60.7872 17.9329L35.6913 17.9329C35.4145 17.9329 35.1491 18.0429 34.9534 18.2386C34.7577 18.4344 34.6477 18.6998 34.6476 18.9766L34.6476 24.1434C34.6477 24.4202 34.7577 24.6856 34.9534 24.8814C35.1491 25.0771 35.4145 25.187 35.6913 25.1871L44.9812 25.1871L44.9812 34.1714L44.1543 34.1714C43.8775 34.1715 43.6121 34.2814 43.4164 34.4771C43.2206 34.6729 43.1107 34.9383 43.1106 35.2151L43.1106 71.0771L37.2266 71.0771C35.971 71.0771 34.7668 71.5759 33.879 72.4637C32.9911 73.3516 32.4923 73.7886 32.4923 75.0442C32.4923 76.2998 32.9911 76.7994 33.879 77.6872C34.7668 78.575 35.971 79.0738 37.2266 79.0738L59.2519 79.0738ZM68.0612 64.8135C68.0631 64.951 68.0922 65.0869 68.1468 65.2131C68.2014 65.3394 68.2805 65.4536 68.3794 65.5493C68.4783 65.6449 68.5952 65.72 68.7232 65.7703C68.8512 65.8206 68.988 65.8451 69.1255 65.8424L71.0749 65.8033L71.0771 68.1246L68.975 68.1246C68.5599 68.1247 68.149 68.0423 67.7661 67.8822C67.3832 67.722 67.0359 67.4874 66.7445 67.1918C66.4531 66.8963 66.2234 66.5458 66.0686 66.1607C65.9139 65.7756 65.8372 65.3635 65.8431 64.9485L65.8926 61.2248H68.0154L68.0612 64.8135ZM36.735 23.0997L36.735 20.0203L59.7435 20.0203L59.7435 23.0997L50.4536 23.0997L46.0249 23.0997L36.735 23.0997ZM47.0686 25.1871L49.4099 25.1871L49.4099 34.1714L47.0686 34.1714V25.1871ZM44.8543 36.2588H46.0249L50.4536 36.2588L51.6243 36.2588L51.6243 67.3865L44.8543 67.3865L44.8543 36.2588ZM44.8543 69.4739L51.6243 69.4739L51.6243 74.0296L44.8543 74.0296L44.8543 69.4739ZM34.5797 75.0442C34.5929 74.3464 34.876 74.4479 35.3696 73.9543C35.8631 73.4608 36.5287 73.1777 37.2266 73.1645L43.1106 73.1645L43.1106 74.3061C43.1107 74.5829 43.2206 74.8483 43.4163 75.0441C43.6121 75.2398 43.8775 75.3498 44.1543 75.3498L52.3118 75.3542C52.5886 75.3542 52.854 75.2442 53.0498 75.0485C53.2455 74.8528 53.3555 74.5873 53.3555 74.3105V73.1689L59.2519 73.1645C59.5999 73.163 59.9448 73.2305 60.2666 73.363C60.5884 73.4955 60.8808 73.6904 61.1268 73.9365C61.3729 74.1825 61.5678 73.7078 61.7003 74.0296C61.8328 74.3514 61.9003 74.6962 61.8988 75.0442C61.8856 75.7421 61.6025 75.7031 61.1089 76.1966C60.6154 76.6902 59.9498 76.9733 59.2519 76.9864L37.2266 76.9864C36.8786 76.9879 36.5337 76.9205 36.2119 76.788C35.8901 76.6555 35.5978 76.4606 35.3517 76.2145C35.1056 75.9684 34.9107 75.676 34.7782 75.3542C34.6457 75.0324 34.5783 75.3922 34.5797 75.0442Z" fill="white"/> -<rect x="44.3118" y="35.5698" width="8" height="39" fill="white"/> -<rect x="35.3118" y="19.5699" width="25" height="4" fill="white"/> -<rect x="46.3118" y="21.5698" width="4" height="14" fill="white"/> -<rect x="34.3118" y="72.5698" width="28" height="5" fill="white"/> -<rect x="65.3118" y="60.5698" width="4" height="8" fill="white"/> -<rect x="68.3118" y="64.5699" width="3" height="4" fill="white"/> -<rect x="66" y="72.2385" width="21.8167" height="3.68967" transform="rotate(-44.3049 66 72.2385)" fill="#F00D0D"/> -</g> -<rect x="68.4303" y="56.8712" width="22.1163" height="3.52552" transform="rotate(43.5782 68.4303 56.8712)" fill="#F00D0D"/> -<defs> -<filter id="filter0_d" x="-3" y="2" width="102.479" height="102.479" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> -</filter> -<clipPath id="clip0"> -<rect width="66.8064" height="66.8064" fill="white" transform="translate(1 49.2393) rotate(-45)"/> -</clipPath> -</defs> -</svg> From 360c653553c441b2885a2ac370580e74f8407d60 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 16:32:35 +0200 Subject: [PATCH 07/29] New icons --- Customizations/Layers/BikeParkings.ts | 2 +- Customizations/Layers/BikeShop.ts | 33 +++++++ Customizations/Layers/BikeStations.ts | 1 - Customizations/Layers/DrinkingWater.ts | 2 +- Customizations/Layouts/Cyclofix.ts | 3 +- .../Questions/bike/StationPumpTools.ts | 2 +- assets/bike/broken_pump.svg | 30 ++++++ assets/bike/drinking_water.svg | 18 ++++ assets/bike/parking_old.svg | 93 +++++++++++++++++++ 9 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 Customizations/Layers/BikeShop.ts create mode 100644 assets/bike/broken_pump.svg create mode 100644 assets/bike/drinking_water.svg create mode 100644 assets/bike/parking_old.svg diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index cd5b344..2c8a731 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -10,7 +10,7 @@ import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWi export default class BikeParkings extends LayerDefinition { constructor() { super(); - this.name = "bike_parking"; + this.name = "bike parking"; this.icon = "./assets/bike/parking.svg"; this.overpassFilter = new Tag("amenity", "bicycle_parking"); this.newElementTags = [ diff --git a/Customizations/Layers/BikeShop.ts b/Customizations/Layers/BikeShop.ts new file mode 100644 index 0000000..8c68f24 --- /dev/null +++ b/Customizations/Layers/BikeShop.ts @@ -0,0 +1,33 @@ +import {TagRenderingOptions} from "../TagRendering"; +import {LayerDefinition} from "../LayerDefinition"; +import {Tag} from "../../Logic/TagsFilter"; + +export class BikeShop extends LayerDefinition{ + + constructor() { + super( + { + name: "bike shop or repair", + icon: "assets/bike/repair_shop.svg", + minzoom: 14, + overpassFilter: new Tag("shop","bicycle"), + newElementTags: [new Tag("shop","bicycle")] + } + ); + + this.title = new TagRenderingOptions({ + mappings:[ + {k:new Tag("service:bicycle:retail","yes"), txt: "Bicycle shop"}, + {k:new Tag("service:bicycle:retail","no"), txt: "Bicycle repair"}, + {k:new Tag("service:bicycle:retail",""), txt: "Bicycle repair/shop"}, + ] + }) + + + this.style() + + + } + + +} \ No newline at end of file diff --git a/Customizations/Layers/BikeStations.ts b/Customizations/Layers/BikeStations.ts index 995ce64..258aad9 100644 --- a/Customizations/Layers/BikeStations.ts +++ b/Customizations/Layers/BikeStations.ts @@ -82,7 +82,6 @@ export default class BikeStations extends LayerDefinition { iconUrl: iconUrl, iconSize: [50, 50], iconAnchor: [25,50] - }) }; }; diff --git a/Customizations/Layers/DrinkingWater.ts b/Customizations/Layers/DrinkingWater.ts index 7808b24..7bdd632 100644 --- a/Customizations/Layers/DrinkingWater.ts +++ b/Customizations/Layers/DrinkingWater.ts @@ -10,7 +10,7 @@ export class DrinkingWater extends LayerDefinition { constructor() { super(); - this.name = "drinking_water"; + this.name = "drinking water"; this.icon = "./assets/bike/drinking_water.svg"; this.overpassFilter = new Or([ diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index b64cf18..dfc35d0 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -3,6 +3,7 @@ import BikeParkings from "../Layers/BikeParkings"; import BikeServices from "../Layers/BikeStations"; import {GhostBike} from "../Layers/GhostBike"; import {DrinkingWater} from "../Layers/DrinkingWater"; +import {BikeShop} from "../Layers/BikeShop"; export default class Cyclofix extends Layout { @@ -10,7 +11,7 @@ export default class Cyclofix extends Layout { super( "pomp", "Cyclofix bicycle infrastructure", - [ new BikeServices(), new BikeParkings(), new DrinkingWater()], + [new BikeServices(), new BikeShop(), new DrinkingWater(), new BikeParkings()], 16, 50.8465573, 4.3516970, diff --git a/Customizations/Questions/bike/StationPumpTools.ts b/Customizations/Questions/bike/StationPumpTools.ts index be81c8b..532f24c 100644 --- a/Customizations/Questions/bike/StationPumpTools.ts +++ b/Customizations/Questions/bike/StationPumpTools.ts @@ -6,7 +6,7 @@ export default class BikeStationPumpTools extends TagRenderingOptions { constructor() { super({ priority: 15, - question: "Which services are available at this bike station?", + question: "Which services are available here?", mappings: [ {k: new And([new Tag("service:bicycle:tools", "no"), new Tag("service:bicycle:pump", "yes")]), txt: "There is only a pump available."}, {k: new And([new Tag("service:bicycle:tools", "yes"), new Tag("service:bicycle:pump", "no")]), txt: "There are only tools (screwdrivers, pliers...) available."}, diff --git a/assets/bike/broken_pump.svg b/assets/bike/broken_pump.svg new file mode 100644 index 0000000..38868a5 --- /dev/null +++ b/assets/bike/broken_pump.svg @@ -0,0 +1,30 @@ +<svg width="104" height="123" viewBox="0 0 104 123" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M60.0445 113.094C58.2614 116.981 52.7386 116.981 50.9555 113.094L20.2124 46.085C18.6928 42.7729 21.1129 39 24.7569 39L86.2431 39C89.8871 39 92.3072 42.7729 90.7876 46.085L60.0445 113.094Z" fill="#171615"/> +<circle cx="55" cy="49" r="49" fill="#171615"/> +<g clip-path="url(#clip0)" filter="url(#filter0_d)"> +<path d="M59.2519 79.0738C60.5075 79.0738 61.7117 78.5751 62.5995 77.6872C63.4874 76.7994 63.9862 76.2998 63.9862 75.0442C63.9862 73.7886 63.4874 73.3516 62.5996 72.4637C61.7117 71.5759 60.5075 71.0771 59.2519 71.0771L53.3555 71.0815V69.4784L54.8823 69.4739C56.1376 69.4729 57.3412 68.9738 58.2288 68.0862C59.1164 67.1986 60.2656 66.2038 60.2666 64.9485L60.3118 33.5699C60.3119 33.2223 60.3118 32.5699 60.401 32.2506C60.5341 31.9295 60.547 31.5699 60.725 31.3919C60.9708 31.1462 61.1038 30.9511 61.4249 30.8181C61.746 30.6852 62.6524 30.5698 63 30.5699C63.3475 30.5699 63.9907 30.5669 64.3118 30.7C64.6329 30.833 64.9889 31.2478 65.3118 31.5699C65.5575 31.8156 65.5753 31.9295 65.7083 32.2506C65.8412 32.5717 65.9096 32.9158 65.9096 33.2634L65.9169 59.1411L64.8659 59.1411C64.5926 59.1418 64.3306 59.2497 64.136 59.4415C63.9414 59.6334 63.8298 59.8938 63.8251 60.167L63.7587 64.9175C63.7491 65.609 63.8771 66.2955 64.1351 66.9371C64.393 67.5787 64.776 68.1626 65.2616 68.6549C65.7472 69.1473 66.3258 69.5382 66.9638 69.805C67.6017 70.0718 68.2864 70.2091 68.9779 70.2091L72.1237 70.2091C72.4005 70.209 72.666 70.0991 72.8617 69.9033C73.0574 69.7076 73.1674 69.4422 73.1674 69.1654L73.1674 64.7367C73.1674 64.5979 73.1396 64.4605 73.0857 64.3326C73.0319 64.2046 72.9531 64.0887 72.8539 63.9916C72.7547 63.8944 72.6372 63.8181 72.5081 63.7669C72.3791 63.7158 72.2411 63.6909 72.1023 63.6938L70.1412 63.7358L70.091 60.1693C70.0863 59.8961 69.9747 59.6356 69.7801 59.4438C69.5855 59.2519 69.3235 59.1441 69.0502 59.1433L68.3794 59.1411L68.7232 33.5699C68.7232 32.3143 68.1997 30.4577 67.3118 29.5699C66.424 28.682 65.5 28.0699 63 28.0699C60.7903 28.0699 59.6997 28.682 58.8118 29.5699C57.924 30.4577 57.5395 32.0115 57.5395 33.2671L57.5395 64.7441C57.5383 65.4457 57.2591 66.1183 56.7629 66.6144C56.2668 67.1105 55.5943 67.3898 54.8926 67.391L53.3555 67.391V35.2151C53.3555 34.9383 53.2455 34.6729 53.0498 34.4771C52.854 34.2814 52.5886 34.1715 52.3118 34.1714L51.4973 34.1714L51.4973 25.1871L60.7872 25.1871C61.064 25.187 61.3294 25.0771 61.5252 24.8814C61.7209 24.6856 61.8309 24.4202 61.8309 24.1434V18.9766C61.8308 18.6998 61.7209 18.4344 61.5252 18.2386C61.3294 18.0429 61.064 17.933 60.7872 17.9329L35.6913 17.9329C35.4145 17.9329 35.1491 18.0429 34.9534 18.2386C34.7577 18.4344 34.6477 18.6998 34.6476 18.9766L34.6476 24.1434C34.6477 24.4202 34.7577 24.6856 34.9534 24.8814C35.1491 25.0771 35.4145 25.187 35.6913 25.1871L44.9812 25.1871L44.9812 34.1714L44.1543 34.1714C43.8775 34.1715 43.6121 34.2814 43.4164 34.4771C43.2206 34.6729 43.1107 34.9383 43.1106 35.2151L43.1106 71.0771L37.2266 71.0771C35.971 71.0771 34.7668 71.5759 33.879 72.4637C32.9911 73.3516 32.4923 73.7886 32.4923 75.0442C32.4923 76.2998 32.9911 76.7994 33.879 77.6872C34.7668 78.575 35.971 79.0738 37.2266 79.0738L59.2519 79.0738ZM68.0612 64.8135C68.0631 64.951 68.0922 65.0869 68.1468 65.2131C68.2014 65.3394 68.2805 65.4536 68.3794 65.5493C68.4783 65.6449 68.5952 65.72 68.7232 65.7703C68.8512 65.8206 68.988 65.8451 69.1255 65.8424L71.0749 65.8033L71.0771 68.1246L68.975 68.1246C68.5599 68.1247 68.149 68.0423 67.7661 67.8822C67.3832 67.722 67.0359 67.4874 66.7445 67.1918C66.4531 66.8963 66.2234 66.5458 66.0686 66.1607C65.9139 65.7756 65.8372 65.3635 65.8431 64.9485L65.8926 61.2248H68.0154L68.0612 64.8135ZM36.735 23.0997L36.735 20.0203L59.7435 20.0203L59.7435 23.0997L50.4536 23.0997L46.0249 23.0997L36.735 23.0997ZM47.0686 25.1871L49.4099 25.1871L49.4099 34.1714L47.0686 34.1714V25.1871ZM44.8543 36.2588H46.0249L50.4536 36.2588L51.6243 36.2588L51.6243 67.3865L44.8543 67.3865L44.8543 36.2588ZM44.8543 69.4739L51.6243 69.4739L51.6243 74.0296L44.8543 74.0296L44.8543 69.4739ZM34.5797 75.0442C34.5929 74.3464 34.876 74.4479 35.3696 73.9543C35.8631 73.4608 36.5287 73.1777 37.2266 73.1645L43.1106 73.1645L43.1106 74.3061C43.1107 74.5829 43.2206 74.8483 43.4163 75.0441C43.6121 75.2398 43.8775 75.3498 44.1543 75.3498L52.3118 75.3542C52.5886 75.3542 52.854 75.2442 53.0498 75.0485C53.2455 74.8528 53.3555 74.5873 53.3555 74.3105V73.1689L59.2519 73.1645C59.5999 73.163 59.9448 73.2305 60.2666 73.363C60.5884 73.4955 60.8808 73.6904 61.1268 73.9365C61.3729 74.1825 61.5678 73.7078 61.7003 74.0296C61.8328 74.3514 61.9003 74.6962 61.8988 75.0442C61.8856 75.7421 61.6025 75.7031 61.1089 76.1966C60.6154 76.6902 59.9498 76.9733 59.2519 76.9864L37.2266 76.9864C36.8786 76.9879 36.5337 76.9205 36.2119 76.788C35.8901 76.6555 35.5978 76.4606 35.3517 76.2145C35.1056 75.9684 34.9107 75.676 34.7782 75.3542C34.6457 75.0324 34.5783 75.3922 34.5797 75.0442Z" fill="white"/> +<rect x="44.3118" y="35.5698" width="8" height="39" fill="white"/> +<rect x="35.3118" y="19.5699" width="25" height="4" fill="white"/> +<rect x="46.3118" y="21.5698" width="4" height="14" fill="white"/> +<rect x="34.3118" y="72.5698" width="28" height="5" fill="white"/> +<rect x="65.3118" y="60.5698" width="4" height="8" fill="white"/> +<rect x="68.3118" y="64.5699" width="3" height="4" fill="white"/> +<rect x="56" y="42" width="5" height="10" fill="#171615"/> +</g> +<rect x="72.4167" y="53.8615" width="29.0549" height="7.85787" transform="rotate(43.5782 72.4167 53.8615)" fill="#F00D0D"/> +<path d="M68.2141 73.9097L88.6128 54L94.0003 59.5199L73.6016 79.4295L68.2141 73.9097Z" fill="#F00D0D"/> +<defs> +<filter id="filter0_d" x="-3" y="2" width="102.479" height="102.479" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<clipPath id="clip0"> +<rect width="66.8064" height="66.8064" fill="white" transform="translate(1 49.2393) rotate(-45)"/> +</clipPath> +</defs> +</svg> diff --git a/assets/bike/drinking_water.svg b/assets/bike/drinking_water.svg new file mode 100644 index 0000000..4962afe --- /dev/null +++ b/assets/bike/drinking_water.svg @@ -0,0 +1,18 @@ +<svg width="98" height="124" viewBox="0 0 98 124" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M55.0445 114.094C53.2614 117.981 47.7386 117.981 45.9555 114.094L15.2124 47.085C13.6928 43.7729 16.1129 40 19.7569 40L81.2431 40C84.8871 40 87.3072 43.7729 85.7876 47.085L55.0445 114.094Z" fill="#6BC4F7"/> +<circle cx="49" cy="49" r="49" fill="#6BC4F7"/> +<g filter="url(#filter0_d)"> +<path d="M79 41.8705C79 39.3976 77.4079 35.6726 74.1327 30.4823C71.8064 26.7956 69.5171 23.8005 69.4208 23.6747C69.0964 23.2513 68.5726 23 68.0144 23C67.4562 23 66.9322 23.2514 66.6078 23.6748C66.3787 23.9738 61.828 29.939 59.0942 35.3688C54.8849 28.8794 50.9494 23.7307 50.9067 23.6748C50.5821 23.2513 50.0582 23 49.5 23C48.9418 23 48.4179 23.2513 48.0934 23.6747C48.0468 23.7357 43.367 29.8581 38.753 37.1701C38.2587 37.9536 37.7856 38.7192 37.3295 39.4706C34.9483 35.6504 32.5162 32.4661 32.3922 32.3043C32.0677 31.8809 31.5438 31.6296 30.9856 31.6296C30.4274 31.6296 29.9036 31.8809 29.5791 32.3043C29.4828 32.43 27.1935 35.4252 24.8672 39.1119C21.5921 44.3021 20 48.0271 20 50.5C20 55.6193 24.0504 59.8732 29.3205 60.6234C30.1402 70.3329 38.8775 78 49.5 78C60.6619 78 69.7429 69.5347 69.7429 59.1295C69.7429 57.2539 69.2077 54.9089 68.1449 52.1097C74.1423 52.0443 79 47.4766 79 41.8705V41.8705ZM23.457 50.5C23.457 49.457 24.0257 46.8053 27.8344 40.7656C28.9755 38.9562 30.126 37.2947 30.9856 36.0929C32.1576 37.7318 33.8718 40.2278 35.3741 42.8008C31.9223 48.9002 29.9183 53.7824 29.3954 57.3607C26.0053 56.6784 23.457 53.8613 23.457 50.5V50.5ZM49.5 74.7773C40.2443 74.7773 32.7142 67.7578 32.7142 59.1295C32.7142 56.3792 34.2766 50.6239 41.7201 38.8241C44.7375 34.0407 47.784 29.7745 49.5 27.4354C51.213 29.7702 54.2519 34.0258 57.2673 38.8042C64.7212 50.6167 66.2857 56.3773 66.2857 59.1296C66.2858 67.7578 58.7557 74.7773 49.5 74.7773V74.7773ZM68.0144 48.8887C67.5836 48.8887 67.156 48.8549 66.7362 48.7879C65.3713 45.8533 63.5372 42.506 61.2717 38.8163C62.8077 35.0043 66.1199 30.1147 68.0149 27.4638C68.8707 28.6601 70.0148 30.3125 71.153 32.1162C74.9727 38.1694 75.543 40.8257 75.543 41.8705C75.543 45.7402 72.1656 48.8887 68.0144 48.8887V48.8887Z" fill="white"/> +</g> +<defs> +<filter id="filter0_d" x="16" y="23" width="67" height="63" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +</defs> +</svg> diff --git a/assets/bike/parking_old.svg b/assets/bike/parking_old.svg new file mode 100644 index 0000000..b095bc1 --- /dev/null +++ b/assets/bike/parking_old.svg @@ -0,0 +1,93 @@ +<svg width="97" height="123" viewBox="0 0 97 123" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M52.1412 111.419C50.4633 115.605 44.5366 115.605 42.8588 111.419L24.7014 66.1099C23.385 62.8252 25.8039 59.25 29.3426 59.25L65.6574 59.25C69.1962 59.25 71.615 62.8252 70.2986 66.11L52.1412 111.419Z" fill="#5675DF"/> +<ellipse cx="48.5" cy="47.5" rx="48.5" ry="47.5" fill="#5675DF"/> +<g filter="url(#filter0_d)"> +<circle cx="39" cy="66" r="2" stroke="white" stroke-width="2"/> +</g> +<g filter="url(#filter1_d)"> +<path d="M37.375 67H25L31.75 51.4H38.5H43.375H48.625M48.625 51.4L46.75 47H49.375H52M48.625 51.4L49.375 53.4L50.875 56.6L55 67M48.625 51.4L45.0625 57.4L42.925 61M41.5 63.4L42.925 61M34.375 55.8L38.125 64.6L30.625 47L32.875 51.8M40.375 65.4L42.925 61" stroke="white" stroke-width="2"/> +</g> +<g filter="url(#filter2_d)"> +<circle cx="23" cy="67" r="9" stroke="white" stroke-width="2"/> +</g> +<g filter="url(#filter3_d)"> +<circle cx="55" cy="67" r="9" stroke="white" stroke-width="2"/> +</g> +<path d="M61 77V59.0476V55.9524L62.4814 54.4851C64.4301 52.5549 67.5699 52.5549 69.5186 54.4851L71 55.9524V58.4286V77" stroke="white" stroke-width="2"/> +<line x1="66" y1="53" x2="66" y2="16" stroke="white" stroke-width="2"/> +<g filter="url(#filter4_d)"> +<circle cx="66" cy="23" r="13" fill="white"/> +</g> +<g filter="url(#filter5_d)"> +<circle cx="66" cy="23" r="11" fill="#496DEB"/> +</g> +<g filter="url(#filter6_d)"> +<path d="M64.1729 24.9902V30H62.4854V17.2031H67.2051C68.6055 17.2031 69.7012 17.5605 70.4922 18.2754C71.2891 18.9902 71.6875 19.9365 71.6875 21.1143C71.6875 22.3564 71.2979 23.3145 70.5186 23.9883C69.7451 24.6562 68.6348 24.9902 67.1875 24.9902H64.1729ZM64.1729 23.6104H67.2051C68.1074 23.6104 68.7988 23.3994 69.2793 22.9775C69.7598 22.5498 70 21.9346 70 21.1318C70 20.3701 69.7598 19.7607 69.2793 19.3037C68.7988 18.8467 68.1396 18.6094 67.3018 18.5918H64.1729V23.6104Z" fill="white"/> +</g> +<line x1="43" y1="78" x2="72" y2="78" stroke="white" stroke-width="2"/> +<defs> +<filter id="filter0_d" x="32" y="63" width="14" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter1_d" x="19.4777" y="46" width="40.4518" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter2_d" x="9" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter3_d" x="41" y="57" width="28" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter4_d" x="49" y="10" width="34" height="34" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter5_d" x="51" y="12" width="30" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +<filter id="filter6_d" x="58.4854" y="17.2031" width="17.2021" height="20.7969" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> +<feOffset dy="4"/> +<feGaussianBlur stdDeviation="2"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> +</filter> +</defs> +</svg> From c688c42258664bdebf0656b516dc2cd541846fe6 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 16:37:19 +0200 Subject: [PATCH 08/29] Fix bug with add buttons not always working --- Logic/StrayClickHandler.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Logic/StrayClickHandler.ts b/Logic/StrayClickHandler.ts index 85ccc9d..89acbcc 100644 --- a/Logic/StrayClickHandler.ts +++ b/Logic/StrayClickHandler.ts @@ -32,15 +32,16 @@ export class StrayClickHandler { self._lastMarker = L.marker([lastClick.lat, lastClick.lon]); const uiElement = uiToShow(); const popup = L.popup().setContent(uiElement.Render()); - uiElement.Activate(); uiElement.Update(); + uiElement.Activate(); self._lastMarker.addTo(map); self._lastMarker.bindPopup(popup).openPopup(); self._lastMarker.on("click", () => { leftMessage.setData(self._uiToShow); }); - + uiElement.Update(); + uiElement.Activate(); }); selectElement.addCallback(() => { From 51166268164860a8758eca2e4716194d88a4f749 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 16:55:16 +0200 Subject: [PATCH 09/29] Remove dutch hardcoded texts for now; add a simple bikeshop quest --- Customizations/Layers/BikeShop.ts | 81 +++++++++++++++++++++++++------ UI/CenterMessageBox.ts | 6 +-- UI/SearchAndGo.ts | 10 ++-- UI/SimpleAddUI.ts | 10 ++-- 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/Customizations/Layers/BikeShop.ts b/Customizations/Layers/BikeShop.ts index 8c68f24..714930e 100644 --- a/Customizations/Layers/BikeShop.ts +++ b/Customizations/Layers/BikeShop.ts @@ -1,32 +1,85 @@ import {TagRenderingOptions} from "../TagRendering"; import {LayerDefinition} from "../LayerDefinition"; import {Tag} from "../../Logic/TagsFilter"; +import L from "leaflet"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; +import {NameQuestion} from "../Questions/NameQuestion"; + +export class BikeShop extends LayerDefinition { + + + const + sellsBikes = new Tag("service:bicycle:retail", "yes"); -export class BikeShop extends LayerDefinition{ - constructor() { super( { name: "bike shop or repair", - icon: "assets/bike/repair_shop.svg", - minzoom: 14, - overpassFilter: new Tag("shop","bicycle"), - newElementTags: [new Tag("shop","bicycle")] + icon: "assets/bike/repair_shop.svg", + minzoom: 14, + overpassFilter: new Tag("shop", "bicycle"), + newElementTags: [new Tag("shop", "bicycle")] } ); - + this.title = new TagRenderingOptions({ - mappings:[ - {k:new Tag("service:bicycle:retail","yes"), txt: "Bicycle shop"}, - {k:new Tag("service:bicycle:retail","no"), txt: "Bicycle repair"}, - {k:new Tag("service:bicycle:retail",""), txt: "Bicycle repair/shop"}, + mappings: [ + {k: this.sellsBikes, txt: "Bicycle shop"}, + {k: new Tag("service:bicycle:retail", "no"), txt: "Bicycle repair"}, + {k: new Tag("service:bicycle:retail", ""), txt: "Bicycle repair/shop"}, ] }) - this.style() - - + this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), + new TagRenderingOptions({ + question: "What is the name of this bicycle shop?", + freeform:{ + key:"name", + renderTemplate: "The name of this bicycle shop is {name}", + template: "The name of this bicycle shop is $$$" + } + }), + + new TagRenderingOptions({ + question: "Can one buy a new bike here?", + mappings: [ + {k: this.sellsBikes, txt: "Bikes are sold here"}, + {k: new Tag("service:bicycle:retail", "no"), txt: "No bikes can be bought here"}, + ] + }), + + + + new TagRenderingOptions({ + question: "Does this shop repair bicycles?", + mappings: [ + {k: this.sellsBikes, txt: "Bikes can be repaired here"}, + {k: new Tag("service:bicycle:retail", "no"), txt: "No bikes can be bought here"}, + ] + }), + ] + + + this.style = (tags) => { + let icon = "assets/bike/repair_shop.svg"; + + if (this.sellsBikes.matchesProperties(tags)) { + icon = "assets/bike/shop.svg"; + } + + return { + color: "#ff0000", + icon: L.icon({ + iconUrl: icon, + iconSize: [50, 50], + iconAnchor: [25, 50] + }) + } + } + + } diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts index 8bbb7a0..a5cd8b6 100644 --- a/UI/CenterMessageBox.ts +++ b/UI/CenterMessageBox.ts @@ -40,11 +40,11 @@ export class CenterMessageBox extends UIElement { return this._centermessage.data; } if (this._queryRunning.data) { - return "Data wordt geladen..."; + return "Data is loading..."; } else if (this._zoomInMore.data) { - return "Zoom in om de data te zien en te bewerken"; + return "Zoom in more to see the data"; } - return "Klaar!"; + return "Done!"; } diff --git a/UI/SearchAndGo.ts b/UI/SearchAndGo.ts index 3916168..5bff841 100644 --- a/UI/SearchAndGo.ts +++ b/UI/SearchAndGo.ts @@ -8,8 +8,8 @@ import {Basemap} from "../Logic/Basemap"; export class SearchAndGo extends UIElement { - private _placeholder = new UIEventSource("Zoek naar een locatie...") - private _searchField = new TextField(this._placeholder); + private _placeholder = new UIEventSource("Search a location...") + private _searchField = new TextField(this._placeholder, undefined); private _foundEntries = new UIEventSource([]); private _map: Basemap; @@ -35,7 +35,7 @@ export class SearchAndGo extends UIElement { private RunSearch() { const searchString = this._searchField.value.data; this._searchField.Clear(); - this._placeholder.setData("Bezig met zoeken..."); + this._placeholder.setData("Searching..."); const self = this; Geocoding.Search(searchString, this._map, (result) => { @@ -50,10 +50,10 @@ export class SearchAndGo extends UIElement { [bb[1], bb[3]] ] self._map.map.fitBounds(bounds); - this._placeholder.setData("Zoek naar een locatie..."); + this._placeholder.setData("Search a location..."); }, () => { - this._placeholder.setData("Niets gevonden: er ging iets mis"); + this._placeholder.setData("Something went wrong. Try again."); }); } diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index b235437..34300d8 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -61,18 +61,18 @@ export class SimpleAddUI extends UIElement { } protected InnerRender(): string { - const header = "<h2>Geen selectie</h2>" + - "Je klikte ergens waar er nog geen gezochte data is.<br/>"; + const header = "<h2>No data here</h2>" + + "You clicked somewhere where no data is known yet.<br/>"; if (!this._userDetails.data.loggedIn) { - return header + "<a class='activate-osm-authentication'>Gelieve je aan te melden om een nieuw punt toe te voegen</a>" + return header + "<a class='activate-osm-authentication'>Please log in to add a new point</a>" } if (this._zoomlevel.data.zoom < 19) { - return header + "Zoom verder in om een element toe te voegen."; + return header + "Zoom in further to add a point."; } if (this._dataIsLoading.data) { - return header + "De data is nog aan het laden. Nog even geduld, dan kan je een punt toevoegen"; + return header + "The data is still loading. Please wait a bit before you add a new point"; } var html = ""; From a528159bb7fee99bb8f523b1d0fd53dd70e08a77 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 16:57:43 +0200 Subject: [PATCH 10/29] Removed bit of stray dutch --- UI/SimpleAddUI.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index 34300d8..1d9f3d6 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -42,7 +42,7 @@ export class SimpleAddUI extends UIElement { // <button type='button'> looks SO retarded // the default type of button is 'submit', which performs a POST and page reload const button = - new Button(new FixedUiElement("Voeg hier een " + option.name + " toe"), + new Button(new FixedUiElement("Add a " + option.name + " here"), this.CreatePoint(option)); this._addButtons.push(button); } From 81a80ef95cc4121a594bbd229b892bfb22f3b3f5 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 17:30:02 +0200 Subject: [PATCH 11/29] More translations --- Customizations/Layers/BikeParkings.ts | 2 +- Customizations/Layers/BikeShop.ts | 12 ++++- Customizations/Layers/BikeStations.ts | 6 ++- .../Questions/bike/StationOperator.ts | 2 +- Customizations/TagRendering.ts | 4 +- UI/Image/ImageCarousel.ts | 6 +-- UI/ImageUploadFlow.ts | 46 +++++++------------ UI/MessageBoxHandler.ts | 2 +- UI/SaveButton.ts | 2 +- UI/UserBadge.ts | 2 +- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index 2c8a731..e336ba8 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -23,7 +23,7 @@ export default class BikeParkings extends LayerDefinition { this.title = new FixedText("Fietsparking"); this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), - new OperatorTag(), + // new OperatorTag(), new ParkingType() ]; diff --git a/Customizations/Layers/BikeShop.ts b/Customizations/Layers/BikeShop.ts index 714930e..0056139 100644 --- a/Customizations/Layers/BikeShop.ts +++ b/Customizations/Layers/BikeShop.ts @@ -55,8 +55,16 @@ export class BikeShop extends LayerDefinition { new TagRenderingOptions({ question: "Does this shop repair bicycles?", mappings: [ - {k: this.sellsBikes, txt: "Bikes can be repaired here"}, - {k: new Tag("service:bicycle:retail", "no"), txt: "No bikes can be bought here"}, + {k: new Tag("service:bicycle:repair", "yes"), txt: "Bikes are repaired here, by the shop owner (for a fee)"}, + {k: new Tag("service:bicycle:repair", "no"), txt: "Bikes are not repaired here"}, + ] + }), + + new TagRenderingOptions({ + question: "Are there tools here so that one can repair their own bike?", + mappings: [ + {k: new Tag("service:bicycle:diy", "yes"), txt: "Tools for DIY are available here"}, + {k: new Tag("service:bicycle:diy", "no"), txt: "No tools for DIY are available here"}, ] }), ] diff --git a/Customizations/Layers/BikeStations.ts b/Customizations/Layers/BikeStations.ts index 258aad9..5af8cfd 100644 --- a/Customizations/Layers/BikeStations.ts +++ b/Customizations/Layers/BikeStations.ts @@ -73,7 +73,11 @@ export default class BikeStations extends LayerDefinition { } } } else { - iconName = "repair_station.svg" + if (!self.pump.matchesProperties(properties)) { + iconName = "repair_station.svg" + } else { + iconName = "repair_station.svg" + } } const iconUrl = `./assets/bike/${iconName}` return { diff --git a/Customizations/Questions/bike/StationOperator.ts b/Customizations/Questions/bike/StationOperator.ts index 001f920..56b00eb 100644 --- a/Customizations/Questions/bike/StationOperator.ts +++ b/Customizations/Questions/bike/StationOperator.ts @@ -18,7 +18,7 @@ export default class BikeStationOperator extends TagRenderingOptions { {k: new Tag("operator", "Stad Halle"), txt: "Stad Halle"}, {k: new Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis"}, {k: new Tag("operator", "Jette"), txt: "Jette"}, - {k: new Tag("operator", "private"), txt: "Beheer door een privépersoon"} + {k: new Tag("operator", "private"), txt: "Operated by a private individual"} ] }); } diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index ea8ca32..5003ab1 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -325,9 +325,9 @@ class TagRendering extends UIElement implements TagDependantUIElement { const cancelContents = this._editMode.map((isEditing) => { if (isEditing) { - return "<span class='skip-button'>Annuleren</span>"; + return "<span class='skip-button'>Cancel</span>"; } else { - return "<span class='skip-button'>Overslaan (Ik weet het niet zeker...)</span>"; + return "<span class='skip-button'>Skip this question</span>"; } }); // And at last, set up the skip button diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 9d4e1fd..b3f40d8 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -23,10 +23,10 @@ export class ImageCarouselConstructor implements TagDependantUIElementConstructo return 0; } - construct(tags: UIEventSource<any>, changes: Changes): TagDependantUIElement { - return new ImageCarousel(tags, changes); + construct(dependencies: { tags: UIEventSource<any>, changes: Changes }): TagDependantUIElement { + return new ImageCarousel(dependencies.tags, dependencies.changes); } - + } export class ImageCarousel extends TagDependantUIElement { diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 6542824..6c3d569 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -9,7 +9,6 @@ import {VariableUiElement} from "./Base/VariableUIElement"; export class ImageUploadFlow extends UIElement { private _licensePicker: UIElement; private _selectedLicence: UIEventSource<string>; - private _licenseExplanation: UIElement; private _isUploading: UIEventSource<number> = new UIEventSource<number>(0) private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) }; private _userdetails: UIEventSource<UserDetails>; @@ -31,12 +30,12 @@ export class ImageUploadFlow extends UIElement { this._uploadOptions = uploadOptions; this.ListenTo(this._isUploading); - const licensePicker = new DropDownUI("Jouw foto wordt gepubliceerd ", + const licensePicker = new DropDownUI("The picture is published ", [ - {value: "CC0", shown: "in het publiek domein"}, - {value: "CC-BY-SA 4.0", shown: "onder een CC-BY-SA-licentie"}, - {value: "CC-BY 4.0", shown: "onder een CC-BY-licentie"} + {value: "CC0", shown: "in the public domain"}, + {value: "CC-BY-SA 4.0", shown: "with a CC-BY-SA license"}, + {value: "CC-BY 4.0", shown: "with a CC-BY license"} ], preferedLicense ); @@ -44,48 +43,37 @@ export class ImageUploadFlow extends UIElement { this._selectedLicence = licensePicker.selectedElement; - const licenseExplanations = { - "CC-BY-SA 4.0": - "<b>Creative Commonse met naamsvermelding en gelijk delen</b><br/>" + - "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden én ze afgeleide werken met deze licentie en attributie delen.", - "CC-BY 4.0": - "<b>Creative Commonse met naamsvermelding</b> <br/>" + - "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden", - "CC0": - "<b>Geen copyright</b><br/> Je foto mag door iedereen voor alles gebruikt worden" - } - this._licenseExplanation = new VariableUiElement( - this._selectedLicence.map((license) => { - return licenseExplanations[license] - }) - ); } protected InnerRender(): string { if (!this._userdetails.data.loggedIn) { - return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden</div>"; + return "<div class='activate-osm-authentication'>Please log in to add a picture</div>"; } + + let uploadingMessage = ""; if (this._isUploading.data == 1) { - return "<b>Bezig met een foto te uploaden...</b>" + uploadingMessage = "<b>Uploading a picture...</b>" } if (this._isUploading.data > 0) { - return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>" + uploadingMessage = "<b>Uploading multiple pictures, " + this._isUploading.data + " left...</b>" } return "" + "<div class='imageflow'>" + - + "<label for='fileselector-" + this.id + "'>" + - + "<div class='imageflow-file-input-wrapper'>" + "<img src='./assets/camera-plus.svg' alt='upload image'/> " + - "<span class='imageflow-add-picture'>Voeg foto toe</span>" + - "<div class='break'></div>"+ + "<span class='imageflow-add-picture'>Add a picture</span>" + + "<div class='break'></div>" + "</div>" + - this._licensePicker.Render() + - + + this._licensePicker.Render() + "<br/>" + + uploadingMessage + + "</label>" + "<input id='fileselector-" + this.id + "' " + diff --git a/UI/MessageBoxHandler.ts b/UI/MessageBoxHandler.ts index 5ce7bdb..af4d086 100644 --- a/UI/MessageBoxHandler.ts +++ b/UI/MessageBoxHandler.ts @@ -22,7 +22,7 @@ export class MessageBoxHandler { } } - new VariableUiElement(new UIEventSource<string>("<h2>Naar de kaart</h2>"), + new VariableUiElement(new UIEventSource<string>("<h2>Return to the map</h2>"), () => { document.getElementById("to-the-map").onclick = function () { uielement.setData(undefined); diff --git a/UI/SaveButton.ts b/UI/SaveButton.ts index 3bba390..f78fbd4 100644 --- a/UI/SaveButton.ts +++ b/UI/SaveButton.ts @@ -19,7 +19,7 @@ export class SaveButton extends UIElement { ) { return "<span class='save-non-active'>Opslaan</span>" } - return "<span class='save'>Opslaan</span>"; + return "<span class='save'>Save</span>"; } } \ No newline at end of file diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index a3912e0..c810acf 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -60,7 +60,7 @@ export class UserBadge extends UIElement { protected InnerRender(): string { const user = this._userDetails.data; if (!user.loggedIn) { - return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>"; + return "<div class='activate-osm-authentication'>Login with OpenStreetMap</div>"; } From 96c03dc8e3243f852ecd7e3202a2f081df870a57 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 17:37:16 +0200 Subject: [PATCH 12/29] Add more quests for bike shop --- Customizations/Layers/BikeShop.ts | 38 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Customizations/Layers/BikeShop.ts b/Customizations/Layers/BikeShop.ts index 0056139..3e4413a 100644 --- a/Customizations/Layers/BikeShop.ts +++ b/Customizations/Layers/BikeShop.ts @@ -8,8 +8,8 @@ import {NameQuestion} from "../Questions/NameQuestion"; export class BikeShop extends LayerDefinition { - const - sellsBikes = new Tag("service:bicycle:retail", "yes"); + private readonly sellsBikes = new Tag("service:bicycle:retail", "yes"); + private readonly repairsBikes = new Tag("service:bicycle:repair", "yes"); constructor() { super( @@ -35,31 +35,49 @@ export class BikeShop extends LayerDefinition { new ImageCarouselWithUploadConstructor(), new TagRenderingOptions({ question: "What is the name of this bicycle shop?", - freeform:{ - key:"name", + freeform: { + key: "name", renderTemplate: "The name of this bicycle shop is {name}", template: "The name of this bicycle shop is $$$" } }), new TagRenderingOptions({ - question: "Can one buy a new bike here?", + question: "Can one buy a bike here?", mappings: [ {k: this.sellsBikes, txt: "Bikes are sold here"}, - {k: new Tag("service:bicycle:retail", "no"), txt: "No bikes can be bought here"}, + {k: new Tag("service:bicycle:retail", "no"), txt: "No bikes are sold here"}, ] }), - - - + + new TagRenderingOptions({ + question: "Can one buy a new bike here?", + mappings: [ + {k: new Tag("service:bicycle:second_hand", "yes"), txt: "Second-hand bikes are sold here"}, + {k: new Tag("service:bicycle:second_hand", "only"), txt: "All bicycles sold here are second-hand"}, + {k: new Tag("service:bicycle:second_hand", "no"), txt: "Only brand new bikes are sold here"}, + ] + }).OnlyShowIf(this.sellsBikes), + + new TagRenderingOptions({ question: "Does this shop repair bicycles?", mappings: [ - {k: new Tag("service:bicycle:repair", "yes"), txt: "Bikes are repaired here, by the shop owner (for a fee)"}, + {k: this.repairsBikes, txt: "Bikes are repaired here, by the shop owner (for a fee)"}, + {k: new Tag("service:bicycle:repair", "only_sold"), txt: "Only bikes that were bought here, are repaired"}, + {k: new Tag("service:bicycle:repair", "brand"), txt: "Only bikes of a fixed brand are repaired here"}, {k: new Tag("service:bicycle:repair", "no"), txt: "Bikes are not repaired here"}, ] }), + new TagRenderingOptions({ + question: "Can one hire a new bike here?", + mappings: [ + {k: new Tag("service:bicycle:rental", "yes"), txt: "Bikes can be rented here"}, + {k: new Tag("service:bicycle:rental", "no"), txt: "Bikes cannot be rented here"}, + ] + }).OnlyShowIf(this.sellsBikes), + new TagRenderingOptions({ question: "Are there tools here so that one can repair their own bike?", mappings: [ From 7bec750ae200b7a8dbccababf7989a90b60ab166 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 17:40:25 +0200 Subject: [PATCH 13/29] Update bike parking to english --- Customizations/Layers/BikeParkings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index e336ba8..84c293e 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -20,7 +20,7 @@ export default class BikeParkings extends LayerDefinition { this.minzoom = 13; this.style = this.generateStyleFunction(); - this.title = new FixedText("Fietsparking"); + this.title = new FixedText("Bicycle parking"); this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), // new OperatorTag(), From 7b80e945bbe185931068a72f42136fbd7222800a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 18:24:00 +0200 Subject: [PATCH 14/29] Intermediate refactoring --- Customizations/LayerDefinition.ts | 2 -- Customizations/Layers/Bookcases.ts | 4 +--- Customizations/TagRendering.ts | 20 +++++++++++++++++++- Logic/Imgur.ts | 1 - Logic/LayerUpdater.ts | 4 +--- UI/Base/Button.ts | 2 +- UI/Base/FixedUiElement.ts | 2 +- UI/Base/VariableUIElement.ts | 10 +--------- UI/Input/InputElementWrapper.ts | 2 +- UI/Input/RadioButton.ts | 6 ++++-- UI/Input/TextField.ts | 17 ++++++++++------- UI/MessageBoxHandler.ts | 5 ----- UI/SaveButton.ts | 2 +- UI/SimpleAddUI.ts | 6 +----- UI/UIElement.ts | 2 +- UI/UserBadge.ts | 2 +- 16 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index 1065f43..138102c 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -88,7 +88,6 @@ export class LayerDefinition { } } = undefined) { if (options === undefined) { - console.log("No options!") return; } this.name = options.name; @@ -100,7 +99,6 @@ export class LayerDefinition { this.title = options.title; this.elementsToShow = options.elementsToShow; this.style = options.style; - console.log(this) } asLayer(basemap: Basemap, allElements: ElementStorage, changes: Changes, userDetails: UIEventSource<UserDetails>, selectedElement: UIEventSource<any>, diff --git a/Customizations/Layers/Bookcases.ts b/Customizations/Layers/Bookcases.ts index 25953f4..16f78d9 100644 --- a/Customizations/Layers/Bookcases.ts +++ b/Customizations/Layers/Bookcases.ts @@ -1,10 +1,8 @@ import {LayerDefinition} from "../LayerDefinition"; import L from "leaflet"; -import {And, Or, Regex, Tag} from "../../Logic/TagsFilter"; -import {QuestionDefinition} from "../../Logic/Question"; +import {And, Or, Tag} from "../../Logic/TagsFilter"; import {TagRenderingOptions} from "../TagRendering"; import {NameInline} from "../Questions/NameInline"; -import {NameQuestion} from "../Questions/NameQuestion"; import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; export class Bookcases extends LayerDefinition { diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 2b9c8bf..2be71e0 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -294,7 +294,19 @@ class TagRendering extends UIElement implements TagDependantUIElement { const elements = []; if (options.mappings !== undefined) { + + const previousTexts= []; for (const mapping of options.mappings) { + console.log(mapping); + if(mapping.k === null){ + continue; + } + if(previousTexts.indexOf(mapping.txt) >= 0){ + continue; + } + previousTexts.push(mapping.txt); + + console.log("PUshed") elements.push(this.InputElementForMapping(mapping)); } } @@ -350,6 +362,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { } else if (tag instanceof Tag) { return tag.value } + console.log("Could not decode tag to string", tag) return undefined; } @@ -447,7 +460,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { } - protected InnerRender(): string { + InnerRender(): string { if (this.IsQuestioning() || this._editMode.data) { // Not yet known or questioning, we have to ask a question @@ -492,5 +505,10 @@ class TagRendering extends UIElement implements TagDependantUIElement { return TagUtils.ApplyTemplate(template, tags); } + + InnerUpdate(htmlElement: HTMLElement) { + super.InnerUpdate(htmlElement); + this._questionElement.Update(); // Another manual update for them + } } \ No newline at end of file diff --git a/Logic/Imgur.ts b/Logic/Imgur.ts index bfba380..b41929f 100644 --- a/Logic/Imgur.ts +++ b/Logic/Imgur.ts @@ -60,7 +60,6 @@ export class Imgur { } - console.log(data); const licenseInfo = new LicenseInfo(); licenseInfo.licenseShortName = data.license; diff --git a/Logic/LayerUpdater.ts b/Logic/LayerUpdater.ts index 408d756..5478e89 100644 --- a/Logic/LayerUpdater.ts +++ b/Logic/LayerUpdater.ts @@ -57,8 +57,7 @@ export class LayerUpdater { } private handleFail(reason: any) { - console.log("QUERY FAILED", reason); - console.log("Retrying in 1s") + console.log("QUERY FAILED (retrying in 1 sec)", reason); this.previousBounds = undefined; const self = this; window.setTimeout( @@ -73,7 +72,6 @@ export class LayerUpdater { } console.log("Zoom level: ",this._map.map.getZoom(), "Least needed zoom:", this._minzoom) if (this._map.map.getZoom() < this._minzoom || this._map.Location.data.zoom < this._minzoom) { - console.log("Not running query: zoom not sufficient"); return; } diff --git a/UI/Base/Button.ts b/UI/Base/Button.ts index 3026729..817de11 100644 --- a/UI/Base/Button.ts +++ b/UI/Base/Button.ts @@ -18,7 +18,7 @@ export class Button extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { return "<form>" + "<button id='button-"+this.id+"' type='button' "+this._clss+">" + this._text.Render() + "</button>" + diff --git a/UI/Base/FixedUiElement.ts b/UI/Base/FixedUiElement.ts index 6680b89..0579092 100644 --- a/UI/Base/FixedUiElement.ts +++ b/UI/Base/FixedUiElement.ts @@ -8,7 +8,7 @@ export class FixedUiElement extends UIElement { this._html = html ?? ""; } - protected InnerRender(): string { + InnerRender(): string { return this._html; } diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index b1f7f05..8afab13 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -12,16 +12,8 @@ export class VariableUiElement extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { return this._html.data; } - InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); - if(this._innerUpdate !== undefined){ - this._innerUpdate(htmlElement); - } - } - - } \ No newline at end of file diff --git a/UI/Input/InputElementWrapper.ts b/UI/Input/InputElementWrapper.ts index e04a4f1..979a712 100644 --- a/UI/Input/InputElementWrapper.ts +++ b/UI/Input/InputElementWrapper.ts @@ -26,7 +26,7 @@ export class InputElementWrapper<T> extends InputElement<T>{ return this.input.GetValue(); } - protected InnerRender(): string { + InnerRender(): string { return this.pre.Render() + this.input.Render() + this.post.Render(); } diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index b463633..647fda1 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -62,7 +62,7 @@ export class RadioButton<T> extends InputElement<T> { return 'radio-' + this.id + '-' + i; } - protected InnerRender(): string { + InnerRender(): string { let body = ""; let i = 0; @@ -83,14 +83,16 @@ export class RadioButton<T> extends InputElement<T> { if (t === undefined) { return; } + console.log("Trying to find an option for", t) // We check that what is selected matches the previous rendering for (let i = 0; i < this._elements.length; i++) { const e = this._elements[i]; if (e.IsValid(t)) { this._selectedElementIndex.setData(i); e.GetValue().setData(t); + const radio = document.getElementById(this.IdFor(i)); // @ts-ignore - document.getElementById(this.IdFor(i)).checked = true; + radio?.checked = true; return; } diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 32ffb1c..090dc45 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -18,8 +18,8 @@ export class TextField<T> extends InputElement<T> { constructor(options: { placeholder?: string | UIElement, - toString?: (t: T) => string, - fromString?: (string: string) => T, + toString: (t: T) => string, + fromString: (string: string) => T, value?: UIEventSource<T> }) { super(undefined); @@ -33,15 +33,18 @@ export class TextField<T> extends InputElement<T> { this.mappedValue.addCallback((t) => this.value.setData(options.toString(t))); - this._placeholder = - typeof(options.placeholder) === "string" ? new FixedUiElement(options.placeholder) : - (options.placeholder ?? new FixedUiElement("")); + options.placeholder = options.placeholder ?? ""; + if (options.placeholder instanceof UIElement) { + this._placeholder = options.placeholder + } else { + this._placeholder = new FixedUiElement(options.placeholder); + } this._toString = options.toString ?? ((t) => ("" + t)); const self = this; this.mappedValue.addCallback((t) => { - if (t === undefined && t === null) { + if (t === undefined || t === null) { return; } const field = document.getElementById('text-' + this.id); @@ -57,7 +60,7 @@ export class TextField<T> extends InputElement<T> { return this.mappedValue; } - protected InnerRender(): string { + InnerRender(): string { return "<form onSubmit='return false' class='form-text-field'>" + "<input type='text' placeholder='" + this._placeholder.InnerRender() + "' id='text-" + this.id + "'>" + "</form>"; diff --git a/UI/MessageBoxHandler.ts b/UI/MessageBoxHandler.ts index 5ce7bdb..2cdb96e 100644 --- a/UI/MessageBoxHandler.ts +++ b/UI/MessageBoxHandler.ts @@ -45,7 +45,6 @@ export class MessageBoxHandler { update() { const wrapper = document.getElementById("messagesboxmobilewrapper"); const gen = this._uielement.data; - console.log("Generator: ", gen); if (gen === undefined) { wrapper.classList.add("hidden") if (location.hash !== "") { @@ -55,10 +54,6 @@ export class MessageBoxHandler { } location.hash = "#element" wrapper.classList.remove("hidden"); - /* gen() - ?.HideOnEmpty(true) - ?.AttachTo("messagesbox") - ?.Activate();*/ gen() ?.HideOnEmpty(true) diff --git a/UI/SaveButton.ts b/UI/SaveButton.ts index 3bba390..60d8bda 100644 --- a/UI/SaveButton.ts +++ b/UI/SaveButton.ts @@ -12,7 +12,7 @@ export class SaveButton extends UIElement { this._value = value; } - protected InnerRender(): string { + InnerRender(): string { if (this._value.data === undefined || this._value.data === null || this._value.data === "" diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index b235437..956569b 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -60,7 +60,7 @@ export class SimpleAddUI extends UIElement { } } - protected InnerRender(): string { + InnerRender(): string { const header = "<h2>Geen selectie</h2>" + "Je klikte ergens waar er nog geen gezochte data is.<br/>"; if (!this._userDetails.data.loggedIn) { @@ -83,10 +83,6 @@ export class SimpleAddUI extends UIElement { } InnerUpdate(htmlElement: HTMLElement) { - super.InnerUpdate(htmlElement); - for (const button of this._addButtons) { - button.Update(); - } this._userDetails.data.osmConnection.registerActivateOsmAUthenticationClass(); } diff --git a/UI/UIElement.ts b/UI/UIElement.ts index b4bdd69..700cc5c 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -100,7 +100,7 @@ export abstract class UIElement { return this; } - protected abstract InnerRender(): string; + public abstract InnerRender(): string; public Activate(): void { for (const i in this) { diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index c9a2bd5..403cb0b 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -58,7 +58,7 @@ export class UserBadge extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { const user = this._userDetails.data; if (!user.loggedIn) { return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>"; From b20be9e1321e3ed5d55ca1fb89816fb1912130c4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 19:53:35 +0200 Subject: [PATCH 15/29] Removed js files --- Customizations/AllKnownLayouts.js | 51 --- Customizations/LayerDefinition.js | 33 -- Customizations/Layers/Artwork.js | 87 ----- Customizations/Layers/BikeParkings.js | 58 ---- Customizations/Layers/BikeStations.js | 97 ------ Customizations/Layers/Birdhide.js | 145 -------- Customizations/Layers/Bookcases.js | 167 --------- Customizations/Layers/Bos.js | 85 ----- Customizations/Layers/DrinkingWater.js | 71 ---- Customizations/Layers/GhostBike.js | 80 ----- Customizations/Layers/GrbToFix.js | 90 ----- Customizations/Layers/InformationBoard.js | 122 ------- Customizations/Layers/Map.js | 99 ------ Customizations/Layers/NatureReserves.js | 134 -------- Customizations/Layers/Park.js | 108 ------ Customizations/Layers/Toilets.js | 94 ----- Customizations/Layers/Widths.js | 270 --------------- Customizations/Layout.js | 38 --- Customizations/Layouts/All.js | 26 -- Customizations/Layouts/Bookcases.js | 36 -- Customizations/Layouts/Cyclofix.js | 35 -- Customizations/Layouts/GRB.js | 28 -- Customizations/Layouts/Groen.js | 56 --- Customizations/Layouts/MetaMap.js | 27 -- Customizations/Layouts/Natuurpunt.js | 28 -- Customizations/Layouts/Statues.js | 31 -- Customizations/Layouts/StreetWidth.js | 37 -- Customizations/Layouts/Toilets.js | 31 -- Customizations/Layouts/WalkByBrussels.js | 33 -- Customizations/OnlyShowIf.js | 89 ----- Customizations/Questions/AccessTag.js | 50 --- .../Questions/DescriptionQuestion.js | 36 -- Customizations/Questions/FixedText.js | 30 -- Customizations/Questions/NameInline.js | 41 --- Customizations/Questions/NameQuestion.js | 48 --- Customizations/Questions/OperatorTag.js | 41 --- Customizations/Questions/OsmLink.js | 40 --- Customizations/Questions/WikipediaLink.js | 59 ---- Customizations/Questions/bike/ParkingType.js | 52 --- .../Questions/bike/PumpManometer.js | 32 -- Customizations/Questions/bike/PumpManual.js | 32 -- .../Questions/bike/PumpOperational.js | 31 -- Customizations/Questions/bike/PumpValves.js | 41 --- Customizations/Questions/bike/StationBrand.js | 44 --- Customizations/Questions/bike/StationChain.js | 32 -- .../Questions/bike/StationOperator.js | 41 --- .../Questions/bike/StationPumpTools.js | 33 -- Customizations/Questions/bike/StationStand.js | 32 -- Customizations/TagRendering.js | 321 ------------------ Customizations/UIElementConstructor.js | 25 -- Helpers.js | 57 ---- Quests.js | 40 --- UI/AddButton.js | 129 ------- UI/Base/Button.js | 49 --- UI/Base/CollapseButton.js | 57 ---- UI/Base/FixedUiElement.js | 30 -- UI/Base/VariableUIElement.js | 38 --- UI/Base/VerticalCombine.js | 45 --- UI/CenterMessageBox.js | 72 ---- UI/ConfirmDialog.js | 67 ---- UI/FeatureInfoBox.js | 89 ----- UI/Image/ImageCarousel.js | 112 ------ UI/Image/ImageCarouselWithUpload.js | 74 ---- UI/Image/ImgurImage.js | 59 ---- UI/Image/SimpleImageElement.js | 28 -- UI/Image/WikimediaImage.js | 59 ---- UI/ImageUploadFlow.js | 107 ------ UI/Img.js | 16 - UI/Input/DropDown.js | 69 ---- UI/Input/FixedInputElement.js | 39 --- UI/Input/InputElement.js | 25 -- UI/Input/InputElementWrapper.js | 39 --- UI/Input/RadioButton.js | 122 ------- UI/Input/TextField.js | 94 ----- UI/MessageBoxHandler.js | 56 --- UI/PendingChanges.js | 49 --- UI/QuestionPicker.js | 51 --- UI/SaveButton.js | 38 --- UI/SearchAndGo.js | 83 ----- UI/SimpleAddUI.js | 84 ----- UI/SlideShow.js | 92 ----- UI/UIElement.js | 115 ------- UI/UIEventSource.js | 58 ---- UI/UserBadge.js | 112 ------ index.js | 187 ---------- test.js | 12 - 86 files changed, 5800 deletions(-) delete mode 100644 Customizations/AllKnownLayouts.js delete mode 100644 Customizations/LayerDefinition.js delete mode 100644 Customizations/Layers/Artwork.js delete mode 100644 Customizations/Layers/BikeParkings.js delete mode 100644 Customizations/Layers/BikeStations.js delete mode 100644 Customizations/Layers/Birdhide.js delete mode 100644 Customizations/Layers/Bookcases.js delete mode 100644 Customizations/Layers/Bos.js delete mode 100644 Customizations/Layers/DrinkingWater.js delete mode 100644 Customizations/Layers/GhostBike.js delete mode 100644 Customizations/Layers/GrbToFix.js delete mode 100644 Customizations/Layers/InformationBoard.js delete mode 100644 Customizations/Layers/Map.js delete mode 100644 Customizations/Layers/NatureReserves.js delete mode 100644 Customizations/Layers/Park.js delete mode 100644 Customizations/Layers/Toilets.js delete mode 100644 Customizations/Layers/Widths.js delete mode 100644 Customizations/Layout.js delete mode 100644 Customizations/Layouts/All.js delete mode 100644 Customizations/Layouts/Bookcases.js delete mode 100644 Customizations/Layouts/Cyclofix.js delete mode 100644 Customizations/Layouts/GRB.js delete mode 100644 Customizations/Layouts/Groen.js delete mode 100644 Customizations/Layouts/MetaMap.js delete mode 100644 Customizations/Layouts/Natuurpunt.js delete mode 100644 Customizations/Layouts/Statues.js delete mode 100644 Customizations/Layouts/StreetWidth.js delete mode 100644 Customizations/Layouts/Toilets.js delete mode 100644 Customizations/Layouts/WalkByBrussels.js delete mode 100644 Customizations/OnlyShowIf.js delete mode 100644 Customizations/Questions/AccessTag.js delete mode 100644 Customizations/Questions/DescriptionQuestion.js delete mode 100644 Customizations/Questions/FixedText.js delete mode 100644 Customizations/Questions/NameInline.js delete mode 100644 Customizations/Questions/NameQuestion.js delete mode 100644 Customizations/Questions/OperatorTag.js delete mode 100644 Customizations/Questions/OsmLink.js delete mode 100644 Customizations/Questions/WikipediaLink.js delete mode 100644 Customizations/Questions/bike/ParkingType.js delete mode 100644 Customizations/Questions/bike/PumpManometer.js delete mode 100644 Customizations/Questions/bike/PumpManual.js delete mode 100644 Customizations/Questions/bike/PumpOperational.js delete mode 100644 Customizations/Questions/bike/PumpValves.js delete mode 100644 Customizations/Questions/bike/StationBrand.js delete mode 100644 Customizations/Questions/bike/StationChain.js delete mode 100644 Customizations/Questions/bike/StationOperator.js delete mode 100644 Customizations/Questions/bike/StationPumpTools.js delete mode 100644 Customizations/Questions/bike/StationStand.js delete mode 100644 Customizations/TagRendering.js delete mode 100644 Customizations/UIElementConstructor.js delete mode 100644 Helpers.js delete mode 100644 Quests.js delete mode 100644 UI/AddButton.js delete mode 100644 UI/Base/Button.js delete mode 100644 UI/Base/CollapseButton.js delete mode 100644 UI/Base/FixedUiElement.js delete mode 100644 UI/Base/VariableUIElement.js delete mode 100644 UI/Base/VerticalCombine.js delete mode 100644 UI/CenterMessageBox.js delete mode 100644 UI/ConfirmDialog.js delete mode 100644 UI/FeatureInfoBox.js delete mode 100644 UI/Image/ImageCarousel.js delete mode 100644 UI/Image/ImageCarouselWithUpload.js delete mode 100644 UI/Image/ImgurImage.js delete mode 100644 UI/Image/SimpleImageElement.js delete mode 100644 UI/Image/WikimediaImage.js delete mode 100644 UI/ImageUploadFlow.js delete mode 100644 UI/Img.js delete mode 100644 UI/Input/DropDown.js delete mode 100644 UI/Input/FixedInputElement.js delete mode 100644 UI/Input/InputElement.js delete mode 100644 UI/Input/InputElementWrapper.js delete mode 100644 UI/Input/RadioButton.js delete mode 100644 UI/Input/TextField.js delete mode 100644 UI/MessageBoxHandler.js delete mode 100644 UI/PendingChanges.js delete mode 100644 UI/QuestionPicker.js delete mode 100644 UI/SaveButton.js delete mode 100644 UI/SearchAndGo.js delete mode 100644 UI/SimpleAddUI.js delete mode 100644 UI/SlideShow.js delete mode 100644 UI/UIElement.js delete mode 100644 UI/UIEventSource.js delete mode 100644 UI/UserBadge.js delete mode 100644 index.js delete mode 100644 test.js diff --git a/Customizations/AllKnownLayouts.js b/Customizations/AllKnownLayouts.js deleted file mode 100644 index b1d47be..0000000 --- a/Customizations/AllKnownLayouts.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AllKnownLayouts = void 0; -var Groen_1 = require("./Layouts/Groen"); -var GRB_1 = require("./Layouts/GRB"); -var Bookcases_1 = require("./Layouts/Bookcases"); -var Cyclofix_1 = require("./Layouts/Cyclofix"); -var WalkByBrussels_1 = require("./Layouts/WalkByBrussels"); -var All_1 = require("./Layouts/All"); -var MetaMap_1 = require("./Layouts/MetaMap"); -var StreetWidth_1 = require("./Layouts/StreetWidth"); -var Natuurpunt_1 = require("./Layouts/Natuurpunt"); -var AllKnownLayouts = /** @class */ (function () { - function AllKnownLayouts() { - } - AllKnownLayouts.AllLayouts = function () { - var all = new All_1.All(); - var layouts = [ - new Groen_1.Groen(), - new GRB_1.GRB(), - new Cyclofix_1.default(), - new Bookcases_1.Bookcases(), - new WalkByBrussels_1.WalkByBrussels(), - new MetaMap_1.MetaMap(), - new StreetWidth_1.StreetWidth(), - new Natuurpunt_1.Natuurpunt(), - all - /*new Toilets(), - new Statues(), - */ - ]; - var allSets = {}; - for (var _i = 0, layouts_1 = layouts; _i < layouts_1.length; _i++) { - var layout = layouts_1[_i]; - allSets[layout.name] = layout; - all.layers = all.layers.concat(layout.layers); - } - return allSets; - }; - AllKnownLayouts.GetSets = function (layoutNames) { - var all = new All_1.All(); - for (var _i = 0, layoutNames_1 = layoutNames; _i < layoutNames_1.length; _i++) { - var name_1 = layoutNames_1[_i]; - all.layers = all.layers.concat(AllKnownLayouts.allSets[name_1].layers); - } - return all; - }; - AllKnownLayouts.allSets = AllKnownLayouts.AllLayouts(); - return AllKnownLayouts; -}()); -exports.AllKnownLayouts = AllKnownLayouts; diff --git a/Customizations/LayerDefinition.js b/Customizations/LayerDefinition.js deleted file mode 100644 index d646849..0000000 --- a/Customizations/LayerDefinition.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LayerDefinition = void 0; -var FilteredLayer_1 = require("../Logic/FilteredLayer"); -var LayerDefinition = /** @class */ (function () { - function LayerDefinition(options) { - if (options === void 0) { options = undefined; } - var _a; - /** - * If an object of the next layer is contained for this many percent in this feature, it is eaten and not shown - */ - this.maxAllowedOverlapPercentage = undefined; - if (options === undefined) { - console.log("No options!"); - return; - } - this.name = options.name; - this.maxAllowedOverlapPercentage = (_a = options.maxAllowedOverlapPercentage) !== null && _a !== void 0 ? _a : 0; - this.newElementTags = options.newElementTags; - this.icon = options.icon; - this.minzoom = options.minzoom; - this.overpassFilter = options.overpassFilter; - this.title = options.title; - this.elementsToShow = options.elementsToShow; - this.style = options.style; - console.log(this); - } - LayerDefinition.prototype.asLayer = function (basemap, allElements, changes, userDetails, selectedElement, showOnPopup) { - return new FilteredLayer_1.FilteredLayer(this.name, basemap, allElements, changes, this.overpassFilter, this.maxAllowedOverlapPercentage, this.style, selectedElement, showOnPopup); - }; - return LayerDefinition; -}()); -exports.LayerDefinition = LayerDefinition; diff --git a/Customizations/Layers/Artwork.js b/Customizations/Layers/Artwork.js deleted file mode 100644 index 34d7371..0000000 --- a/Customizations/Layers/Artwork.js +++ /dev/null @@ -1,87 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Artwork = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var Question_1 = require("../../Logic/Question"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var leaflet_1 = require("leaflet"); -var Artwork = /** @class */ (function (_super) { - __extends(Artwork, _super); - function Artwork() { - var _this = _super.call(this) || this; - _this.name = "artwork"; - _this.newElementTags = [new TagsFilter_1.Tag("tourism", "artwork")]; - _this.icon = "./assets/statue.svg"; - _this.overpassFilter = new TagsFilter_1.Tag("tourism", "artwork"); - _this.minzoom = 13; - _this.questions = [ - Question_1.QuestionDefinition.radioAndTextQuestion("What kind of artwork is this?", 10, "artwork_type", [ - { text: "A statue", value: "statue" }, - { text: "A bust (thus a statue, but only of the head and shoulders)", value: "bust" }, - { text: "A sculpture", value: "sculpture" }, - { text: "A mural painting", value: "mural" }, - { text: "A painting", value: "painting" }, - { text: "A graffiti", value: "graffiti" }, - { text: "A relief", value: "relief" }, - { text: "An installation", value: "installation" } - ]), - Question_1.QuestionDefinition.textQuestion("Whom or what is depicted in this statue?", "subject", 20).addUnrequiredTag("subject:wikidata", "*"), - Question_1.QuestionDefinition.textQuestion("Is there an inscription on this artwork?", "inscription", 16), - Question_1.QuestionDefinition.textQuestion("What is the name of this artwork? If there is no explicit name, skip the question", "name", 15), - ]; - _this.style = function (tags) { - return { - icon: new leaflet_1.default.icon({ - iconUrl: "assets/statue.svg", - iconSize: [40, 40], - text: "hi" - }), - color: "#0000ff" - }; - }; - _this.elementsToShow = [ - new TagMappingOptions({ - key: "name", - template: "<h2>Artwork '{name}'</h2>", - missing: "Artwork" - }), - new TagMappingOptions({ - key: "artwork_type", - template: "This artwork is a {artwork_type}" - }), - new TagMappingOptions({ - key: "artist_name", - template: "This artwork was made by {artist_name}" - }), - new TagMappingOptions({ - key: "subject", - template: "This artwork depicts {subject}" - }), - new TagMappingOptions({ - key: "subject:wikidata", - template: "<a href='https://www.wikidata.org/wiki/{subject:wikidata}' target='_blank'>See more data about the subject</a>" - }), - new TagMappingOptions({ - key: "website", - template: "<a href='{website}' target='_blank'>Website of the statue</a>" - }), - new TagMappingOptions({ key: "image", template: "<img class='popupImg' alt='image' src='{image}' />" }) - ]; - return _this; - } - return Artwork; -}(LayerDefinition_1.LayerDefinition)); -exports.Artwork = Artwork; diff --git a/Customizations/Layers/BikeParkings.js b/Customizations/Layers/BikeParkings.js deleted file mode 100644 index 95c1208..0000000 --- a/Customizations/Layers/BikeParkings.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var OperatorTag_1 = require("../Questions/OperatorTag"); -var L = require("leaflet"); -var FixedText_1 = require("../Questions/FixedText"); -var ParkingType_1 = require("../Questions/bike/ParkingType"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var BikeParkings = /** @class */ (function (_super) { - __extends(BikeParkings, _super); - function BikeParkings() { - var _this = _super.call(this) || this; - _this.name = "bike_parking"; - _this.icon = "./assets/bike/parking.svg"; - _this.overpassFilter = new TagsFilter_1.Tag("amenity", "bicycle_parking"); - _this.newElementTags = [ - new TagsFilter_1.Tag("amenity", "bicycle_parking"), - ]; - _this.maxAllowedOverlapPercentage = 10; - _this.minzoom = 13; - _this.style = _this.generateStyleFunction(); - _this.title = new FixedText_1.default("Fietsparking"); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new OperatorTag_1.OperatorTag(), - new ParkingType_1.default() - ]; - return _this; - } - BikeParkings.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - return { - color: "#00bb00", - icon: L.icon({ - iconUrl: self.icon, - iconSize: [50, 50] - }) - }; - }; - }; - return BikeParkings; -}(LayerDefinition_1.LayerDefinition)); -exports.default = BikeParkings; diff --git a/Customizations/Layers/BikeStations.js b/Customizations/Layers/BikeStations.js deleted file mode 100644 index fcd7aa9..0000000 --- a/Customizations/Layers/BikeStations.js +++ /dev/null @@ -1,97 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var L = require("leaflet"); -var StationChain_1 = require("../Questions/bike/StationChain"); -var StationPumpTools_1 = require("../Questions/bike/StationPumpTools"); -var StationStand_1 = require("../Questions/bike/StationStand"); -var PumpManual_1 = require("../Questions/bike/PumpManual"); -var StationOperator_1 = require("../Questions/bike/StationOperator"); -var FixedText_1 = require("../Questions/FixedText"); -var PumpManometer_1 = require("../Questions/bike/PumpManometer"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var PumpOperational_1 = require("../Questions/bike/PumpOperational"); -var PumpValves_1 = require("../Questions/bike/PumpValves"); -var BikeStations = /** @class */ (function (_super) { - __extends(BikeStations, _super); - function BikeStations() { - var _this = _super.call(this) || this; - _this.pump = new TagsFilter_1.Tag("service:bicycle:pump", "yes"); - _this.pumpOperationalAny = new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "yes"); - _this.pumpOperationalOk = new TagsFilter_1.Or([new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "yes"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "operational"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "ok"), new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "")]); - _this.tools = new TagsFilter_1.Tag("service:bicycle:tools", "yes"); - _this.name = "bike station or pump"; - _this.icon = "./assets/wrench.svg"; - _this.overpassFilter = new TagsFilter_1.And([ - new TagsFilter_1.Tag("amenity", "bicycle_repair_station") - ]); - _this.newElementTags = [ - new TagsFilter_1.Tag("amenity", "bicycle_repair_station") - ]; - _this.maxAllowedOverlapPercentage = 10; - _this.minzoom = 13; - _this.style = _this.generateStyleFunction(); - _this.title = new FixedText_1.default("Bike station"); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new StationPumpTools_1.default(), - new StationChain_1.default().OnlyShowIf(_this.tools), - new StationStand_1.default().OnlyShowIf(_this.tools), - new PumpManual_1.default().OnlyShowIf(_this.pump), - new PumpManometer_1.default().OnlyShowIf(_this.pump), - new PumpValves_1.default().OnlyShowIf(_this.pump), - new PumpOperational_1.default().OnlyShowIf(_this.pump), - new StationOperator_1.default(), - ]; - return _this; - } - BikeStations.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - var hasPump = self.pump.matchesProperties(properties); - var isOperational = self.pumpOperationalOk.matchesProperties(properties); - var hasTools = self.tools.matchesProperties(properties); - var iconName = ""; - if (hasPump) { - if (hasTools) { - iconName = "repair_station_pump.svg"; - } - else { - if (isOperational) { - iconName = "pump.svg"; - } - else { - iconName = "pump_broken.svg"; - } - } - } - else { - iconName = "repair_station.svg"; - } - var iconUrl = "./assets/bike/" + iconName; - return { - color: "#00bb00", - icon: L.icon({ - iconUrl: iconUrl, - iconSize: [50, 50] - }) - }; - }; - }; - return BikeStations; -}(LayerDefinition_1.LayerDefinition)); -exports.default = BikeStations; diff --git a/Customizations/Layers/Birdhide.js b/Customizations/Layers/Birdhide.js deleted file mode 100644 index f7ca70b..0000000 --- a/Customizations/Layers/Birdhide.js +++ /dev/null @@ -1,145 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Birdhide = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var FixedText_1 = require("../Questions/FixedText"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var leaflet_1 = require("leaflet"); -var Birdhide = /** @class */ (function (_super) { - __extends(Birdhide, _super); - function Birdhide() { - var _this = _super.call(this, { - name: "vogelkijkplaats", - overpassFilter: Birdhide.birdhide, - elementsToShow: [new FixedText_1.default("hi")], - icon: "assets/nature/birdhide.svg", - minzoom: 12, - newElementTags: [Birdhide.birdhide], - style: function (tags) { - return { color: "", icon: undefined }; - }, - }) || this; - function rmStart(toRemove, title) { - if (title.toLowerCase().indexOf(toRemove.toLowerCase()) == 0) { - return title.substr(toRemove.length).trim(); - } - return title; - } - function rmStarts(toRemove, title) { - for (var _i = 0, toRemove_1 = toRemove; _i < toRemove_1.length; _i++) { - var toRm = toRemove_1[_i]; - title = rmStart(toRm, title); - } - return title; - } - _this.title = new TagRendering_1.TagRenderingOptions({ - tagsPreprocessor: function (tags) { - if (tags.name) { - var nm = rmStarts(["Vogelkijkhut", "Vogelkijkwand", "Kijkwand", "Kijkhut"], tags.name); - tags.name = " '" + nm + "'"; - } - else { - tags.name = ""; - } - }, - mappings: [ - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("shelter", "no"), new TagsFilter_1.Tag("building", "")]), - txt: "Vogelkijkwand{name}" - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes")]), - txt: "Vogelijkhut{name}" - }, - { - k: new TagsFilter_1.Tag("amenity", "shelter"), - txt: "Vogelijkhut{name}" - }, - { - k: new TagsFilter_1.Tag("building", "yes"), - txt: "Vogelijkhut{name}" - }, - { k: null, txt: "Vogelkijkplaats{name}" } - ] - }); - _this.style = function (properties) { - var icon = "assets/nature/birdhide.svg"; - if (new TagsFilter_1.Or([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes"), new TagsFilter_1.Tag("shelter", "yes")]).matchesProperties(properties)) { - icon = "assets/nature/birdshelter.svg"; - } - return { - color: "#0000bb", - icon: leaflet_1.default.icon({ - iconUrl: icon, - iconSize: [40, 40], - iconAnchor: [20, 20] - }) - }; - }; - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "Is dit een kijkwand of kijkhut?", - mappings: [ - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("shelter", "no"), new TagsFilter_1.Tag("building", ""), new TagsFilter_1.Tag("amenity", "")]), - txt: "Vogelkijkwand" - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("amenity", "shelter"), new TagsFilter_1.Tag("building", "yes"), new TagsFilter_1.Tag("shelter", "yes")]), - txt: "Vogelijkhut" - } - ] - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is ze rolstoeltoegankelijk?", - mappings: [ - { - k: new TagsFilter_1.Tag("wheelchair", "no"), - txt: "Niet rolstoeltoegankelijk" - }, - { - k: new TagsFilter_1.Tag("wheelchair", "limited"), - txt: "Een rolstoel raakt er, maar het is niet makkelijk" - }, - { - k: new TagsFilter_1.Tag("wheelchair", "yes"), - txt: "Een rolstoel raakt er gemakkelijk" - } - ] - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wie beheert deze?", - freeform: { - key: "operator", - template: "Beheer door $$$", - renderTemplate: "Beheer door {operator}", - placeholder: "organisatie" - }, - mappings: [ - { k: new TagsFilter_1.Tag("operator", "Natuurpunt"), txt: "Natuurpunt" }, - { k: new TagsFilter_1.Tag("operator", "Agentschap Natuur en Bos"), txt: "het Agentschap Natuur en Bos (ANB)" }, - ] - }) - ]; - return _this; - } - Birdhide.birdhide = new TagsFilter_1.Tag("leisure", "bird_hide"); - return Birdhide; -}(LayerDefinition_1.LayerDefinition)); -exports.Birdhide = Birdhide; diff --git a/Customizations/Layers/Bookcases.js b/Customizations/Layers/Bookcases.js deleted file mode 100644 index 39793ab..0000000 --- a/Customizations/Layers/Bookcases.js +++ /dev/null @@ -1,167 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Bookcases = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var leaflet_1 = require("leaflet"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var NameInline_1 = require("../Questions/NameInline"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var Bookcases = /** @class */ (function (_super) { - __extends(Bookcases, _super); - function Bookcases() { - var _this = _super.call(this) || this; - _this.name = "boekenkast"; - _this.newElementTags = [new TagsFilter_1.Tag("amenity", "public_bookcase")]; - _this.icon = "./assets/bookcase.svg"; - _this.overpassFilter = new TagsFilter_1.Tag("amenity", "public_bookcase"); - _this.minzoom = 11; - _this.title = new NameInline_1.NameInline("ruilboekenkastje"); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "Heeft dit boekenruilkastje een naam?", - freeform: { - key: "name", - template: "De naam is $$$", - renderTemplate: "", - placeholder: "", - extraTags: new TagsFilter_1.Tag("noname", "") - }, - mappings: [ - { k: new TagsFilter_1.Tag("noname", "yes"), txt: "Neen, er is geen naam aangeduid op het boekenruilkastje" }, - ] - }), - new TagRendering_1.TagRenderingOptions({ - question: "Hoeveel boeken passen in dit boekenruilkastje?", - freeform: { - renderTemplate: "Er passen {capacity} boeken in dit boekenruilkastje", - template: "Er passen $$$ boeken in dit boekenruilkastje", - key: "capacity", - placeholder: "aantal" - }, - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wat voor soort boeken heeft dit boekenruilkastje?", - mappings: [ - { k: new TagsFilter_1.Tag("books", "children"), txt: "Voornamelijk kinderboeken" }, - { k: new TagsFilter_1.Tag("books", "adults"), txt: "Voornamelijk boeken voor volwassenen" }, - { k: new TagsFilter_1.Tag("books", "children;adults"), txt: "Zowel kinderboeken als boeken voor volwassenen" } - ], - }), - new TagRendering_1.TagRenderingOptions({ - question: "Staat dit boekenruilkastje binnen of buiten?", - mappings: [ - { k: new TagsFilter_1.Tag("indoor", "yes"), txt: "Dit boekenruilkastje staat binnen" }, - { k: new TagsFilter_1.Tag("indoor", "no"), txt: "Dit boekenruilkastje staat buiten" }, - { k: new TagsFilter_1.Tag("indoor", ""), txt: "Dit boekenruilkastje staat buiten" } - ] - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is dit boekenruilkastje vrij toegankelijk?", - mappings: [ - { k: new TagsFilter_1.Tag("access", "yes"), txt: "Ja, vrij toegankelijk" }, - { k: new TagsFilter_1.Tag("access", "customers"), txt: "Enkel voor klanten" }, - ] - }).OnlyShowIf(new TagsFilter_1.Tag("indoor", "yes")), - new TagRendering_1.TagRenderingOptions({ - question: "Wie (welke organisatie) beheert dit boekenruilkastje?", - freeform: { - key: "opeartor", - renderTemplate: "Dit boekenruilkastje wordt beheerd door {operator}", - template: "Dit boekenruilkastje wordt beheerd door $$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Zijn er openingsuren voor dit boekenruilkastje?", - mappings: [ - { k: new TagsFilter_1.Tag("opening_hours", "24/7"), txt: "Dag en nacht toegankelijk" }, - { k: new TagsFilter_1.Tag("opening_hours", ""), txt: "Dag en nacht toegankelijk" }, - { k: new TagsFilter_1.Tag("opening_hours", "sunrise-sunset"), txt: "Van zonsopgang tot zonsondergang" }, - ], - freeform: { - key: "opening_hours", - renderTemplate: "De openingsuren zijn {opening_hours}", - template: "De openingsuren zijn $$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is dit boekenruilkastje deel van een netwerk?", - freeform: { - key: "brand", - renderTemplate: "Deel van het netwerk {brand}", - template: "Deel van het netwerk $$$" - }, - mappings: [{ - k: new TagsFilter_1.And([new TagsFilter_1.Tag("brand", "Little Free Library"), new TagsFilter_1.Tag("nobrand", "")]), - txt: "Little Free Library" - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("brand", ""), new TagsFilter_1.Tag("nobrand", "yes")]), - txt: "Maakt geen deel uit van een groter netwerk" - }] - }).OnlyShowIf(new TagsFilter_1.Or([ - new TagsFilter_1.Tag("ref", ""), - new TagsFilter_1.And([new TagsFilter_1.Tag("ref", "*"), new TagsFilter_1.Tag("brand", "")]) - ])), - new TagRendering_1.TagRenderingOptions({ - question: "Wat is het referentienummer van dit boekenruilkastje?", - freeform: { - key: "ref", - template: "Het referentienummer is $$$", - renderTemplate: "Gekend als {brand} <b>{ref}</b>" - } - }).OnlyShowIf(new TagsFilter_1.Tag("brand", "*")), - new TagRendering_1.TagRenderingOptions({ - question: "Wanneer werd dit boekenruilkastje geinstalleerd?", - priority: -1, - freeform: { - key: "start_date", - renderTemplate: "Geplaatst op {start_date}", - template: "Geplaatst op $$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is er een website waar we er meer informatie is over dit boekenruilkastje?", - freeform: { - key: "website", - renderTemplate: "<a href='{website}' target='_blank'>Meer informatie over dit boekenruilkastje</a>", - template: "$$$", - placeholder: "website" - } - }), - new TagRendering_1.TagRenderingOptions({ - freeform: { - key: "description", - renderTemplate: "<b>Beschrijving door de uitbater:</b><br>{description}", - template: "$$$", - } - }) - ]; - _this.style = function (tags) { - return { - icon: new leaflet_1.default.icon({ - iconUrl: "assets/bookcase.svg", - iconSize: [40, 40] - }), - color: "#0000ff" - }; - }; - return _this; - } - return Bookcases; -}(LayerDefinition_1.LayerDefinition)); -exports.Bookcases = Bookcases; diff --git a/Customizations/Layers/Bos.js b/Customizations/Layers/Bos.js deleted file mode 100644 index ef95dec..0000000 --- a/Customizations/Layers/Bos.js +++ /dev/null @@ -1,85 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Bos = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var AccessTag_1 = require("../Questions/AccessTag"); -var OperatorTag_1 = require("../Questions/OperatorTag"); -var NameQuestion_1 = require("../Questions/NameQuestion"); -var NameInline_1 = require("../Questions/NameInline"); -var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var Bos = /** @class */ (function (_super) { - __extends(Bos, _super); - function Bos() { - var _this = _super.call(this) || this; - _this.name = "bos"; - _this.icon = "./assets/tree_white_background.svg"; - _this.overpassFilter = new TagsFilter_1.Or([ - new TagsFilter_1.Tag("natural", "wood"), - new TagsFilter_1.Tag("landuse", "forest"), - new TagsFilter_1.Tag("natural", "scrub") - ]); - _this.newElementTags = [ - new TagsFilter_1.Tag("landuse", "forest"), - new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen") - ]; - _this.maxAllowedOverlapPercentage = 10; - _this.minzoom = 13; - _this.style = _this.generateStyleFunction(); - _this.title = new NameInline_1.NameInline("bos"); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new NameQuestion_1.NameQuestion(), - new AccessTag_1.AccessTag(), - new OperatorTag_1.OperatorTag(), - new DescriptionQuestion_1.DescriptionQuestion("bos") - ]; - return _this; - } - Bos.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - var questionSeverity = 0; - for (var _i = 0, _a = self.elementsToShow; _i < _a.length; _i++) { - var qd = _a[_i]; - if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { - continue; - } - if (qd.IsQuestioning(properties)) { - questionSeverity = Math.max(questionSeverity, qd.Priority()); - } - } - var colormapping = { - 0: "#00bb00", - 1: "#00ff00", - 10: "#dddd00", - 20: "#ff0000" - }; - var colour = colormapping[questionSeverity]; - while (colour == undefined) { - questionSeverity--; - colour = colormapping[questionSeverity]; - } - return { - color: colour, - icon: undefined - }; - }; - }; - return Bos; -}(LayerDefinition_1.LayerDefinition)); -exports.Bos = Bos; diff --git a/Customizations/Layers/DrinkingWater.js b/Customizations/Layers/DrinkingWater.js deleted file mode 100644 index ba18984..0000000 --- a/Customizations/Layers/DrinkingWater.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DrinkingWater = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var OperatorTag_1 = require("../Questions/OperatorTag"); -var L = require("leaflet"); -var FixedText_1 = require("../Questions/FixedText"); -var TagRendering_1 = require("../TagRendering"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var DrinkingWater = /** @class */ (function (_super) { - __extends(DrinkingWater, _super); - function DrinkingWater() { - var _this = _super.call(this) || this; - _this.name = "drinking_water"; - _this.icon = "./assets/bug.svg"; - _this.overpassFilter = new TagsFilter_1.Or([ - new TagsFilter_1.And([ - new TagsFilter_1.Tag("amenity", "drinking_water") - ]) - ]); - _this.newElementTags = [ - new TagsFilter_1.Tag("amenity", "drinking_water"), - ]; - _this.maxAllowedOverlapPercentage = 10; - _this.minzoom = 13; - _this.style = _this.generateStyleFunction(); - _this.title = new FixedText_1.default("Drinking water"); - _this.elementsToShow = [ - new OperatorTag_1.OperatorTag(), - ]; - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "How easy is it to fill water bottles?", - mappings: [ - { k: new TagsFilter_1.Tag("bottle", "yes"), txt: "It is easy to refill water bottles" }, - { k: new TagsFilter_1.Tag("bottle", "no"), txt: "Water bottles may not fit" } - ], - }) - ]; - return _this; - } - DrinkingWater.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - return { - color: "#00bb00", - icon: new L.icon({ - iconUrl: self.icon, - iconSize: [40, 40] - }) - }; - }; - }; - return DrinkingWater; -}(LayerDefinition_1.LayerDefinition)); -exports.DrinkingWater = DrinkingWater; diff --git a/Customizations/Layers/GhostBike.js b/Customizations/Layers/GhostBike.js deleted file mode 100644 index 23de7ef..0000000 --- a/Customizations/Layers/GhostBike.js +++ /dev/null @@ -1,80 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GhostBike = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var FixedText_1 = require("../Questions/FixedText"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var leaflet_1 = require("leaflet"); -var GhostBike = /** @class */ (function (_super) { - __extends(GhostBike, _super); - function GhostBike() { - var _this = _super.call(this) || this; - _this.name = "ghost bike"; - _this.overpassFilter = new TagsFilter_1.Tag("memorial", "ghost_bike"); - _this.title = new FixedText_1.default("Ghost bike"); - _this.elementsToShow = [ - new FixedText_1.default("A <b>ghost bike</b> is a memorial for a cyclist who died in a traffic accident," + - " in the form of a white bicycle placed permanently near the accident location."), - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "Whom is remembered by this ghost bike?" + - "<span class='question-subtext'>" + - "<br/>" + - "Please respect privacy - only fill out the name if it is widely published or marked on the cycle." + - "</span>", - mappings: [{ k: new TagsFilter_1.Tag("noname", "yes"), txt: "There is no name marked on the bike" },], - freeform: { - key: "name", - extraTags: new TagsFilter_1.Tag("noname", ""), - template: "$$$", - renderTemplate: "In the remembrance of <b>{name}</b>", - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "When was the ghost bike installed?", - freeform: { - key: "start_date", - template: "The ghost bike was placed on $$$", - renderTemplate: "The ghost bike was placed on <b>{start_date}</b>", - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "On what URL can more information be found?" + - "<span class='question-subtext'>If available, add a link to a news report about the accident or about the placing of the ghost bike</span>", - freeform: { - key: "source", - template: "More information available on $$$", - renderTemplate: "<a href='{source}' target='_blank'>More information</a>", - } - }), - ]; - _this.style = function (tags) { - return { - color: "#000000", - icon: leaflet_1.default.icon({ - iconUrl: 'assets/bike/ghost.svg', - iconSize: [40, 40], - iconAnchor: [20, 20], - }) - }; - }; - return _this; - } - return GhostBike; -}(LayerDefinition_1.LayerDefinition)); -exports.GhostBike = GhostBike; diff --git a/Customizations/Layers/GrbToFix.js b/Customizations/Layers/GrbToFix.js deleted file mode 100644 index bec3a35..0000000 --- a/Customizations/Layers/GrbToFix.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GrbToFix = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var leaflet_1 = require("leaflet"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var GrbToFix = /** @class */ (function (_super) { - __extends(GrbToFix, _super); - function GrbToFix() { - var _this = _super.call(this) || this; - _this.name = "grb"; - _this.newElementTags = undefined; - _this.icon = "./assets/star.svg"; - _this.overpassFilter = new TagsFilter_1.Regex("fixme", "GRB"); - _this.minzoom = 13; - _this.style = function (tags) { - return { - icon: new leaflet_1.default.icon({ - iconUrl: "assets/star.svg", - iconSize: [40, 40], - text: "hi" - }), - color: "#ff0000" - }; - }; - _this.title = new TagRendering_1.TagRenderingOptions({ - freeform: { - key: "fixme", - renderTemplate: "{fixme}", - template: "Fixme $$$" - } - }); - _this.elementsToShow = [ - new TagRendering_1.TagRenderingOptions({ - freeform: { - key: "addr:street", - renderTemplate: "Het adres is {addr:street} <b>{addr:housenumber}</b>", - template: "Straat? $$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wat is het huisnummer?", - tagsPreprocessor: function (tags) { - var telltale = "GRB thinks that this has number "; - var index = tags.fixme.indexOf(telltale); - if (index >= 0) { - var housenumber = tags.fixme.slice(index + telltale.length); - tags["grb:housenumber:human"] = housenumber; - tags["grb:housenumber"] = housenumber == "no number" ? "" : housenumber; - } - }, - freeform: { - key: "addr:housenumber", - template: "Het huisnummer is $$$", - renderTemplate: "Het huisnummer is <b>{addr:housenumber}</b>, GRB denkt <i>{grb:housenumber:human}</i>", - extraTags: new TagsFilter_1.Tag("fixme", "") - }, - mappings: [ - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("addr:housenumber", "{grb:housenumber}"), new TagsFilter_1.Tag("fixme", "")]), - txt: "Volg GRB: <b>{grb:housenumber:human}</b>", - substitute: true - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("addr:housenumber", "{addr:housenumber}"), new TagsFilter_1.Tag("fixme", "")]), - txt: "Volg OSM: <b>{addr:housenumber}</b>", - substitute: true - } - ] - }) - ]; - return _this; - } - return GrbToFix; -}(LayerDefinition_1.LayerDefinition)); -exports.GrbToFix = GrbToFix; diff --git a/Customizations/Layers/InformationBoard.js b/Customizations/Layers/InformationBoard.js deleted file mode 100644 index 57c8e24..0000000 --- a/Customizations/Layers/InformationBoard.js +++ /dev/null @@ -1,122 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InformationBoard = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var leaflet_1 = require("leaflet"); -var InformationBoard = /** @class */ (function (_super) { - __extends(InformationBoard, _super); - function InformationBoard() { - var _this = _super.call(this, { - name: "Informatiebord", - minzoom: 12, - overpassFilter: new TagsFilter_1.Tag("tourism", "information"), - newElementTags: [new TagsFilter_1.Tag("tourism", "information")], - maxAllowedOverlapPercentage: 0, - icon: "assets/nature/info.png", - }) || this; - var isMap = new TagsFilter_1.Tag("information", "map"); - var isOsmSource = new TagsFilter_1.Tag("map_source", "OpenStreetMap"); - _this.title = new TagRendering_1.TagRenderingOptions({ - mappings: [ - { k: isMap, txt: "Kaart" }, - { k: null, txt: "Informatiebord" } - ] - }); - _this.style = function (properties) { - var icon = "assets/nature/info.png"; - if (isMap.matchesProperties(properties)) { - icon = "assets/map.svg"; - if (isOsmSource.matchesProperties(properties)) { - icon = "assets/osm-logo-white-bg.svg"; - var attr = properties["map_source:attribution"]; - if (attr == "sticker") { - icon = "assets/map-stickered.svg"; - } - else if (attr == "no") { - icon = "assets/osm-logo-buggy-attr.svg"; - } - } - } - return { - color: "#000000", - icon: leaflet_1.default.icon({ - iconUrl: icon, - iconSize: [50, 50] - }) - }; - }; - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "Heeft dit informatiebord een kaart?", - mappings: [ - { k: new TagsFilter_1.Tag("information", "board"), txt: "Dit is een informatiebord" }, - { k: isMap, txt: "Dit is een kaart" } - ] - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is this map based on OpenStreetMap?", - mappings: [ - { - k: isOsmSource, - txt: "This map is based on OpenStreetMap" - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("map_source:attribution", ""), new TagsFilter_1.Tag("map_source", "")]), - txt: "Unknown" - }, - ], - freeform: { - key: "map_source", - extraTags: new TagsFilter_1.Tag("map_source:attribution", ""), - renderTemplate: "The map data is based on {map_source}", - template: "The map data is based on $$$" - } - }).OnlyShowIf(isMap), - new TagRendering_1.TagRenderingOptions({ - question: "Is the attribution present?", - mappings: [ - { - k: new TagsFilter_1.Tag("map_source:attribution", "yes"), - txt: "OpenStreetMap is clearly attribute, including the ODBL-license" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "incomplete"), - txt: "OpenStreetMap is clearly attribute, but the license is not mentioned" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "sticker"), - txt: "OpenStreetMap wasn't mentioned, but someone put an OpenStreetMap-sticker on it" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "no"), - txt: "There is no attribution at all" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "none"), - txt: "There is no attribution at all" - } - ] - }).OnlyShowIf(new TagsFilter_1.Tag("map_source", "OpenStreetMap")) - ]; - return _this; - } - return InformationBoard; -}(LayerDefinition_1.LayerDefinition)); -exports.InformationBoard = InformationBoard; diff --git a/Customizations/Layers/Map.js b/Customizations/Layers/Map.js deleted file mode 100644 index 6206d8f..0000000 --- a/Customizations/Layers/Map.js +++ /dev/null @@ -1,99 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Map = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var FixedText_1 = require("../Questions/FixedText"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var leaflet_1 = require("leaflet"); -var Map = /** @class */ (function (_super) { - __extends(Map, _super); - function Map() { - var _this = _super.call(this) || this; - _this.name = "Map"; - _this.title = new FixedText_1.default("Map"); - _this.minzoom = 12; - _this.overpassFilter = new TagsFilter_1.Tag("information", "map"); - _this.newElementTags = [new TagsFilter_1.Tag("tourism", "information"), new TagsFilter_1.Tag("information", "map")]; - var isOsmSource = new TagsFilter_1.Tag("map_source", "OpenStreetMap"); - _this.style = function (properties) { - var icon = "assets/map.svg"; - if (isOsmSource.matchesProperties(properties)) { - icon = "assets/osm-logo-white-bg.svg"; - var attr = properties["map_source:attribution"]; - if (attr == "sticker") { - icon = "assets/map-stickered.svg"; - } - else if (attr == "no") { - icon = "assets/osm-logo-buggy-attr.svg"; - } - } - return { - color: "#000000", - icon: leaflet_1.default.icon({ - iconUrl: icon, - iconSize: [50, 50] - }) - }; - }; - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new TagRendering_1.TagRenderingOptions({ - question: "Is this map based on OpenStreetMap?", - mappings: [ - { - k: isOsmSource, - txt: "This map is based on OpenStreetMap" - }, - ], - freeform: { - key: "map_source", - renderTemplate: "The map data is based on {map_source}", - template: "The map data is based on $$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Is the attribution present?", - mappings: [ - { - k: new TagsFilter_1.Tag("map_source:attribution", "yes"), - txt: "OpenStreetMap is clearly attribute, including the ODBL-license" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "incomplete"), - txt: "OpenStreetMap is clearly attribute, but the license is not mentioned" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "sticker"), - txt: "OpenStreetMap wasn't mentioned, but someone put an OpenStreetMap-sticker on it" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "no"), - txt: "There is no attribution at all" - }, - { - k: new TagsFilter_1.Tag("map_source:attribution", "none"), - txt: "There is no attribution at all" - } - ] - }).OnlyShowIf(new TagsFilter_1.Tag("map_source", "OpenStreetMap")) - ]; - return _this; - } - return Map; -}(LayerDefinition_1.LayerDefinition)); -exports.Map = Map; diff --git a/Customizations/Layers/NatureReserves.js b/Customizations/Layers/NatureReserves.js deleted file mode 100644 index 76e2777..0000000 --- a/Customizations/Layers/NatureReserves.js +++ /dev/null @@ -1,134 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.NatureReserves = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var AccessTag_1 = require("../Questions/AccessTag"); -var OperatorTag_1 = require("../Questions/OperatorTag"); -var NameQuestion_1 = require("../Questions/NameQuestion"); -var NameInline_1 = require("../Questions/NameInline"); -var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var NatureReserves = /** @class */ (function (_super) { - __extends(NatureReserves, _super); - function NatureReserves(moreQuests) { - if (moreQuests === void 0) { moreQuests = false; } - var _this = _super.call(this) || this; - _this.name = "natuurgebied"; - _this.icon = "./assets/tree_white_background.svg"; - _this.overpassFilter = - new TagsFilter_1.Or([new TagsFilter_1.Tag("leisure", "nature_reserve"), new TagsFilter_1.Tag("boundary", "protected_area")]); - _this.maxAllowedOverlapPercentage = 10; - _this.newElementTags = [new TagsFilter_1.Tag("leisure", "nature_reserve"), - new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")]; - _this.minzoom = 13; - _this.title = new NameInline_1.NameInline("natuurreservaat"); - _this.style = _this.generateStyleFunction(); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new NameQuestion_1.NameQuestion(), - new AccessTag_1.AccessTag(), - new OperatorTag_1.OperatorTag(), - new DescriptionQuestion_1.DescriptionQuestion("natuurgebied") - ]; - var extraRenderings = [ - new TagRendering_1.TagRenderingOptions({ - question: "Mogen honden in dit natuurgebied?", - mappings: [ - { k: new TagsFilter_1.Tag("dog", "leashed"), txt: "Honden moeten aan de leiband" }, - { k: new TagsFilter_1.Tag("dog", "no"), txt: "Honden zijn niet toegestaan" }, - { k: new TagsFilter_1.Tag("dog", "yes"), txt: "Honden zijn welkom" }, - ] - }).OnlyShowIf(new TagsFilter_1.Tag("access", "yes")), - new TagRendering_1.TagRenderingOptions({ - question: "Op welke website kunnen we meer informatie vinden over dit natuurgebied?", - freeform: { - key: "website", - renderTemplate: "<a href='{website}' target='_blank'>Meer informatie</a>", - template: "$$$" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wie is de conservator van dit gebied?<br>" + - "<span class='question-subtext'>Geef de naam van de conservator énkel als die duidelijk online staat gepubliceerd.</span>", - freeform: { - renderTemplate: "De conservator van dit gebied is {curator}", - template: "$$$", - key: "curator" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wat is het email-adres van de beheerder?<br>" + - "<span class='question-subtext'>Geef bij voorkeur het emailadres van de Natuurpunt-afdeling; geef enkel een email-adres van de conservator als dit duidelijk is gepubliceerd</span>", - freeform: { - renderTemplate: "Bij problemen of vragen, de conservator kan bereikt worden via " + - "<a href='mailto:{email}'>{email}</a>", - template: "$$$", - key: "email" - } - }), - new TagRendering_1.TagRenderingOptions({ - question: "Wat is het telefoonnummer van de beheerder?<br>" + - "<span class='question-subtext'>Geef bij voorkeur het telefoonnummer van de Natuurpunt-afdeling; geef enkel een email-adres van de conservator als dit duidelijk is gepubliceerd</span>", - freeform: { - renderTemplate: "Bij problemen of vragen, de {conservator} kan bereikt worden via " + - "<a href='tel:{phone}'>{phone}</a>", - template: "$$$", - key: "phone" - } - }), - ]; - if (moreQuests) { - _this.elementsToShow = - _this.elementsToShow.concat(extraRenderings); - } - return _this; - } - NatureReserves.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - var _a; - var questionSeverity = 0; - for (var _i = 0, _b = self.elementsToShow; _i < _b.length; _i++) { - var qd = _b[_i]; - if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { - continue; - } - if (qd.IsQuestioning(properties)) { - questionSeverity = Math.max(questionSeverity, (_a = qd.Priority()) !== null && _a !== void 0 ? _a : 0); - } - } - var colormapping = { - 0: "#00bb00", - 1: "#00ff00", - 10: "#dddd00", - 20: "#ff0000" - }; - var colour = colormapping[questionSeverity]; - while (colour == undefined) { - questionSeverity--; - colour = colormapping[questionSeverity]; - } - return { - color: colour, - icon: undefined - }; - }; - }; - return NatureReserves; -}(LayerDefinition_1.LayerDefinition)); -exports.NatureReserves = NatureReserves; diff --git a/Customizations/Layers/Park.js b/Customizations/Layers/Park.js deleted file mode 100644 index d9751e3..0000000 --- a/Customizations/Layers/Park.js +++ /dev/null @@ -1,108 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Park = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var NameQuestion_1 = require("../Questions/NameQuestion"); -var NameInline_1 = require("../Questions/NameInline"); -var DescriptionQuestion_1 = require("../Questions/DescriptionQuestion"); -var ImageCarouselWithUpload_1 = require("../../UI/Image/ImageCarouselWithUpload"); -var Park = /** @class */ (function (_super) { - __extends(Park, _super); - function Park() { - var _this = _super.call(this) || this; - _this.accessByDefault = new TagRendering_1.TagRenderingOptions({ - question: "Is dit park publiek toegankelijk?", - mappings: [ - { k: new TagsFilter_1.Tag("access", "yes"), txt: "Publiek toegankelijk" }, - { k: new TagsFilter_1.Tag("access", ""), txt: "Publiek toegankelijk" }, - { k: new TagsFilter_1.Tag("access", "no"), txt: "Niet publiek toegankelijk" }, - { k: new TagsFilter_1.Tag("access", "private"), txt: "Niet publiek toegankelijk, want privaat" }, - { k: new TagsFilter_1.Tag("access", "guided"), txt: "Enkel toegankelijk met een gids of op een activiteit" }, - ], - freeform: { - key: "access", - renderTemplate: "Dit park is niet toegankelijk: {access}", - template: "De toegankelijkheid van dit park is: $$$" - }, - priority: 20 - }); - _this.operatorByDefault = new TagRendering_1.TagRenderingOptions({ - question: "Wie beheert dit park?", - freeform: { - key: "operator", - renderTemplate: "Dit park wordt beheerd door {operator}", - template: "$$$", - }, - mappings: [{ - k: null, txt: "De gemeente beheert dit park" - }], - priority: 15 - }); - _this.name = "park"; - _this.icon = "./assets/tree_white_background.svg"; - _this.overpassFilter = - new TagsFilter_1.Or([new TagsFilter_1.Tag("leisure", "park"), new TagsFilter_1.Tag("landuse", "village_green")]); - _this.newElementTags = [new TagsFilter_1.Tag("leisure", "park"), - new TagsFilter_1.Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")]; - _this.maxAllowedOverlapPercentage = 25; - _this.minzoom = 13; - _this.style = _this.generateStyleFunction(); - _this.title = new NameInline_1.NameInline("park"); - _this.elementsToShow = [ - new ImageCarouselWithUpload_1.ImageCarouselWithUploadConstructor(), - new NameQuestion_1.NameQuestion(), - _this.accessByDefault, - _this.operatorByDefault, - new DescriptionQuestion_1.DescriptionQuestion("park"), - ]; - return _this; - } - Park.prototype.generateStyleFunction = function () { - var self = this; - return function (properties) { - var _a; - var questionSeverity = 0; - for (var _i = 0, _b = self.elementsToShow; _i < _b.length; _i++) { - var qd = _b[_i]; - if (qd instanceof DescriptionQuestion_1.DescriptionQuestion) { - continue; - } - if (qd.IsQuestioning(properties)) { - questionSeverity = Math.max(questionSeverity, (_a = qd.Priority()) !== null && _a !== void 0 ? _a : 0); - } - } - var colormapping = { - 0: "#00bb00", - 1: "#00ff00", - 10: "#dddd00", - 20: "#ff0000" - }; - var colour = colormapping[questionSeverity]; - while (colour == undefined) { - questionSeverity--; - colour = colormapping[questionSeverity]; - } - return { - color: colour, - icon: undefined - }; - }; - }; - return Park; -}(LayerDefinition_1.LayerDefinition)); -exports.Park = Park; diff --git a/Customizations/Layers/Toilets.js b/Customizations/Layers/Toilets.js deleted file mode 100644 index 564c59c..0000000 --- a/Customizations/Layers/Toilets.js +++ /dev/null @@ -1,94 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Toilets = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var Quests_1 = require("../../Quests"); -var FixedUiElement_1 = require("../../UI/Base/FixedUiElement"); -var leaflet_1 = require("leaflet"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var Toilets = /** @class */ (function (_super) { - __extends(Toilets, _super); - function Toilets() { - var _a, _b; - var _this = _super.call(this) || this; - _this.name = "toilet"; - _this.newElementTags = [new TagsFilter_1.Tag("amenity", "toilets")]; - _this.icon = "./assets/toilets.svg"; - _this.overpassFilter = new TagsFilter_1.Tag("amenity", "toilets"); - _this.minzoom = 13; - _this.questions = [Quests_1.Quests.hasFee, - Quests_1.Quests.toiletsWheelChairs, - Quests_1.Quests.toiletsChangingTable, - Quests_1.Quests.toiletsChangingTableLocation, - Quests_1.Quests.toiletsPosition]; - _this.style = function (tags) { - if (tags.wheelchair == "yes") { - return { icon: new leaflet_1.default.icon({ - iconUrl: "assets/wheelchair.svg", - iconSize: [40, 40] - }) }; - } - return { icon: new leaflet_1.default.icon({ - iconUrl: "assets/toilets.svg", - iconSize: [40, 40] - }) }; - }; - _this.elementsToShow = [ - new FixedUiElement_1.FixedUiElement("Toiletten"), - new TagMappingOptions({ - key: "access", - mapping: { - yes: "Toegankelijk", - no: "Niet toegankelijk", - private: "Niet toegankelijk", - customers: "Enkel voor klanten", - } - }), - new TagMappingOptions({ - key: "fee", - mapping: (_a = { - yes: "Betalend", - no: "Gratis" - }, - _a["0"] = "Gratis", - _a), - template: "Betalend, men vraagt {fee}" - }), - new TagMappingOptions({ - key: "toilets:position", - mapping: (_b = { - seated: 'Gewone zittoiletten', - urinal: 'Een enkele urinoir', - urinals: 'Urinoirs' - }, - _b['urinals;seated'] = "Urinoirs en gewone toiletten", - _b['seated;urinals'] = "Urinoirs en gewone toiletten", - _b) - }), - new TagMappingOptions({ - key: "wheelchair", - mapping: { - yes: "Rolstoeltoegankelijk", - no: "Niet Rolstoeltoegankelijk", - limited: "Beperkt rolstoeltoegankelijk", - } - }), - ]; - return _this; - } - return Toilets; -}(LayerDefinition_1.LayerDefinition)); -exports.Toilets = Toilets; diff --git a/Customizations/Layers/Widths.js b/Customizations/Layers/Widths.js deleted file mode 100644 index 748e453..0000000 --- a/Customizations/Layers/Widths.js +++ /dev/null @@ -1,270 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Widths = void 0; -var LayerDefinition_1 = require("../LayerDefinition"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var TagRendering_1 = require("../TagRendering"); -var Widths = /** @class */ (function (_super) { - __extends(Widths, _super); - function Widths(carWidth, cyclistWidth, pedestrianWidth) { - var _this = _super.call(this) || this; - _this._bothSideParking = new TagsFilter_1.Tag("parking:lane:both", "parallel"); - _this._noSideParking = new TagsFilter_1.Tag("parking:lane:both", "no_parking"); - _this._otherParkingMode = new TagsFilter_1.Or([ - new TagsFilter_1.Tag("parking:lane:both", "perpendicular"), - new TagsFilter_1.Tag("parking:lane:left", "perpendicular"), - new TagsFilter_1.Tag("parking:lane:right", "perpendicular"), - new TagsFilter_1.Tag("parking:lane:both", "diagonal"), - new TagsFilter_1.Tag("parking:lane:left", "diagonal"), - new TagsFilter_1.Tag("parking:lane:right", "diagonal"), - ]); - _this._leftSideParking = new TagsFilter_1.And([new TagsFilter_1.Tag("parking:lane:left", "parallel"), new TagsFilter_1.Tag("parking:lane:right", "no_parking")]); - _this._rightSideParking = new TagsFilter_1.And([new TagsFilter_1.Tag("parking:lane:right", "parallel"), new TagsFilter_1.Tag("parking:lane:left", "no_parking")]); - _this._sidewalkBoth = new TagsFilter_1.Tag("sidewalk", "both"); - _this._sidewalkLeft = new TagsFilter_1.Tag("sidewalk", "left"); - _this._sidewalkRight = new TagsFilter_1.Tag("sidewalk", "right"); - _this._sidewalkNone = new TagsFilter_1.Tag("sidewalk", "none"); - _this._oneSideParking = new TagsFilter_1.Or([_this._leftSideParking, _this._rightSideParking]); - _this._carfree = new TagsFilter_1.Or([new TagsFilter_1.Tag("highway", "pedestrian"), new TagsFilter_1.Tag("highway", "living_street")]); - _this._notCarFree = new TagsFilter_1.Not(_this._carfree); - _this.carWidth = carWidth; - _this.cyclistWidth = cyclistWidth; - _this.pedestrianWidth = pedestrianWidth; - _this.minzoom = 12; - function r(n) { - var pre = Math.floor(n); - var post = Math.floor((n * 10) % 10); - return "" + pre + "." + post; - } - _this.name = "widths"; - _this.overpassFilter = new TagsFilter_1.Tag("width:carriageway", "*"); - _this.title = new TagRendering_1.TagRenderingOptions({ - freeform: { - renderTemplate: "{name}", - template: "$$$", - key: "name" - } - }); - var self = _this; - _this.style = function (properties) { - var c = "#f00"; - var props = self.calcProps(properties); - if (props.pedestrianFlowNeeded > 0) { - c = "#fa0"; - } - if (props.width >= props.targetWidth || !props.cyclingAllowed) { - c = "#0c0"; - } - if (!props.parkingStateKnown && properties["note:width:carriageway"] === undefined) { - c = "#f0f"; - } - if (_this._carfree.matchesProperties(properties)) { - c = "#aaa"; - } - // Mark probably wrong data - if (props.width > 15) { - c = "#f0f"; - } - var dashArray = undefined; - if (props.onewayBike) { - dashArray = [20, 8]; - } - return { - icon: null, - color: c, - weight: 7, - dashArray: dashArray - }; - }; - _this.elementsToShow = [ - new TagRendering_1.TagRenderingOptions({ - question: "Mogen auto's hier parkeren?", - mappings: [ - { - k: _this._bothSideParking, - txt: "Auto's kunnen langs beide zijden parkeren.<br+>Dit gebruikt <b>" + r(_this.carWidth * 2) + "m</b><br/>" - }, - { - k: _this._oneSideParking, - txt: "Auto's kunnen langs één kant parkeren.<br/>Dit gebruikt <b>" + r(_this.carWidth) + "m</b><br/>" - }, - { - k: _this._otherParkingMode, - txt: "Deze straat heeft dwarsparkeren of diagonaalparkeren aan minstens één zijde. Deze parkeerruimte is niet opgenomen in de straatbreedte." - }, - { k: _this._noSideParking, txt: "Auto's mogen hier niet parkeren" }, - ], - freeform: { - key: "note:width:carriageway", - renderTemplate: "{note:width:carriageway}", - template: "$$$", - } - }).OnlyShowIf(_this._notCarFree), - new TagRendering_1.TagRenderingOptions({ - mappings: [ - { - k: _this._sidewalkNone, - txt: "Deze straat heeft geen voetpaden. Voetgangers hebben hier <b>" + r(_this.pedestrianWidth * 2) + "m</b> nodig" - }, - { - k: new TagsFilter_1.Or([_this._sidewalkLeft, _this._sidewalkRight]), - txt: "Deze straat heeft een voetpad aan één kant. Voetgangers hebben hier <b>" + r(_this.pedestrianWidth) + "m</b> nodig" - }, - { k: _this._sidewalkBoth, txt: "Deze straat heeft voetpad aan beide zijden." }, - ], - freeform: { - key: "note:width:carriageway", - renderTemplate: "{note:width:carriageway}", - template: "$$$", - } - }).OnlyShowIf(_this._notCarFree), - new TagRendering_1.TagRenderingOptions({ - mappings: [ - { - k: new TagsFilter_1.Tag("bicycle", "use_sidepath"), - txt: "Er is een afgescheiden, verplicht te gebruiken fietspad. Fietsen op dit wegsegment hoeft dus niet" - }, - { - k: new TagsFilter_1.Tag("bicycle", "no"), - txt: "Fietsen is hier niet toegestaan" - }, - { - k: new TagsFilter_1.Tag("oneway:bicycle", "yes"), - txt: "Eenrichtingsverkeer, óók voor fietsers. Dit gebruikt <b>" + r(_this.carWidth + _this.cyclistWidth) + "m</b>" - }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("oneway", "yes"), new TagsFilter_1.Tag("oneway:bicycle", "no")]), - txt: "Tweerichtingverkeer voor fietsers, eenrichting voor auto's Dit gebruikt <b>" + r(_this.carWidth + 2 * _this.cyclistWidth) + "m</b>" - }, - { - k: new TagsFilter_1.Tag("oneway", "yes"), - txt: "Eenrichtingsverkeer voor iedereen. Dit gebruikt <b>" + (_this.carWidth + _this.cyclistWidth) + "m</b>" - }, - { - k: null, - txt: "Tweerichtingsverkeer voor iedereen. Dit gebruikt <b>" + r(2 * _this.carWidth + 2 * _this.cyclistWidth) + "m</b>" - } - ] - }).OnlyShowIf(_this._notCarFree), - new TagRendering_1.TagRenderingOptions({ - tagsPreprocessor: function (tags) { - var props = self.calcProps(tags); - tags.targetWidth = r(props.targetWidth); - tags.short = ""; - if (props.width < props.targetWidth) { - tags.short = "Er is dus <b class='alert'>" + r(props.targetWidth - props.width) + "m</b> te weinig"; - } - }, - freeform: { - key: "width:carriageway", - renderTemplate: "De totale nodige ruimte voor vlot en veilig verkeer is dus <b>{targetWidth}m</b><br>" + - "{short}", - template: "$$$", - } - }).OnlyShowIf(_this._notCarFree), - new TagRendering_1.TagRenderingOptions({ - mappings: [ - { k: new TagsFilter_1.Tag("highway", "living_street"), txt: "Dit is een woonerf" }, - { k: new TagsFilter_1.Tag("highway", "pedestrian"), txt: "Deze weg is autovrij" } - ] - }), - new TagRendering_1.TagRenderingOptions({ - mappings: [ - { - k: new TagsFilter_1.Tag("sidewalk", "none"), - txt: "De afstand van huis tot huis is <b>{width:carriageway}m</b>" - }, - { - k: new TagsFilter_1.Tag("sidewalk", "left"), - txt: "De afstand van huis tot voetpad is <b>{width:carriageway}m</b>" - }, - { - k: new TagsFilter_1.Tag("sidewalk", "right"), - txt: "De afstand van huis tot voetpad is <b>{width:carriageway}m</b>" - }, - { - k: new TagsFilter_1.Tag("sidewalk", "both"), - txt: "De afstand van voetpad tot voetpad is <b>{width:carriageway}m</b>" - }, - { - k: new TagsFilter_1.Tag("sidewalk", ""), - txt: "De straatbreedte is <b>{width:carriageway}m</b>" - } - ] - }) - ]; - return _this; - } - Widths.prototype.calcProps = function (properties) { - var parkingStateKnown = true; - var parallelParkingCount = 0; - if (this._oneSideParking.matchesProperties(properties)) { - parallelParkingCount = 1; - } - else if (this._bothSideParking.matchesProperties(properties)) { - parallelParkingCount = 2; - } - else if (this._noSideParking.matchesProperties(properties)) { - parallelParkingCount = 0; - } - else if (this._otherParkingMode.matchesProperties(properties)) { - parallelParkingCount = 0; - } - else { - parkingStateKnown = false; - console.log("No parking data for ", properties.name, properties.id, properties); - } - var pedestrianFlowNeeded = 0; - if (this._sidewalkBoth.matchesProperties(properties)) { - pedestrianFlowNeeded = 0; - } - else if (this._sidewalkNone.matchesProperties(properties)) { - pedestrianFlowNeeded = 2; - } - else if (this._sidewalkLeft.matchesProperties(properties) || this._sidewalkRight.matches(properties)) { - pedestrianFlowNeeded = 1; - } - else { - pedestrianFlowNeeded = -1; - } - var onewayCar = properties.oneway === "yes"; - var onewayBike = properties["oneway:bicycle"] === "yes" || - (onewayCar && properties["oneway:bicycle"] === undefined); - var cyclingAllowed = !(properties.bicycle === "use_sidepath" - || properties.bicycle === "no"); - var carWidth = (onewayCar ? 1 : 2) * this.carWidth; - var cyclistWidth = 0; - if (cyclingAllowed) { - cyclistWidth = (onewayBike ? 1 : 2) * this.cyclistWidth; - } - var width = parseFloat(properties["width:carriageway"]); - var targetWidth = carWidth + - cyclistWidth + - Math.max(0, pedestrianFlowNeeded) * this.pedestrianWidth + - parallelParkingCount * this.carWidth; - return { - parkingLanes: parallelParkingCount, - parkingStateKnown: parkingStateKnown, - width: width, - targetWidth: targetWidth, - onewayBike: onewayBike, - pedestrianFlowNeeded: pedestrianFlowNeeded, - cyclingAllowed: cyclingAllowed - }; - }; - return Widths; -}(LayerDefinition_1.LayerDefinition)); -exports.Widths = Widths; diff --git a/Customizations/Layout.js b/Customizations/Layout.js deleted file mode 100644 index 0ba3214..0000000 --- a/Customizations/Layout.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Layout = void 0; -/** - * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). - */ -var Layout = /** @class */ (function () { - /** - * - * @param name: The name used in the query string. If in the query "quests=<name>" is defined, it will select this layout - * @param title: Will be used in the <title> of the page - * @param layers: The layers to show, a list of LayerDefinitions - * @param startzoom: The initial starting zoom of the map - * @param startLat:The initial starting latitude of the map - * @param startLon: the initial starting longitude of the map - * @param welcomeMessage: This message is shown in the collapsable box on the left - * @param gettingStartedPlzLogin: This is shown below the welcomemessage and wrapped in a login link. - * @param welcomeBackMessage: This is shown when the user is logged in - * @param welcomeTail: This text is shown below the login message. It is ideal for extra help - */ - function Layout(name, title, layers, startzoom, startLat, startLon, welcomeMessage, gettingStartedPlzLogin, welcomeBackMessage, welcomeTail) { - if (gettingStartedPlzLogin === void 0) { gettingStartedPlzLogin = "Please login to get started"; } - if (welcomeBackMessage === void 0) { welcomeBackMessage = "You are logged in. Welcome back!"; } - if (welcomeTail === void 0) { welcomeTail = ""; } - this.title = title; - this.startLon = startLon; - this.startLat = startLat; - this.startzoom = startzoom; - this.name = name; - this.layers = layers; - this.welcomeMessage = welcomeMessage; - this.gettingStartedPlzLogin = gettingStartedPlzLogin; - this.welcomeBackMessage = welcomeBackMessage; - this.welcomeTail = welcomeTail; - } - return Layout; -}()); -exports.Layout = Layout; diff --git a/Customizations/Layouts/All.js b/Customizations/Layouts/All.js deleted file mode 100644 index b03e05a..0000000 --- a/Customizations/Layouts/All.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.All = void 0; -var Layout_1 = require("../Layout"); -var All = /** @class */ (function (_super) { - __extends(All, _super); - function All() { - return _super.call(this, "all", "All quest layers", [], 15, 51.2, 3.2, "<h3>All quests of MapComplete</h3>" + - "This is a mixed bag. Some quests might be hard or for experts to answer only", "Please log in", "") || this; - } - return All; -}(Layout_1.Layout)); -exports.All = All; diff --git a/Customizations/Layouts/Bookcases.js b/Customizations/Layouts/Bookcases.js deleted file mode 100644 index 86b30ce..0000000 --- a/Customizations/Layouts/Bookcases.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Bookcases = void 0; -var Layout_1 = require("../Layout"); -var Layer = require("../Layers/Bookcases"); -var Bookcases = /** @class */ (function (_super) { - __extends(Bookcases, _super); - function Bookcases() { - var _this = _super.call(this, "bookcases", "Open Bookcase Map", [new Layer.Bookcases()], 14, 51.2, 3.2, " <h3>Open BoekenkastjesKaart</h3>\n" + - "\n" + - "<p>" + - "Help mee met het creëeren van een volledige kaart met alle boekenruilkastjes!" + - "Een boekenruilkastje is een vaste plaats in publieke ruimte waar iedereen een boek in kan zetten of uit kan meenemen." + - "Meestal een klein kastje of doosje dat op straat staat, maar ook een oude telefooncellen of een schap in een station valt hieronder." + - "</p>", " <p>Begin met <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">het aanmaken van een account\n" + - " </a> of door je " + - " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">aan te melden</span>.</p>", "Klik op een boekenruilkastje om vragen te beantwoorden") || this; - _this.locationContains = ["Bookcases.html", "Bookcase.html", "bookcase"]; - return _this; - } - return Bookcases; -}(Layout_1.Layout)); -exports.Bookcases = Bookcases; diff --git a/Customizations/Layouts/Cyclofix.js b/Customizations/Layouts/Cyclofix.js deleted file mode 100644 index 80e1df7..0000000 --- a/Customizations/Layouts/Cyclofix.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var Layout_1 = require("../Layout"); -var BikeParkings_1 = require("../Layers/BikeParkings"); -var BikeStations_1 = require("../Layers/BikeStations"); -var GhostBike_1 = require("../Layers/GhostBike"); -var DrinkingWater_1 = require("../Layers/DrinkingWater"); -var Cyclofix = /** @class */ (function (_super) { - __extends(Cyclofix, _super); - function Cyclofix() { - return _super.call(this, "pomp", "Cyclofix bicycle infrastructure", [new GhostBike_1.GhostBike(), new BikeStations_1.default(), new BikeParkings_1.default(), new DrinkingWater_1.DrinkingWater()], 16, 50.8465573, 4.3516970, "<h3>Cyclofix bicycle infrastructure</h3>\n" + - "\n" + - "<p><b>EN></b> On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + - "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.</p>" + - "<p><b>NL></b> Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + - "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.</p>" + - "<p><b>FR></b> Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + - "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins.</p>", "", "") || this; - } - return Cyclofix; -}(Layout_1.Layout)); -exports.default = Cyclofix; diff --git a/Customizations/Layouts/GRB.js b/Customizations/Layouts/GRB.js deleted file mode 100644 index b6b741c..0000000 --- a/Customizations/Layouts/GRB.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GRB = void 0; -var Layout_1 = require("../Layout"); -var GrbToFix_1 = require("../Layers/GrbToFix"); -var GRB = /** @class */ (function (_super) { - __extends(GRB, _super); - function GRB() { - return _super.call(this, "grb", "Grb import fix tool", [new GrbToFix_1.GrbToFix()], 15, 51.2083, 3.2279, "<h3>GRB Fix tool</h3>\n" + - "\n" + - "Expert use only", "", "") || this; - } - return GRB; -}(Layout_1.Layout)); -exports.GRB = GRB; diff --git a/Customizations/Layouts/Groen.js b/Customizations/Layouts/Groen.js deleted file mode 100644 index 94033ee..0000000 --- a/Customizations/Layouts/Groen.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Groen = void 0; -var NatureReserves_1 = require("../Layers/NatureReserves"); -var Park_1 = require("../Layers/Park"); -var Bos_1 = require("../Layers/Bos"); -var Layout_1 = require("../Layout"); -var Groen = /** @class */ (function (_super) { - __extends(Groen, _super); - function Groen() { - var _this = _super.call(this, "buurtnatuur", "Buurtnatuur", [new NatureReserves_1.NatureReserves(), new Park_1.Park(), new Bos_1.Bos()], 10, 50.8435, 4.3688, "\n" + - "<img src='assets/groen.svg' alt='logo-groen' class='logo'> <br />" + - "<h3>Breng jouw buurtnatuur in kaart</h3>" + - "<b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten. \n" + - "<ul>" + - "<li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li>" + - "<li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li>" + - "<li>Op welke onbekende plekjes is het zalig spelen?</li>" + - "</ul>" + - "<p>Samen kleuren we heel Vlaanderen en Brussel groen.</p>" + - "<p>Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href=\"https://www.groen.be/buurtnatuur\" target='_blank'>meld je aan voor e-mailupdates</a>.</p> \n", "<b>Begin meteen door <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">een account te maken\n" + - " te maken</a> of\n" + - " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">in te loggen</span>.</b>", "", "<h4>Tips</h4>" + - "<ul>" + - "<li>Over groen ingekleurde gebieden weten we alles wat we willen weten.</li>" + - "<li>Bij rood ingekleurde gebieden ontbreekt nog heel wat info: klik een gebied aan en beantwoord de vragen.</li>" + - "<li>Je kan altijd een vraag overslaan als je het antwoord niet weet of niet zeker bent</li>" + - "<li>Je kan altijd een foto toevoegen</li>" + - "<li>Je kan ook zelf een gebied toevoegen door op de kaart te klikken</li>" + - "</ul>" + - "<small>" + - "<p>" + - "De oorspronkelijke data komt van <b>OpenStreetMap</b> en je antwoorden worden daar bewaard.<br/> Omdat iedereen vrij kan meewerken aan dit project, kunnen we niet garanderen dat er geen fouten opduiken." + - "</p>" + - "Je privacy is belangrijk. We tellen wel hoeveel gebruikers deze website bezoeken. We plaatsen een cookie waar geen persoonlijke informatie in bewaard wordt. " + - "Als je inlogt, komt er een tweede cookie bij met je inloggegevens." + - "</small>") || this; - _this.locationContains = ["buurtnatuur.be"]; - return _this; - } - return Groen; -}(Layout_1.Layout)); -exports.Groen = Groen; diff --git a/Customizations/Layouts/MetaMap.js b/Customizations/Layouts/MetaMap.js deleted file mode 100644 index 65d4762..0000000 --- a/Customizations/Layouts/MetaMap.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MetaMap = void 0; -var Layout_1 = require("../Layout"); -var Map_1 = require("../Layers/Map"); -var MetaMap = /** @class */ (function (_super) { - __extends(MetaMap, _super); - function MetaMap() { - return _super.call(this, "metamap", "Open Map Map", [new Map_1.Map()], 1, 0, 0, " <h3>Open Map Map</h3>\n" + - "This map is a map of physical maps, as known by OpenStreetMap.") || this; - } - return MetaMap; -}(Layout_1.Layout)); -exports.MetaMap = MetaMap; diff --git a/Customizations/Layouts/Natuurpunt.js b/Customizations/Layouts/Natuurpunt.js deleted file mode 100644 index 51ee110..0000000 --- a/Customizations/Layouts/Natuurpunt.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Natuurpunt = void 0; -var Layout_1 = require("../Layout"); -var Birdhide_1 = require("../Layers/Birdhide"); -var InformationBoard_1 = require("../Layers/InformationBoard"); -var NatureReserves_1 = require("../Layers/NatureReserves"); -var Natuurpunt = /** @class */ (function (_super) { - __extends(Natuurpunt, _super); - function Natuurpunt() { - return _super.call(this, "natuurpunt", "De natuur in", [new Birdhide_1.Birdhide(), new InformationBoard_1.InformationBoard(), new NatureReserves_1.NatureReserves(true)], 12, 51.20875, 3.22435, "<h3>Natuurpuntstuff</h3>", "", "") || this; - } - return Natuurpunt; -}(Layout_1.Layout)); -exports.Natuurpunt = Natuurpunt; diff --git a/Customizations/Layouts/Statues.js b/Customizations/Layouts/Statues.js deleted file mode 100644 index fbcd44e..0000000 --- a/Customizations/Layouts/Statues.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Statues = void 0; -var Layout_1 = require("../Layout"); -var Artwork_1 = require("../Layers/Artwork"); -var Statues = /** @class */ (function (_super) { - __extends(Statues, _super); - function Statues() { - return _super.call(this, "statues", "Open Artwork Map", [new Artwork_1.Artwork()], 10, 50.8435, 4.3688, " <h3>Open Statue Map</h3>\n" + - "\n" + - "<p>" + - "Help with creating a map of all statues all over the world!", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + - " </a> or by " + - " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; - } - return Statues; -}(Layout_1.Layout)); -exports.Statues = Statues; diff --git a/Customizations/Layouts/StreetWidth.js b/Customizations/Layouts/StreetWidth.js deleted file mode 100644 index 3afdca1..0000000 --- a/Customizations/Layouts/StreetWidth.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StreetWidth = void 0; -var Layout_1 = require("../Layout"); -var Widths_1 = require("../Layers/Widths"); -var StreetWidth = /** @class */ (function (_super) { - __extends(StreetWidth, _super); - function StreetWidth() { - return _super.call(this, "width", "Straatbreedtes in Brugge", [new Widths_1.Widths(2, 1.5, 0.75)], 15, 51.20875, 3.22435, "<h3>De straat is opgebruikt</h3>" + - "<p>Er is steeds meer druk op de openbare ruimte. Voetgangers, fietsers, steps, auto's, bussen, bestelwagens, buggies, cargobikes, ... willen allemaal hun deel van de openbare ruimte.</p>" + - "" + - "<p>In deze studie nemen we Brugge onder de loep en kijken we hoe breed elke straat is én hoe breed elke straat zou moeten zijn voor een veilig én vlot verkeer.</p>" + - "Verschillende ingrepen kunnen de stad teruggeven aan de inwoners en de stad leefbaarder en levendiger maken.<br/>" + - "Denk aan:" + - "<ul>" + - "<li>De autovrije zone's uitbreiden</li>" + - "<li>De binnenstad fietszone maken</li>" + - "<li>Het aantal woonerven uitbreiden</li>" + - "<li>Grotere auto's meer belasten - ze nemen immers meer parkeerruimte in.</li>" + - "</ul>", "", "") || this; - } - return StreetWidth; -}(Layout_1.Layout)); -exports.StreetWidth = StreetWidth; diff --git a/Customizations/Layouts/Toilets.js b/Customizations/Layouts/Toilets.js deleted file mode 100644 index 1caedb9..0000000 --- a/Customizations/Layouts/Toilets.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Toilets = void 0; -var Layout_1 = require("../Layout"); -var Layer = require("../Layers/Toilets"); -var Toilets = /** @class */ (function (_super) { - __extends(Toilets, _super); - function Toilets() { - return _super.call(this, "toilets", "Open Toilet Map", [new Layer.Toilets()], 12, 51.2, 3.2, " <h3>Open Toilet Map</h3>\n" + - "\n" + - "<p>Help us to create the most complete map about <i>all</i> the toilets in the world, based on openStreetMap." + - "One can answer questions here, which help users all over the world to find an accessible toilet, close to them.</p>", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + - " </a> or by " + - " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; - } - return Toilets; -}(Layout_1.Layout)); -exports.Toilets = Toilets; diff --git a/Customizations/Layouts/WalkByBrussels.js b/Customizations/Layouts/WalkByBrussels.js deleted file mode 100644 index 8b4f711..0000000 --- a/Customizations/Layouts/WalkByBrussels.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WalkByBrussels = void 0; -var Layout_1 = require("../Layout"); -var DrinkingWater_1 = require("../Layers/DrinkingWater"); -var NatureReserves_1 = require("../Layers/NatureReserves"); -var Park_1 = require("../Layers/Park"); -var WalkByBrussels = /** @class */ (function (_super) { - __extends(WalkByBrussels, _super); - function WalkByBrussels() { - return _super.call(this, "walkbybrussels", "Drinking Water Spots", [new DrinkingWater_1.DrinkingWater(), new Park_1.Park(), new NatureReserves_1.NatureReserves()], 10, 50.8435, 4.3688, " <h3>Drinking water</h3>\n" + - "\n" + - "<p>" + - "Help with creating a map of drinking water points!", " <p>Start by <a href=\"https://www.openstreetmap.org/user/new\" target=\"_blank\">creating an account\n" + - " </a> or by " + - " <span onclick=\"authOsm()\" class=\"activate-osm-authentication\">logging in</span>.</p>", "Start by clicking a pin and answering the questions") || this; - } - return WalkByBrussels; -}(Layout_1.Layout)); -exports.WalkByBrussels = WalkByBrussels; diff --git a/Customizations/OnlyShowIf.js b/Customizations/OnlyShowIf.js deleted file mode 100644 index e1d4c5b..0000000 --- a/Customizations/OnlyShowIf.js +++ /dev/null @@ -1,89 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OnlyShowIfConstructor = void 0; -var TagsFilter_1 = require("../Logic/TagsFilter"); -var UIElement_1 = require("../UI/UIElement"); -var OnlyShowIfConstructor = /** @class */ (function () { - function OnlyShowIfConstructor(tagsFilter, embedded) { - this._tagsFilter = tagsFilter; - this._embedded = embedded; - } - OnlyShowIfConstructor.prototype.construct = function (dependencies) { - return new OnlyShowIf(dependencies.tags, this._embedded.construct(dependencies), this._tagsFilter); - }; - OnlyShowIfConstructor.prototype.IsKnown = function (properties) { - if (!this.Matches(properties)) { - return true; - } - return this._embedded.IsKnown(properties); - }; - OnlyShowIfConstructor.prototype.IsQuestioning = function (properties) { - if (!this.Matches(properties)) { - return false; - } - return this._embedded.IsQuestioning(properties); - }; - OnlyShowIfConstructor.prototype.Priority = function () { - return this._embedded.Priority(); - }; - OnlyShowIfConstructor.prototype.Matches = function (properties) { - return this._tagsFilter.matches(TagsFilter_1.TagUtils.proprtiesToKV(properties)); - }; - return OnlyShowIfConstructor; -}()); -exports.OnlyShowIfConstructor = OnlyShowIfConstructor; -var OnlyShowIf = /** @class */ (function (_super) { - __extends(OnlyShowIf, _super); - function OnlyShowIf(tags, embedded, filter) { - var _this = _super.call(this, tags) || this; - _this._filter = filter; - _this._embedded = embedded; - return _this; - } - OnlyShowIf.prototype.Matches = function () { - return this._filter.matches(TagsFilter_1.TagUtils.proprtiesToKV(this._source.data)); - }; - OnlyShowIf.prototype.InnerRender = function () { - if (this.Matches()) { - return this._embedded.Render(); - } - else { - return ""; - } - }; - OnlyShowIf.prototype.Priority = function () { - return this._embedded.Priority(); - }; - OnlyShowIf.prototype.IsKnown = function () { - if (!this.Matches()) { - return false; - } - return this._embedded.IsKnown(); - }; - OnlyShowIf.prototype.IsQuestioning = function () { - if (!this.Matches()) { - return false; - } - return this._embedded.IsQuestioning(); - }; - OnlyShowIf.prototype.Activate = function () { - this._embedded.Activate(); - }; - OnlyShowIf.prototype.Update = function () { - this._embedded.Update(); - }; - return OnlyShowIf; -}(UIElement_1.UIElement)); diff --git a/Customizations/Questions/AccessTag.js b/Customizations/Questions/AccessTag.js deleted file mode 100644 index b8901fd..0000000 --- a/Customizations/Questions/AccessTag.js +++ /dev/null @@ -1,50 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AccessTag = void 0; -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var AccessTag = /** @class */ (function (_super) { - __extends(AccessTag, _super); - function AccessTag() { - return _super.call(this, AccessTag.options) || this; - } - AccessTag.options = { - priority: 20, - question: "Is dit gebied toegankelijk?", - primer: "Dit gebied is ", - freeform: { - key: "access:description", - template: "Iets anders: $$$", - renderTemplate: "De toegankelijkheid van dit gebied is: {access:description}", - placeholder: "Specifieer" - }, - mappings: [ - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "yes"), new TagsFilter_1.Tag("fee", "")]), txt: "publiek toegankelijk" }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "no"), new TagsFilter_1.Tag("fee", "")]), txt: "niet toegankelijk" }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "private"), new TagsFilter_1.Tag("fee", "")]), txt: "niet toegankelijk, want privegebied" }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "permissive"), new TagsFilter_1.Tag("fee", "")]), txt: "toegankelijk, maar het is privegebied" }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "guided"), new TagsFilter_1.Tag("fee", "")]), txt: "enkel met gids of op activiteit" }, - { - k: new TagsFilter_1.And([new TagsFilter_1.Tag("access", "yes"), - new TagsFilter_1.Tag("fee", "yes")]), - txt: "toegankelijk mits betaling", - priority: 10 - }, - ] - }; - return AccessTag; -}(TagRendering_1.TagRenderingOptions)); -exports.AccessTag = AccessTag; diff --git a/Customizations/Questions/DescriptionQuestion.js b/Customizations/Questions/DescriptionQuestion.js deleted file mode 100644 index a88781f..0000000 --- a/Customizations/Questions/DescriptionQuestion.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DescriptionQuestion = void 0; -var TagRendering_1 = require("../TagRendering"); -var DescriptionQuestion = /** @class */ (function (_super) { - __extends(DescriptionQuestion, _super); - function DescriptionQuestion(category) { - return _super.call(this, { - question: "Zijn er bijzonderheden die we moeten weten over dit " + category + "?<br>" + - "<span class='question-subtext'>Je hoeft niet te herhalen wat je net hebt aangeduid.<br/>" + - "Een <i>naam</i> wordt in de volgende stap gevraagd.<br/>" + - "Voel je vrij om dit veld over te slaan.</span>", - freeform: { - key: "description:0", - renderTemplate: "{description:0}", - template: "$$$" - }, - priority: 14 - }) || this; - } - return DescriptionQuestion; -}(TagRendering_1.TagRenderingOptions)); -exports.DescriptionQuestion = DescriptionQuestion; diff --git a/Customizations/Questions/FixedText.js b/Customizations/Questions/FixedText.js deleted file mode 100644 index ab46069..0000000 --- a/Customizations/Questions/FixedText.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../TagRendering"); -var FixedText = /** @class */ (function (_super) { - __extends(FixedText, _super); - function FixedText(category) { - return _super.call(this, { - mappings: [ - { - k: null, txt: category - } - ] - }) || this; - } - return FixedText; -}(TagRendering_1.TagRenderingOptions)); -exports.default = FixedText; diff --git a/Customizations/Questions/NameInline.js b/Customizations/Questions/NameInline.js deleted file mode 100644 index 5192935..0000000 --- a/Customizations/Questions/NameInline.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.NameInline = void 0; -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var NameInline = /** @class */ (function (_super) { - __extends(NameInline, _super); - function NameInline(category) { - return _super.call(this, { - question: "", - freeform: { - renderTemplate: "{name}", - template: "De naam van dit " + category + " is $$$", - key: "name", - extraTags: new TagsFilter_1.Tag("noname", "") // Remove 'noname=yes' - }, - mappings: [ - { k: new TagsFilter_1.Tag("noname", "yes"), txt: NameInline.Upper(category) + " zonder naam" }, - { k: null, txt: NameInline.Upper(category) } - ] - }) || this; - } - NameInline.Upper = function (string) { - return string.charAt(0).toUpperCase() + string.slice(1); - }; - return NameInline; -}(TagRendering_1.TagRenderingOptions)); -exports.NameInline = NameInline; diff --git a/Customizations/Questions/NameQuestion.js b/Customizations/Questions/NameQuestion.js deleted file mode 100644 index 477039e..0000000 --- a/Customizations/Questions/NameQuestion.js +++ /dev/null @@ -1,48 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.NameQuestion = void 0; -/** - * There are two ways to ask for names: - * One is a big 'name-question', the other is the 'edit name' in the title. - * THis one is the big question - */ -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var NameQuestion = /** @class */ (function (_super) { - __extends(NameQuestion, _super); - function NameQuestion() { - return _super.call(this, NameQuestion.options) || this; - } - NameQuestion.options = { - priority: 10, - question: "Wat is de <i>officiële</i> naam van dit gebied?<br><span class='question-subtext'>" + - "Zelf een naam bedenken wordt afgeraden.<br/>" + - "Een beschrijving van het gebied geven kan in een volgende stap.<br/>" + - "</span>", - freeform: { - key: "name", - template: "De naam is $$$", - renderTemplate: "", - placeholder: "", - extraTags: new TagsFilter_1.Tag("noname", "") - }, - mappings: [ - { k: new TagsFilter_1.Tag("noname", "yes"), txt: "Dit gebied heeft geen naam" }, - ] - }; - return NameQuestion; -}(TagRendering_1.TagRenderingOptions)); -exports.NameQuestion = NameQuestion; diff --git a/Customizations/Questions/OperatorTag.js b/Customizations/Questions/OperatorTag.js deleted file mode 100644 index 9f7f720..0000000 --- a/Customizations/Questions/OperatorTag.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OperatorTag = void 0; -var TagRendering_1 = require("../TagRendering"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var OperatorTag = /** @class */ (function (_super) { - __extends(OperatorTag, _super); - function OperatorTag() { - return _super.call(this, OperatorTag.options) || this; - } - OperatorTag.options = { - priority: 15, - question: "Wie beheert dit gebied?", - freeform: { - key: "operator", - template: "Dit gebied wordt beheerd door $$$", - renderTemplate: "Dit gebied wordt beheerd door {operator}", - placeholder: "organisatie" - }, - mappings: [ - { k: new TagsFilter_1.Tag("operator", "Natuurpunt"), txt: "Natuurpunt" }, - { k: new TagsFilter_1.Tag("operator", "Agentschap Natuur en Bos"), txt: "het Agentschap Natuur en Bos (ANB)" }, - { k: new TagsFilter_1.Tag("operator", "private"), txt: "Beheer door een privépersoon" } - ] - }; - return OperatorTag; -}(TagRendering_1.TagRenderingOptions)); -exports.OperatorTag = OperatorTag; diff --git a/Customizations/Questions/OsmLink.js b/Customizations/Questions/OsmLink.js deleted file mode 100644 index e552f58..0000000 --- a/Customizations/Questions/OsmLink.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OsmLink = void 0; -var TagRendering_1 = require("../TagRendering"); -var Img_1 = require("../../UI/Img"); -var TagsFilter_1 = require("../../Logic/TagsFilter"); -var OsmLink = /** @class */ (function (_super) { - __extends(OsmLink, _super); - function OsmLink() { - return _super.call(this, OsmLink.options) || this; - } - OsmLink.options = { - freeform: { - key: "id", - template: "$$$", - renderTemplate: "<span class='osmlink'><a href='https://osm.org/{id}' target='_blank'>" + - Img_1.Img.osmAbstractLogo + - "</a></span>", - placeholder: "", - }, - mappings: [ - { k: new TagsFilter_1.Tag("id", "node/-1"), txt: "<span class='alert'>Uploading</span>" } - ] - }; - return OsmLink; -}(TagRendering_1.TagRenderingOptions)); -exports.OsmLink = OsmLink; diff --git a/Customizations/Questions/WikipediaLink.js b/Customizations/Questions/WikipediaLink.js deleted file mode 100644 index 3ae56a2..0000000 --- a/Customizations/Questions/WikipediaLink.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WikipediaLink = void 0; -var TagRendering_1 = require("../TagRendering"); -var WikipediaLink = /** @class */ (function (_super) { - __extends(WikipediaLink, _super); - function WikipediaLink() { - return _super.call(this, WikipediaLink.options) || this; - } - WikipediaLink.FixLink = function (value) { - if (value === undefined) { - return undefined; - } - // @ts-ignore - if (value.startsWith("https")) { - return value; - } - else { - var splitted = value.split(":"); - var language = splitted[0]; - splitted.shift(); - var page = splitted.join(":"); - return 'https://' + language + '.wikipedia.org/wiki/' + page; - } - }; - WikipediaLink.options = { - priority: 10, - // question: "Wat is het overeenstemmende wkipedia-artikel?", - tagsPreprocessor: function (tags) { - if (tags.wikipedia !== undefined) { - tags.wikipedia = WikipediaLink.FixLink(tags.wikipedia); - } - }, - freeform: { - key: "wikipedia", - template: "$$$", - renderTemplate: "<span class='wikipedialink'>" + - "<a href='{wikipedia}' target='_blank'>" + - "<img width='64px' src='./assets/wikipedia.svg' alt='wikipedia'>" + - "</a></span>", - placeholder: "" - }, - }; - return WikipediaLink; -}(TagRendering_1.TagRenderingOptions)); -exports.WikipediaLink = WikipediaLink; diff --git a/Customizations/Questions/bike/ParkingType.js b/Customizations/Questions/bike/ParkingType.js deleted file mode 100644 index 2b54c21..0000000 --- a/Customizations/Questions/bike/ParkingType.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var ParkingType = /** @class */ (function (_super) { - __extends(ParkingType, _super); - function ParkingType() { - return _super.call(this, { - priority: 5, - question: "Van welk type is deze fietsenparking?", - freeform: { - key: "bicycle_parking", - extraTags: new TagsFilter_1.Tag("fixme", "Freeform bicycle_parking= tag used: possibly a wrong value"), - template: "Iets anders: $$$", - renderTemplate: "Dit is een fietsenparking van het type: {bicycle_parking}", - placeholder: "Specifieer" - }, - mappings: [ - { k: new TagsFilter_1.Tag("bicycle_parking", "stands"), txt: ParkingType.toImgTxt(ParkingType.images.stands) }, - { k: new TagsFilter_1.Tag("bicycle_parking", "wall_loops"), txt: ParkingType.toImgTxt(ParkingType.images.wall_loops) }, - { k: new TagsFilter_1.Tag("bicycle_parking", "handlebar_holder"), txt: ParkingType.toImgTxt(ParkingType.images.handlebar_holder) }, - { k: new TagsFilter_1.Tag("bicycle_parking", "shed"), txt: ParkingType.toImgTxt(ParkingType.images.shed) }, - { k: new TagsFilter_1.Tag("bicycle_parking", "two-tier"), txt: ParkingType.toImgTxt(ParkingType.images["two-tier"]) } - ] - }) || this; - } - ParkingType.toImgTxt = function (url) { - return "<img src=" + url + ">"; - }; - ParkingType.images = { - stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", - wall_loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", - handlebar_holder: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", - shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", - "two-tier": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" - }; - return ParkingType; -}(TagRendering_1.TagRenderingOptions)); -exports.default = ParkingType; diff --git a/Customizations/Questions/bike/PumpManometer.js b/Customizations/Questions/bike/PumpManometer.js deleted file mode 100644 index c79d20a..0000000 --- a/Customizations/Questions/bike/PumpManometer.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var PumpManometer = /** @class */ (function (_super) { - __extends(PumpManometer, _super); - function PumpManometer() { - return _super.call(this, { - question: "Does the pump have a pressure indicator or manometer?", - mappings: [ - { k: new TagsFilter_1.Tag("manometer", "yes"), txt: "Yes, there is a manometer" }, - { k: new TagsFilter_1.Tag("manometer", "broken"), txt: "Yes, but it is broken" }, - { k: new TagsFilter_1.Tag("manometer", "yes"), txt: "No" } - ] - }) || this; - } - return PumpManometer; -}(TagRendering_1.TagRenderingOptions)); -exports.default = PumpManometer; diff --git a/Customizations/Questions/bike/PumpManual.js b/Customizations/Questions/bike/PumpManual.js deleted file mode 100644 index bfd34cc..0000000 --- a/Customizations/Questions/bike/PumpManual.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var PumpManual = /** @class */ (function (_super) { - __extends(PumpManual, _super); - function PumpManual() { - return _super.call(this, { - priority: 5, - question: "Is this an electric bike pump?", - mappings: [ - { k: new TagsFilter_1.Tag("manual", "yes"), txt: "Manual pump" }, - { k: new TagsFilter_1.Tag("manual", "no"), txt: "Electric pump" } - ] - }) || this; - } - return PumpManual; -}(TagRendering_1.TagRenderingOptions)); -exports.default = PumpManual; diff --git a/Customizations/Questions/bike/PumpOperational.js b/Customizations/Questions/bike/PumpOperational.js deleted file mode 100644 index ffd30e9..0000000 --- a/Customizations/Questions/bike/PumpOperational.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var PumpOperational = /** @class */ (function (_super) { - __extends(PumpOperational, _super); - function PumpOperational() { - return _super.call(this, { - question: "Is the bicycle pump still operational?", - mappings: [ - { k: new TagsFilter_1.Tag("service:bicycle:pump:operational_status", "broken"), txt: "This pump is broken" }, - { k: new TagsFilter_1.Tag("service:bicycle:pump:operational_status", ""), txt: "This pump is operational" } - ] - }) || this; - } - return PumpOperational; -}(TagRendering_1.TagRenderingOptions)); -exports.default = PumpOperational; diff --git a/Customizations/Questions/bike/PumpValves.js b/Customizations/Questions/bike/PumpValves.js deleted file mode 100644 index 65d1240..0000000 --- a/Customizations/Questions/bike/PumpValves.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var PumpValves = /** @class */ (function (_super) { - __extends(PumpValves, _super); - function PumpValves() { - return _super.call(this, { - question: "What valves are supported?", - mappings: [ - { - k: new TagsFilter_1.Tag("valves", " sclaverand;schrader;dunlop"), - txt: "There is a default head, so Presta, Dunlop and Auto" - }, - { k: new TagsFilter_1.Tag("valves", "dunlop"), txt: "Only dunlop" }, - { k: new TagsFilter_1.Tag("valves", "sclaverand"), txt: "Only Sclaverand (also known as Dunlop)" }, - { k: new TagsFilter_1.Tag("valves", "auto"), txt: "Only auto" }, - ], - freeform: { - key: "valves", - template: "Supported valves are $$$", - renderTemplate: "Supported valves are {valves}" - } - }) || this; - } - return PumpValves; -}(TagRendering_1.TagRenderingOptions)); -exports.default = PumpValves; diff --git a/Customizations/Questions/bike/StationBrand.js b/Customizations/Questions/bike/StationBrand.js deleted file mode 100644 index b5d36bd..0000000 --- a/Customizations/Questions/bike/StationBrand.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -/** - * Currently not used in Cyclofix because it's a little vague - */ -var BikeStationBrand = /** @class */ (function (_super) { - __extends(BikeStationBrand, _super); - function BikeStationBrand() { - var _this = this; - throw Error('BikeStationBrand disabled'); - _this = _super.call(this, BikeStationBrand.options) || this; - return _this; - } - BikeStationBrand.options = { - priority: 15, - question: "What is the brand of this bike station (name of university, shop, city...)?", - freeform: { - key: "brand", - template: "The brand of this bike station is $$$", - renderTemplate: "The brand of this bike station is {operator}", - placeholder: "brand" - }, - mappings: [ - { k: new TagsFilter_1.Tag("brand", "Velo Fix Station"), txt: "Velo Fix Station" } - ] - }; - return BikeStationBrand; -}(TagRendering_1.TagRenderingOptions)); -exports.default = BikeStationBrand; diff --git a/Customizations/Questions/bike/StationChain.js b/Customizations/Questions/bike/StationChain.js deleted file mode 100644 index ca34b0e..0000000 --- a/Customizations/Questions/bike/StationChain.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var StationChain = /** @class */ (function (_super) { - __extends(StationChain, _super); - function StationChain() { - return _super.call(this, { - priority: 5, - question: "Does this bike station have a special tool to repair your bike chain?", - mappings: [ - { k: new TagsFilter_1.Tag("service:bicycle:chain_tool", "yes"), txt: "There is a chain tool." }, - { k: new TagsFilter_1.Tag("service:bicycle:chain_tool", "no"), txt: "There is no chain tool." }, - ] - }) || this; - } - return StationChain; -}(TagRendering_1.TagRenderingOptions)); -exports.default = StationChain; diff --git a/Customizations/Questions/bike/StationOperator.js b/Customizations/Questions/bike/StationOperator.js deleted file mode 100644 index 02f6b09..0000000 --- a/Customizations/Questions/bike/StationOperator.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var BikeStationOperator = /** @class */ (function (_super) { - __extends(BikeStationOperator, _super); - function BikeStationOperator() { - return _super.call(this, { - priority: 15, - question: "Who operates this bike station (name of university, shop, city...)?", - freeform: { - key: "operator", - template: "This bike station is operated by $$$", - renderTemplate: "This bike station is operated by {operator}", - placeholder: "organisatie" - }, - mappings: [ - { k: new TagsFilter_1.Tag("operator", "KU Leuven"), txt: "KU Leuven" }, - { k: new TagsFilter_1.Tag("operator", "Stad Halle"), txt: "Stad Halle" }, - { k: new TagsFilter_1.Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis" }, - { k: new TagsFilter_1.Tag("operator", "Jette"), txt: "Jette" }, - { k: new TagsFilter_1.Tag("operator", "private"), txt: "Beheer door een privépersoon" } - ] - }) || this; - } - return BikeStationOperator; -}(TagRendering_1.TagRenderingOptions)); -exports.default = BikeStationOperator; diff --git a/Customizations/Questions/bike/StationPumpTools.js b/Customizations/Questions/bike/StationPumpTools.js deleted file mode 100644 index 2db65ab..0000000 --- a/Customizations/Questions/bike/StationPumpTools.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var BikeStationPumpTools = /** @class */ (function (_super) { - __extends(BikeStationPumpTools, _super); - function BikeStationPumpTools() { - return _super.call(this, { - priority: 15, - question: "Which services are available at this bike station?", - mappings: [ - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "no"), new TagsFilter_1.Tag("service:bicycle:pump", "yes")]), txt: "There is only a pump available." }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "yes"), new TagsFilter_1.Tag("service:bicycle:pump", "no")]), txt: "There are only tools (screwdrivers, pliers...) available." }, - { k: new TagsFilter_1.And([new TagsFilter_1.Tag("service:bicycle:tools", "yes"), new TagsFilter_1.Tag("service:bicycle:pump", "yes")]), txt: "There are both tools and a pump available." } - ] - }) || this; - } - return BikeStationPumpTools; -}(TagRendering_1.TagRenderingOptions)); -exports.default = BikeStationPumpTools; diff --git a/Customizations/Questions/bike/StationStand.js b/Customizations/Questions/bike/StationStand.js deleted file mode 100644 index 55f607a..0000000 --- a/Customizations/Questions/bike/StationStand.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var TagRendering_1 = require("../../TagRendering"); -var TagsFilter_1 = require("../../../Logic/TagsFilter"); -var BikeStationStand = /** @class */ (function (_super) { - __extends(BikeStationStand, _super); - function BikeStationStand() { - return _super.call(this, { - priority: 10, - question: "Does this bike station have a hook to suspend your bike with or a stand to elevate it?", - mappings: [ - { k: new TagsFilter_1.Tag("service:bicycle:stand", "yes"), txt: "There is a hook or stand." }, - { k: new TagsFilter_1.Tag("service:bicycle:stand", "no"), txt: "There is no hook or stand" }, - ] - }) || this; - } - return BikeStationStand; -}(TagRendering_1.TagRenderingOptions)); -exports.default = BikeStationStand; diff --git a/Customizations/TagRendering.js b/Customizations/TagRendering.js deleted file mode 100644 index d8f2f83..0000000 --- a/Customizations/TagRendering.js +++ /dev/null @@ -1,321 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TagRenderingOptions = void 0; -var UIElement_1 = require("../UI/UIElement"); -var UIEventSource_1 = require("../UI/UIEventSource"); -var TagsFilter_1 = require("../Logic/TagsFilter"); -var FixedUiElement_1 = require("../UI/Base/FixedUiElement"); -var SaveButton_1 = require("../UI/SaveButton"); -var VariableUIElement_1 = require("../UI/Base/VariableUIElement"); -var OnlyShowIf_1 = require("./OnlyShowIf"); -var TextField_1 = require("../UI/Input/TextField"); -var InputElementWrapper_1 = require("../UI/Input/InputElementWrapper"); -var FixedInputElement_1 = require("../UI/Input/FixedInputElement"); -var RadioButton_1 = require("../UI/Input/RadioButton"); -var TagRenderingOptions = /** @class */ (function () { - function TagRenderingOptions(options) { - this.options = options; - } - TagRenderingOptions.prototype.OnlyShowIf = function (tagsFilter) { - return new OnlyShowIf_1.OnlyShowIfConstructor(tagsFilter, this); - }; - TagRenderingOptions.prototype.IsQuestioning = function (tags) { - var _a; - var tagsKV = TagsFilter_1.TagUtils.proprtiesToKV(tags); - for (var _i = 0, _b = (_a = this.options.mappings) !== null && _a !== void 0 ? _a : []; _i < _b.length; _i++) { - var oneOnOneElement = _b[_i]; - if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tagsKV)) { - return false; - } - } - if (this.options.freeform !== undefined && tags[this.options.freeform.key] !== undefined) { - return false; - } - if (this.options.question === undefined) { - return false; - } - return true; - }; - TagRenderingOptions.prototype.construct = function (dependencies) { - return new TagRendering(dependencies.tags, dependencies.changes, this.options); - }; - TagRenderingOptions.prototype.IsKnown = function (properties) { - return !this.IsQuestioning(properties); - }; - TagRenderingOptions.prototype.Priority = function () { - var _a; - return (_a = this.options.priority) !== null && _a !== void 0 ? _a : 0; - }; - return TagRenderingOptions; -}()); -exports.TagRenderingOptions = TagRenderingOptions; -var TagRendering = /** @class */ (function (_super) { - __extends(TagRendering, _super); - function TagRendering(tags, changes, options) { - var _a, _b, _c; - var _this = _super.call(this, tags) || this; - _this._questionSkipped = new UIEventSource_1.UIEventSource(false); - _this._editMode = new UIEventSource_1.UIEventSource(false); - var self = _this; - _this.ListenTo(_this._questionSkipped); - _this.ListenTo(_this._editMode); - _this._userDetails = changes.login.userDetails; - _this.ListenTo(_this._userDetails); - _this._question = options.question; - _this._priority = (_a = options.priority) !== null && _a !== void 0 ? _a : 0; - _this._primer = (_b = options.primer) !== null && _b !== void 0 ? _b : ""; - _this._tagsPreprocessor = function (properties) { - if (options.tagsPreprocessor === undefined) { - return properties; - } - var newTags = {}; - for (var k in properties) { - newTags[k] = properties[k]; - } - options.tagsPreprocessor(newTags); - return newTags; - }; - _this._mapping = []; - _this._renderMapping = []; - _this._freeform = options.freeform; - // Prepare the choices for the Radio buttons - var choices = []; - var usedChoices = []; - for (var _i = 0, _d = (_c = options.mappings) !== null && _c !== void 0 ? _c : []; _i < _d.length; _i++) { - var choice = _d[_i]; - if (choice.k === null) { - _this._mapping.push(choice); - continue; - } - var choiceSubbed = choice; - if (choice.substitute) { - choiceSubbed = { - k: choice.k.substituteValues(options.tagsPreprocessor(_this._source.data)), - txt: _this.ApplyTemplate(choice.txt), - substitute: false, - priority: choice.priority - }; - } - var txt = choiceSubbed.txt; - // Choices is what is shown in the radio buttons - if (usedChoices.indexOf(txt) < 0) { - choices.push(new FixedUiElement_1.FixedUiElement(txt)); - usedChoices.push(txt); - // This is used to convert the radio button index into tags needed to add - _this._mapping.push(choiceSubbed); - } - else { - _this._renderMapping.push(choiceSubbed); // only used while rendering - } - } - // Prepare the actual input element -> pick an appropriate implementation - _this._questionElement = _this.InputElementFor(options); - var save = function () { - var selection = self._questionElement.GetValue().data; - if (selection) { - changes.addTag(tags.data.id, selection); - } - self._editMode.setData(false); - }; - var cancel = function () { - self._questionSkipped.setData(true); - self._editMode.setData(false); - self._source.ping(); // Send a ping upstream to render the next question - }; - // Setup the save button and it's action - _this._saveButton = new SaveButton_1.SaveButton(_this._questionElement.GetValue()) - .onClick(save); - _this._editButton = new FixedUiElement_1.FixedUiElement(""); - if (_this._question !== undefined) { - _this._editButton = new FixedUiElement_1.FixedUiElement("<img class='editbutton' src='./assets/pencil.svg' alt='edit'>") - .onClick(function () { - self._questionElement.GetValue().setData(self.CurrentValue()); - self._editMode.setData(true); - }); - } - var cancelContents = _this._editMode.map(function (isEditing) { - if (isEditing) { - return "<span class='skip-button'>Annuleren</span>"; - } - else { - return "<span class='skip-button'>Overslaan (Ik weet het niet zeker...)</span>"; - } - }); - // And at last, set up the skip button - _this._skipButton = new VariableUIElement_1.VariableUiElement(cancelContents).onClick(cancel); - return _this; - } - TagRendering.prototype.InputElementFor = function (options) { - var elements = []; - if (options.mappings !== undefined) { - for (var _i = 0, _a = options.mappings; _i < _a.length; _i++) { - var mapping = _a[_i]; - elements.push(this.InputElementForMapping(mapping)); - } - } - if (options.freeform !== undefined) { - elements.push(this.InputForFreeForm(options.freeform)); - } - if (elements.length == 0) { - throw "NO TAGRENDERINGS!"; - } - if (elements.length == 1) { - return elements[0]; - } - return new RadioButton_1.RadioButton(elements, false); - }; - TagRendering.prototype.InputElementForMapping = function (mapping) { - return new FixedInputElement_1.FixedInputElement(mapping.txt, mapping.k); - }; - TagRendering.prototype.InputForFreeForm = function (freeform) { - if (freeform === undefined) { - return undefined; - } - var pickString = function (string) { - if (string === "" || string === undefined) { - return undefined; - } - var tag = new TagsFilter_1.Tag(freeform.key, string); - if (freeform.extraTags === undefined) { - return tag; - } - return new TagsFilter_1.And([ - freeform.extraTags, - tag - ]); - }; - var toString = function (tag) { - if (tag instanceof TagsFilter_1.And) { - return toString(tag.and[0]); - } - else if (tag instanceof TagsFilter_1.Tag) { - return tag.value; - } - return undefined; - }; - var inputElement; - var textField = new TextField_1.TextField({ - placeholder: this._freeform.placeholder, - fromString: pickString, - toString: toString - }); - var prepost = freeform.template.split("$$$"); - return new InputElementWrapper_1.InputElementWrapper(prepost[0], textField, prepost[1]); - }; - TagRendering.prototype.IsKnown = function () { - var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); - for (var _i = 0, _a = this._mapping.concat(this._renderMapping); _i < _a.length; _i++) { - var oneOnOneElement = _a[_i]; - if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { - return true; - } - } - return this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined; - }; - TagRendering.prototype.CurrentValue = function () { - var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); - for (var _i = 0, _a = this._mapping.concat(this._renderMapping); _i < _a.length; _i++) { - var oneOnOneElement = _a[_i]; - if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { - return oneOnOneElement.k; - } - } - if (this._freeform === undefined) { - return undefined; - } - return new TagsFilter_1.Tag(this._freeform.key, this._source.data[this._freeform.key]); - }; - TagRendering.prototype.IsQuestioning = function () { - if (this.IsKnown()) { - return false; - } - if (this._question === undefined) { - // We don't ask this question in the first place - return false; - } - if (this._questionSkipped.data) { - // We don't ask for this question anymore, skipped by user - return false; - } - return true; - }; - TagRendering.prototype.RenderAnwser = function () { - var _a; - var tags = TagsFilter_1.TagUtils.proprtiesToKV(this._source.data); - var freeform = ""; - var freeformScore = -10; - if (this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined) { - freeform = this.ApplyTemplate(this._freeform.renderTemplate); - freeformScore = 0; - } - var highestScore = -100; - var highestTemplate = undefined; - for (var _i = 0, _b = this._mapping.concat(this._renderMapping); _i < _b.length; _i++) { - var oneOnOneElement = _b[_i]; - if (oneOnOneElement.k == null || - oneOnOneElement.k.matches(tags)) { - // We have found a matching key -> we use the template, but only if it scores better - var score = (_a = oneOnOneElement.priority) !== null && _a !== void 0 ? _a : (oneOnOneElement.k === null ? -1 : 0); - if (score > highestScore) { - highestScore = score; - highestTemplate = oneOnOneElement.txt; - } - } - } - if (freeformScore > highestScore) { - return freeform; - } - if (highestTemplate !== undefined) { - // we render the found template - return this._primer + this.ApplyTemplate(highestTemplate); - } - }; - TagRendering.prototype.InnerRender = function () { - if (this.IsQuestioning() || this._editMode.data) { - // Not yet known or questioning, we have to ask a question - return "<div class='question'>" + - "<span class='question-text'>" + this._question + "</span>" + - (this._question !== "" ? "<br/>" : "") + - this._questionElement.Render() + - this._skipButton.Render() + - this._saveButton.Render() + - "</div>"; - } - if (this.IsKnown()) { - var html = this.RenderAnwser(); - if (html == "") { - return ""; - } - var editButton = ""; - if (this._userDetails.data.loggedIn) { - editButton = this._editButton.Render(); - } - return "<span class='answer'>" + - "<span class='answer-text'>" + html + "</span>" + - editButton + - "</span>"; - } - return ""; - }; - TagRendering.prototype.Priority = function () { - return this._priority; - }; - TagRendering.prototype.ApplyTemplate = function (template) { - var tags = this._tagsPreprocessor(this._source.data); - return TagsFilter_1.TagUtils.ApplyTemplate(template, tags); - }; - return TagRendering; -}(UIElement_1.UIElement)); diff --git a/Customizations/UIElementConstructor.js b/Customizations/UIElementConstructor.js deleted file mode 100644 index 620512d..0000000 --- a/Customizations/UIElementConstructor.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TagDependantUIElement = void 0; -var UIElement_1 = require("../UI/UIElement"); -var TagDependantUIElement = /** @class */ (function (_super) { - __extends(TagDependantUIElement, _super); - function TagDependantUIElement() { - return _super !== null && _super.apply(this, arguments) || this; - } - return TagDependantUIElement; -}(UIElement_1.UIElement)); -exports.TagDependantUIElement = TagDependantUIElement; diff --git a/Helpers.js b/Helpers.js deleted file mode 100644 index e86bd37..0000000 --- a/Helpers.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Helpers = void 0; -var Helpers = /** @class */ (function () { - function Helpers() { - } - Helpers.DoEvery = function (millis, f) { - window.setTimeout(function () { - f(); - Helpers.DoEvery(millis, f); - }, millis); - }; - Helpers.SetupAutoSave = function (changes, millisTillChangesAreSaved, saveAfterXMillis) { - changes.pendingChangesES.addCallback(function () { - var c = changes.pendingChangesES.data; - if (c > 10) { - millisTillChangesAreSaved.setData(0); - changes.uploadAll(undefined); - return; - } - if (c > 0) { - millisTillChangesAreSaved.setData(saveAfterXMillis); - } - }); - millisTillChangesAreSaved.addCallback(function (time) { - if (time <= 0 && changes.pendingChangesES.data > 0) { - changes.uploadAll(undefined); - } - }); - Helpers.DoEvery(1000, function () { - millisTillChangesAreSaved - .setData(millisTillChangesAreSaved.data - 1000); - }); - }; - /* - * Registers an action that: - * -> Upload everything to OSM - * -> Asks the user not to close. The 'not to close' dialog should profide enough time to upload - * -> WHen uploading is done, the window is closed anyway - */ - Helpers.LastEffortSave = function (changes) { - window.addEventListener("beforeunload", function (e) { - // Quickly save everyting! - if (changes.pendingChangesES.data == 0) { - return ""; - } - changes.uploadAll(function () { - window.close(); - }); - var confirmationMessage = "Nog even geduld - je laatset wijzigingen worden opgeslaan!"; - (e || window.event).returnValue = confirmationMessage; //Gecko + IE - return confirmationMessage; //Webkit, Safari, Chrome - }); - }; - return Helpers; -}()); -exports.Helpers = Helpers; diff --git a/Quests.js b/Quests.js deleted file mode 100644 index 4c68f4b..0000000 --- a/Quests.js +++ /dev/null @@ -1,40 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Quests = void 0; -var Question_1 = require("./Logic/Question"); -var Quests = /** @class */ (function () { - function Quests() { - } - Quests.nameOf = function (name) { - return Question_1.QuestionDefinition.noNameOrNameQuestion("<b>Wat is de <i>officiële</i> naam van dit " + name + "?</b><br />" + - "Veel gebieden hebben geen naam. Duid dit dan ook zo aan.", "Dit " + name + " heeft geen naam", 20); - }; - Quests.hasFee = Question_1.QuestionDefinition.radioQuestionSimple("Moet men betalen om deze toiletten te gebruiken?", 10, "fee", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]); - Quests.toiletsWheelChairs = Question_1.QuestionDefinition.radioQuestionSimple("Zijn deze toiletten rolstoeltoegankelijk?", 20, "wheelchair", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]).addUnrequiredTag("toilets:position", "urinals"); - Quests.toiletsChangingTable = Question_1.QuestionDefinition.radioQuestionSimple("Is er een luiertafel beschikbaar?", 20, "changing_table", [{ text: "ja", value: "yes" }, { text: "nee", value: "no" }]) - // Urinals are often a pitlatrine/something very poor where no changing table is - .addUnrequiredTag("toilets:position", "urinals").addUnrequiredTag("toilets:position", "urinal"); - Quests.toiletsChangingTableLocation = Question_1.QuestionDefinition.radioAndTextQuestion("Waar bevindt de luiertafel zich?", 5, "changing_table", [{ text: "In de vrouwentoiletten", value: "female_toilet" }, - { text: "In de mannentoiletten", value: "male_toilet" }, - { text: "In de rolstoeltoegangkelijke toiletten", value: "wheelchair_toilet" }, - { text: "In de aparte, speciaal voorziene ruimte", value: "dedicated_room" }, - { text: "In de genderneutrale toiletten", value: "unisex_toilet" }]) - .addRequiredTag("changing_table", "yes"); - Quests.toiletsPosition = Question_1.QuestionDefinition.radioQuestionSimple("Wat voor toiletten zijn dit?", 1, "toilets:position", [{ text: "Enkel urinoirs", value: "urinals" }, - { text: "Enkel 'gewone' toiletten waar men op gaat zitten", value: "seated" }, - { text: "Er zijn zowel urinoirs als zittoiletten", value: "seated;urinals" }]); - Quests.accessNatureReserve = Question_1.QuestionDefinition.radioQuestionSimple("Is dit gebied toegankelijk voor het publiek?", 10, "access", [ - { text: "Nee, dit is afgesloten", value: "no" }, - { text: "Nee, dit is een privaat terrein", value: "no" }, - { text: "Hoewel het een privebos is, kan men er toch in", value: "permissive" }, - { text: "Enkel tijdens activiteiten of met een gids", value: "guided" }, - { text: "Ja, het is gewoon toegankelijk", value: "yes" } - ]).addUnrequiredTag("seamark:type", "restricted_area"); - Quests.operator = Question_1.QuestionDefinition.radioAndTextQuestion("Wie is de beheerder van dit gebied?", 1, "operator", [{ text: "Natuurpunt", value: "Natuurpunt" }, - { text: "Het Agenschap voor Natuur en Bos", value: "Agentschap Natuur en Bos" }, - { text: "Een prive-eigenaar", value: "private" } - ]).addUnrequiredTag("access", "private") - .addUnrequiredTag("access", "no"); - return Quests; -}()); -exports.Quests = Quests; diff --git a/UI/AddButton.js b/UI/AddButton.js deleted file mode 100644 index 68ce701..0000000 --- a/UI/AddButton.js +++ /dev/null @@ -1,129 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AddButton = void 0; -var UIEventSource_1 = require("./UIEventSource"); -var UIElement_1 = require("./UIElement"); -var AddButton = /** @class */ (function (_super) { - __extends(AddButton, _super); - function AddButton(basemap, changes, options) { - var _this = _super.call(this, undefined) || this; - _this.curentAddSelection = new UIEventSource_1.UIEventSource(""); - _this.SELECTING_POI = "selecting_POI"; - _this.PLACING_POI = "placing_POI"; - /*State is one of: - * "": the default stated - * "select_POI": show a 'select which POI to add' query (skipped if only one option exists) - * "placing_point": shown while adding a point - * "" - */ - _this.state = new UIEventSource_1.UIEventSource(""); - _this.zoomlevel = basemap.Location; - _this.ListenTo(_this.zoomlevel); - _this._options = options; - _this.ListenTo(_this.curentAddSelection); - _this.ListenTo(_this.state); - _this.state.setData(_this.SELECTING_POI); - _this.changes = changes; - var self = _this; - basemap.map.on("click", function (e) { - var location = e.latlng; - console.log("Clicked at ", location); - self.HandleClick(location.lat, location.lng); - }); - basemap.map.on("mousemove", function () { - if (self.state.data === self.PLACING_POI) { - var icon = "crosshair"; - for (var _i = 0, _a = self._options; _i < _a.length; _i++) { - var option = _a[_i]; - if (option.name === self.curentAddSelection.data && option.icon !== undefined) { - icon = 'url("' + option.icon + '") 32 32 ,crosshair'; - console.log("Cursor icon: ", icon); - } - } - document.getElementById('leafletDiv').style.cursor = icon; - } - else { - // @ts-ignore - document.getElementById('leafletDiv').style.cursor = ''; - } - }); - return _this; - } - AddButton.prototype.HandleClick = function (lat, lon) { - this.state.setData(this.SELECTING_POI); - console.log("Handling click", lat, lon, this.curentAddSelection.data); - for (var _i = 0, _a = this._options; _i < _a.length; _i++) { - var option = _a[_i]; - if (this.curentAddSelection.data === option.name) { - console.log("PLACING a ", option); - var feature = this.changes.createElement(option.tags, lat, lon); - option.layerToAddTo.AddNewElement(feature); - return; - } - } - }; - AddButton.prototype.InnerRender = function () { - if (this.zoomlevel.data.zoom < 19) { - return "Zoom in om een punt toe te voegen"; - } - if (this.state.data === this.SELECTING_POI) { - var html = "<form>"; - for (var _i = 0, _a = this._options; _i < _a.length; _i++) { - var option = _a[_i]; - // <button type='button'> looks SO retarded - // the default type of button is 'submit', which performs a POST and page reload - html += "<button type='button' class='addPOIoption' value='" + option.name + "'>Voeg een " + option.name + " toe</button><br/>"; - } - html += "</form>"; - return html; - } - if (this.state.data === this.PLACING_POI) { - return "<div id='clickOnMapInstruction'>Klik op de kaart om een nieuw punt toe te voegen<div>" + - "<div id='cancelInstruction'>Klik hier om toevoegen te annuleren</div>"; - } - if (this.curentAddSelection.data === "") { - return "<span onclick>Voeg een punt toe...</span>"; - } - return "Annuleer"; - }; - AddButton.prototype.InnerUpdate = function (htmlElement) { - var self = this; - htmlElement.onclick = function (event) { - // @ts-ignore - if (event.consumed) { - return; - } - if (self.state.data === self.PLACING_POI) { - self.state.setData(self.SELECTING_POI); - } - }; - var buttons = htmlElement.getElementsByClassName('addPOIoption'); - var _loop_1 = function (button) { - button.onclick = function (event) { - self.curentAddSelection.setData(button.value); - self.state.setData(self.PLACING_POI); - event.consumed = true; - }; - }; - // @ts-ignore - for (var _i = 0, buttons_1 = buttons; _i < buttons_1.length; _i++) { - var button = buttons_1[_i]; - _loop_1(button); - } - }; - return AddButton; -}(UIElement_1.UIElement)); -exports.AddButton = AddButton; diff --git a/UI/Base/Button.js b/UI/Base/Button.js deleted file mode 100644 index c9b0b4a..0000000 --- a/UI/Base/Button.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Button = void 0; -var UIElement_1 = require("../UIElement"); -var Button = /** @class */ (function (_super) { - __extends(Button, _super); - function Button(text, onclick, clss) { - if (clss === void 0) { clss = ""; } - var _this = _super.call(this, undefined) || this; - _this._text = text; - _this._onclick = onclick; - if (clss !== "") { - _this._clss = "class='" + clss + "'"; - } - else { - _this._clss = ""; - } - return _this; - } - Button.prototype.InnerRender = function () { - return "<form>" + - "<button id='button-" + this.id + "' type='button' " + this._clss + ">" + this._text.Render() + "</button>" + - "</form>"; - }; - Button.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - var self = this; - console.log("Update for ", htmlElement); - document.getElementById("button-" + this.id).onclick = function () { - console.log("Clicked"); - self._onclick(); - }; - }; - return Button; -}(UIElement_1.UIElement)); -exports.Button = Button; diff --git a/UI/Base/CollapseButton.js b/UI/Base/CollapseButton.js deleted file mode 100644 index e82b9fb..0000000 --- a/UI/Base/CollapseButton.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CollapseButton = void 0; -var UIElement_1 = require("../UIElement"); -var UIEventSource_1 = require("../UIEventSource"); -var CollapseButton = /** @class */ (function (_super) { - __extends(CollapseButton, _super); - function CollapseButton(idToCollapse) { - var _this = _super.call(this, undefined) || this; - _this.isCollapsed = new UIEventSource_1.UIEventSource(false); - _this.ListenTo(_this.isCollapsed); - _this.isCollapsed.addCallback(function (collapse) { - var el = document.getElementById(idToCollapse); - if (el === undefined || el === null) { - console.log("Element not found"); - return; - } - if (collapse) { - el.style.height = "3.5em"; - el.style.width = "15em"; - } - else { - el.style.height = "auto"; - el.style.width = "auto"; - } - }); - var self = _this; - _this.onClick(function () { - self.isCollapsed.setData(!self.isCollapsed.data); - }); - return _this; - } - CollapseButton.prototype.InnerRender = function () { - var up = './assets/arrow-up.svg'; - var down = './assets/arrow-down.svg'; - var arrow = up; - if (this.isCollapsed.data) { - arrow = down; - } - return "<img class='collapse-button' src='" + arrow + "' alt='collapse'>"; - }; - return CollapseButton; -}(UIElement_1.UIElement)); -exports.CollapseButton = CollapseButton; diff --git a/UI/Base/FixedUiElement.js b/UI/Base/FixedUiElement.js deleted file mode 100644 index 4029e7f..0000000 --- a/UI/Base/FixedUiElement.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FixedUiElement = void 0; -var UIElement_1 = require("../UIElement"); -var FixedUiElement = /** @class */ (function (_super) { - __extends(FixedUiElement, _super); - function FixedUiElement(html) { - var _this = _super.call(this, undefined) || this; - _this._html = html !== null && html !== void 0 ? html : ""; - return _this; - } - FixedUiElement.prototype.InnerRender = function () { - return this._html; - }; - return FixedUiElement; -}(UIElement_1.UIElement)); -exports.FixedUiElement = FixedUiElement; diff --git a/UI/Base/VariableUIElement.js b/UI/Base/VariableUIElement.js deleted file mode 100644 index 8f2dd28..0000000 --- a/UI/Base/VariableUIElement.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VariableUiElement = void 0; -var UIElement_1 = require("../UIElement"); -var VariableUiElement = /** @class */ (function (_super) { - __extends(VariableUiElement, _super); - function VariableUiElement(html, innerUpdate) { - if (innerUpdate === void 0) { innerUpdate = undefined; } - var _this = _super.call(this, html) || this; - _this._html = html; - _this._innerUpdate = innerUpdate; - return _this; - } - VariableUiElement.prototype.InnerRender = function () { - return this._html.data; - }; - VariableUiElement.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - if (this._innerUpdate !== undefined) { - this._innerUpdate(htmlElement); - } - }; - return VariableUiElement; -}(UIElement_1.UIElement)); -exports.VariableUiElement = VariableUiElement; diff --git a/UI/Base/VerticalCombine.js b/UI/Base/VerticalCombine.js deleted file mode 100644 index 3207e90..0000000 --- a/UI/Base/VerticalCombine.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VerticalCombine = void 0; -var UIElement_1 = require("../UIElement"); -var VerticalCombine = /** @class */ (function (_super) { - __extends(VerticalCombine, _super); - function VerticalCombine(elements, className) { - if (className === void 0) { className = undefined; } - var _this = _super.call(this, undefined) || this; - _this._elements = elements; - _this._className = className; - return _this; - } - VerticalCombine.prototype.InnerRender = function () { - var html = ""; - for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { - var element = _a[_i]; - if (!element.IsEmpty()) { - html += "<div>" + element.Render() + "</div>"; - } - } - if (html === "") { - return ""; - } - if (this._className === undefined) { - return html; - } - return "<div class='" + this._className + "'>" + html + "</div>"; - }; - return VerticalCombine; -}(UIElement_1.UIElement)); -exports.VerticalCombine = VerticalCombine; diff --git a/UI/CenterMessageBox.js b/UI/CenterMessageBox.js deleted file mode 100644 index 4def75c..0000000 --- a/UI/CenterMessageBox.js +++ /dev/null @@ -1,72 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CenterMessageBox = void 0; -var UIElement_1 = require("./UIElement"); -var UIEventSource_1 = require("./UIEventSource"); -var CenterMessageBox = /** @class */ (function (_super) { - __extends(CenterMessageBox, _super); - function CenterMessageBox(startZoom, centermessage, osmConnection, location, queryRunning) { - var _this = _super.call(this, centermessage) || this; - _this._zoomInMore = new UIEventSource_1.UIEventSource(true); - _this._centermessage = centermessage; - _this._location = location; - _this._osmConnection = osmConnection; - _this._queryRunning = queryRunning; - _this.ListenTo(queryRunning); - var self = _this; - location.addCallback(function () { - self._zoomInMore.setData(location.data.zoom < startZoom); - }); - _this.ListenTo(_this._zoomInMore); - return _this; - } - CenterMessageBox.prototype.InnerRender = function () { - if (this._centermessage.data != "") { - return this._centermessage.data; - } - if (this._queryRunning.data) { - return "Data wordt geladen..."; - } - else if (this._zoomInMore.data) { - return "Zoom in om de data te zien en te bewerken"; - } - return "Klaar!"; - }; - CenterMessageBox.prototype.ShouldShowSomething = function () { - if (this._queryRunning.data) { - return true; - } - return this._zoomInMore.data; - }; - CenterMessageBox.prototype.InnerUpdate = function (htmlElement) { - var pstyle = htmlElement.parentElement.style; - if (this._centermessage.data != "") { - pstyle.opacity = "1"; - pstyle.pointerEvents = "all"; - this._osmConnection.registerActivateOsmAUthenticationClass(); - return; - } - pstyle.pointerEvents = "none"; - if (this.ShouldShowSomething()) { - pstyle.opacity = "0.5"; - } - else { - pstyle.opacity = "0"; - } - }; - return CenterMessageBox; -}(UIElement_1.UIElement)); -exports.CenterMessageBox = CenterMessageBox; diff --git a/UI/ConfirmDialog.js b/UI/ConfirmDialog.js deleted file mode 100644 index 8e2a46f..0000000 --- a/UI/ConfirmDialog.js +++ /dev/null @@ -1,67 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ConfirmDialog = void 0; -var UIElement_1 = require("./UIElement"); -var UIEventSource_1 = require("./UIEventSource"); -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var VariableUIElement_1 = require("./Base/VariableUIElement"); -var ConfirmDialog = /** @class */ (function (_super) { - __extends(ConfirmDialog, _super); - function ConfirmDialog(show, question, optionA, optionB, executeA, executeB, classA, classB) { - if (classA === void 0) { classA = ""; } - if (classB === void 0) { classB = ""; } - var _this = _super.call(this, show) || this; - _this._showOptions = new UIEventSource_1.UIEventSource(false); - _this.ListenTo(_this._showOptions); - var self = _this; - show.addCallback(function () { - self._showOptions.setData(false); - }); - _this._question = new FixedUiElement_1.FixedUiElement("<span class='ui-question'>" + question + "</span>") - .onClick(function () { - self._showOptions.setData(!self._showOptions.data); - }); - _this._optionA = new VariableUIElement_1.VariableUiElement(_this._showOptions.map(function (show) { return show ? "<div class='" + classA + "'>" + optionA + "</div>" : ""; })) - .onClick(function () { - self._showOptions.setData(false); - executeA(); - }); - _this._optionB = new VariableUIElement_1.VariableUiElement(_this._showOptions.map(function (show) { - return show ? "<div class='" + classB + "'>" + optionB + "</div>" : ""; - })) - .onClick(function () { - self._showOptions.setData(false); - executeB(); - }); - return _this; - } - ConfirmDialog.prototype.InnerRender = function () { - if (!this._source.data) { - return ""; - } - return this._question.Render() + - this._optionA.Render() + - this._optionB.Render(); - }; - ConfirmDialog.prototype.Update = function () { - _super.prototype.Update.call(this); - this._question.Update(); - this._optionA.Update(); - this._optionB.Update(); - }; - return ConfirmDialog; -}(UIElement_1.UIElement)); -exports.ConfirmDialog = ConfirmDialog; diff --git a/UI/FeatureInfoBox.js b/UI/FeatureInfoBox.js deleted file mode 100644 index 768b4e3..0000000 --- a/UI/FeatureInfoBox.js +++ /dev/null @@ -1,89 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FeatureInfoBox = void 0; -var UIElement_1 = require("./UIElement"); -var VerticalCombine_1 = require("./Base/VerticalCombine"); -var TagRendering_1 = require("../Customizations/TagRendering"); -var OsmLink_1 = require("../Customizations/Questions/OsmLink"); -var WikipediaLink_1 = require("../Customizations/Questions/WikipediaLink"); -var TagsFilter_1 = require("../Logic/TagsFilter"); -var FeatureInfoBox = /** @class */ (function (_super) { - __extends(FeatureInfoBox, _super); - function FeatureInfoBox(tagsES, title, elementsToShow, changes, userDetails) { - var _this = _super.call(this, tagsES) || this; - _this._tagsES = tagsES; - _this._changes = changes; - _this._userDetails = userDetails; - _this.ListenTo(userDetails); - var deps = { tags: _this._tagsES, changes: _this._changes }; - _this._infoboxes = []; - elementsToShow = elementsToShow !== null && elementsToShow !== void 0 ? elementsToShow : []; - for (var _i = 0, elementsToShow_1 = elementsToShow; _i < elementsToShow_1.length; _i++) { - var tagRenderingOption = elementsToShow_1[_i]; - _this._infoboxes.push(tagRenderingOption.construct(deps)); - } - title = title !== null && title !== void 0 ? title : new TagRendering_1.TagRenderingOptions({ - mappings: [{ k: new TagsFilter_1.And([]), txt: "" }] - }); - _this._title = new TagRendering_1.TagRenderingOptions(title.options).construct(deps); - _this._osmLink = new OsmLink_1.OsmLink().construct(deps); - _this._wikipedialink = new WikipediaLink_1.WikipediaLink().construct(deps); - return _this; - } - FeatureInfoBox.prototype.InnerRender = function () { - var info = []; - var questions = []; - for (var _i = 0, _a = this._infoboxes; _i < _a.length; _i++) { - var infobox = _a[_i]; - if (infobox.IsKnown()) { - info.push(infobox); - } - else if (infobox.IsQuestioning()) { - questions.push(infobox); - } - } - var questionsHtml = ""; - if (this._userDetails.data.loggedIn && questions.length > 0) { - // We select the most important question and render that one - var mostImportantQuestion = void 0; - var score = -1000; - for (var _b = 0, questions_1 = questions; _b < questions_1.length; _b++) { - var question = questions_1[_b]; - if (mostImportantQuestion === undefined || question.Priority() > score) { - mostImportantQuestion = question; - score = question.Priority(); - } - } - questionsHtml = mostImportantQuestion.Render(); - } - return "<div class='featureinfobox'>" + - "<div class='featureinfoboxtitle'>" + - "<span>" + - this._title.Render() + - "</span>" + - this._wikipedialink.Render() + - this._osmLink.Render() + - "</div>" + - "<div class='infoboxcontents'>" + - new VerticalCombine_1.VerticalCombine(info, "infobox-information ").Render() + - questionsHtml + - "</div>" + - "" + - "</div>"; - }; - return FeatureInfoBox; -}(UIElement_1.UIElement)); -exports.FeatureInfoBox = FeatureInfoBox; diff --git a/UI/Image/ImageCarousel.js b/UI/Image/ImageCarousel.js deleted file mode 100644 index 9009daf..0000000 --- a/UI/Image/ImageCarousel.js +++ /dev/null @@ -1,112 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageCarousel = exports.ImageCarouselConstructor = void 0; -var ImageSearcher_1 = require("../../Logic/ImageSearcher"); -var SlideShow_1 = require("../SlideShow"); -var FixedUiElement_1 = require("../Base/FixedUiElement"); -var VariableUIElement_1 = require("../Base/VariableUIElement"); -var ConfirmDialog_1 = require("../ConfirmDialog"); -var UIElementConstructor_1 = require("../../Customizations/UIElementConstructor"); -var ImageCarouselConstructor = /** @class */ (function () { - function ImageCarouselConstructor() { - } - ImageCarouselConstructor.prototype.IsKnown = function (properties) { - return true; - }; - ImageCarouselConstructor.prototype.IsQuestioning = function (properties) { - return false; - }; - ImageCarouselConstructor.prototype.Priority = function () { - return 0; - }; - ImageCarouselConstructor.prototype.construct = function (tags, changes) { - return new ImageCarousel(tags, changes); - }; - return ImageCarouselConstructor; -}()); -exports.ImageCarouselConstructor = ImageCarouselConstructor; -var ImageCarousel = /** @class */ (function (_super) { - __extends(ImageCarousel, _super); - function ImageCarousel(tags, changes) { - var _this = _super.call(this, tags) || this; - _this._userDetails = changes.login.userDetails; - var self = _this; - _this.searcher = new ImageSearcher_1.ImageSearcher(tags, changes); - _this._uiElements = _this.searcher.map(function (imageURLS) { - var uiElements = []; - for (var _i = 0, imageURLS_1 = imageURLS; _i < imageURLS_1.length; _i++) { - var url = imageURLS_1[_i]; - var image = ImageSearcher_1.ImageSearcher.CreateImageElement(url); - uiElements.push(image); - } - return uiElements; - }); - _this.slideshow = new SlideShow_1.SlideShow(_this._uiElements, new FixedUiElement_1.FixedUiElement("")).HideOnEmpty(true); - var showDeleteButton = _this.slideshow._currentSlide.map(function (i) { - if (!self._userDetails.data.loggedIn) { - return false; - } - return self.searcher.IsDeletable(self.searcher.data[i]); - }, [_this.searcher, _this._userDetails]); - _this.slideshow._currentSlide.addCallback(function () { - showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons - }); - var deleteCurrent = function () { - self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]); - }; - _this._deleteButton = new ConfirmDialog_1.ConfirmDialog(showDeleteButton, "<img src='assets/delete.svg' alt='Afbeelding verwijderen' class='delete-image'>", "<span>Afbeelding verwijderen</span>", "<span>Terug</span>", deleteCurrent, function () { }, 'delete-image-confirm', 'delete-image-cancel'); - var mapping = _this.slideshow._currentSlide.map(function (i) { - if (_this.searcher._deletedImages.data.indexOf(_this.searcher.data[i]) >= 0) { - return "<div class='image-is-removed'>Deze afbeelding is verwijderd</div>"; - } - return ""; - }); - _this._isDeleted = new VariableUIElement_1.VariableUiElement(mapping); - _this.searcher._deletedImages.addCallback(function () { - _this.slideshow._currentSlide.ping(); - }); - return _this; - } - ImageCarousel.prototype.InnerRender = function () { - return "<span class='image-carousel-container'>" + - "<div class='image-delete-container'>" + - this._deleteButton.Render() + - this._isDeleted.Render() + - "</div>" + - this.slideshow.Render() + - "</span>"; - }; - ImageCarousel.prototype.IsKnown = function () { - return true; - }; - ImageCarousel.prototype.IsQuestioning = function () { - return false; - }; - ImageCarousel.prototype.Priority = function () { - return 0; - }; - ImageCarousel.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - this._deleteButton.Update(); - this._isDeleted.Update(); - }; - ImageCarousel.prototype.Activate = function () { - _super.prototype.Activate.call(this); - this.searcher.Activate(); - }; - return ImageCarousel; -}(UIElementConstructor_1.TagDependantUIElement)); -exports.ImageCarousel = ImageCarousel; diff --git a/UI/Image/ImageCarouselWithUpload.js b/UI/Image/ImageCarouselWithUpload.js deleted file mode 100644 index 3138457..0000000 --- a/UI/Image/ImageCarouselWithUpload.js +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageCarouselWithUploadConstructor = void 0; -var UIElementConstructor_1 = require("../../Customizations/UIElementConstructor"); -var ImageCarousel_1 = require("./ImageCarousel"); -var OsmImageUploadHandler_1 = require("../../Logic/OsmImageUploadHandler"); -var ImageCarouselWithUploadConstructor = /** @class */ (function () { - function ImageCarouselWithUploadConstructor() { - } - ImageCarouselWithUploadConstructor.prototype.IsKnown = function (properties) { - return true; - }; - ImageCarouselWithUploadConstructor.prototype.IsQuestioning = function (properties) { - return false; - }; - ImageCarouselWithUploadConstructor.prototype.Priority = function () { - return 0; - }; - ImageCarouselWithUploadConstructor.prototype.construct = function (dependencies) { - return new ImageCarouselWithUpload(dependencies); - }; - return ImageCarouselWithUploadConstructor; -}()); -exports.ImageCarouselWithUploadConstructor = ImageCarouselWithUploadConstructor; -var ImageCarouselWithUpload = /** @class */ (function (_super) { - __extends(ImageCarouselWithUpload, _super); - function ImageCarouselWithUpload(dependencies) { - var _this = _super.call(this, dependencies.tags) || this; - var tags = dependencies.tags; - var changes = dependencies.changes; - _this._imageElement = new ImageCarousel_1.ImageCarousel(tags, changes); - var userDetails = changes.login.userDetails; - var license = changes.login.GetPreference("mapcomplete-pictures-license"); - _this._pictureUploader = new OsmImageUploadHandler_1.OsmImageUploadHandler(tags, userDetails, license, changes, _this._imageElement.slideshow).getUI(); - return _this; - } - ImageCarouselWithUpload.prototype.InnerRender = function () { - return this._imageElement.Render() + - this._pictureUploader.Render(); - }; - ImageCarouselWithUpload.prototype.Activate = function () { - _super.prototype.Activate.call(this); - this._imageElement.Activate(); - this._pictureUploader.Activate(); - }; - ImageCarouselWithUpload.prototype.Update = function () { - _super.prototype.Update.call(this); - this._imageElement.Update(); - this._pictureUploader.Update(); - }; - ImageCarouselWithUpload.prototype.IsKnown = function () { - return true; - }; - ImageCarouselWithUpload.prototype.IsQuestioning = function () { - return false; - }; - ImageCarouselWithUpload.prototype.Priority = function () { - return 0; - }; - return ImageCarouselWithUpload; -}(UIElementConstructor_1.TagDependantUIElement)); diff --git a/UI/Image/ImgurImage.js b/UI/Image/ImgurImage.js deleted file mode 100644 index ae98025..0000000 --- a/UI/Image/ImgurImage.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImgurImage = void 0; -var UIEventSource_1 = require("../UIEventSource"); -var UIElement_1 = require("../UIElement"); -var Imgur_1 = require("../../Logic/Imgur"); -var ImgurImage = /** @class */ (function (_super) { - __extends(ImgurImage, _super); - function ImgurImage(source) { - var _this = _super.call(this, undefined) || this; - _this._imageLocation = source; - if (ImgurImage.allLicenseInfos[source] !== undefined) { - _this._imageMeta = ImgurImage.allLicenseInfos[source]; - } - else { - _this._imageMeta = new UIEventSource_1.UIEventSource(null); - ImgurImage.allLicenseInfos[source] = _this._imageMeta; - var self_1 = _this; - Imgur_1.Imgur.getDescriptionOfImage(source, function (license) { - self_1._imageMeta.setData(license); - }); - } - _this.ListenTo(_this._imageMeta); - return _this; - } - ImgurImage.prototype.InnerRender = function () { - var _a, _b; - var image = "<img src='" + this._imageLocation + "' " + "alt='' >"; - if (this._imageMeta.data === null) { - return image; - } - var attribution = "<span class='attribution-author'><b>" + ((_a = this._imageMeta.data.artist) !== null && _a !== void 0 ? _a : "") + "</b></span>" + " <span class='license'>" + ((_b = this._imageMeta.data.licenseShortName) !== null && _b !== void 0 ? _b : "") + "</span>"; - return "<div class='imgWithAttr'>" + - image + - "<div class='attribution'>" + - attribution + - "</div>" + - "</div>"; - }; - /*** - * Dictionary from url to alreayd known license info - */ - ImgurImage.allLicenseInfos = {}; - return ImgurImage; -}(UIElement_1.UIElement)); -exports.ImgurImage = ImgurImage; diff --git a/UI/Image/SimpleImageElement.js b/UI/Image/SimpleImageElement.js deleted file mode 100644 index 946e8a0..0000000 --- a/UI/Image/SimpleImageElement.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SimpleImageElement = void 0; -var UIElement_1 = require("../UIElement"); -var SimpleImageElement = /** @class */ (function (_super) { - __extends(SimpleImageElement, _super); - function SimpleImageElement(source) { - return _super.call(this, source) || this; - } - SimpleImageElement.prototype.InnerRender = function () { - return "<img src='" + this._source.data + "' alt='img'>"; - }; - return SimpleImageElement; -}(UIElement_1.UIElement)); -exports.SimpleImageElement = SimpleImageElement; diff --git a/UI/Image/WikimediaImage.js b/UI/Image/WikimediaImage.js deleted file mode 100644 index 7a25772..0000000 --- a/UI/Image/WikimediaImage.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WikimediaImage = void 0; -var UIEventSource_1 = require("../UIEventSource"); -var UIElement_1 = require("../UIElement"); -var Wikimedia_1 = require("../../Logic/Wikimedia"); -var WikimediaImage = /** @class */ (function (_super) { - __extends(WikimediaImage, _super); - function WikimediaImage(source) { - var _this = _super.call(this, undefined) || this; - _this._imageLocation = source; - if (WikimediaImage.allLicenseInfos[source] !== undefined) { - _this._imageMeta = WikimediaImage.allLicenseInfos[source]; - } - else { - _this._imageMeta = new UIEventSource_1.UIEventSource(new Wikimedia_1.LicenseInfo()); - WikimediaImage.allLicenseInfos[source] = _this._imageMeta; - var self_1 = _this; - Wikimedia_1.Wikimedia.LicenseData(source, function (info) { - self_1._imageMeta.setData(info); - }); - } - _this.ListenTo(_this._imageMeta); - return _this; - } - WikimediaImage.prototype.InnerRender = function () { - var _a, _b; - var url = Wikimedia_1.Wikimedia.ImageNameToUrl(this._imageLocation, 500, 400); - url = url.replace(/'/g, '%27'); - var wikimediaLink = "<a href='https://commons.wikimedia.org/wiki/" + this._imageLocation + "' target='_blank'>" + - "<img class='wikimedia-link' src='./assets/wikimedia-commons-white.svg' alt='Wikimedia Commons Logo'/>" + - "</a> "; - var attribution = "<span class='attribution-author'>" + ((_a = this._imageMeta.data.artist) !== null && _a !== void 0 ? _a : "") + "</span>" + " <span class='license'>" + ((_b = this._imageMeta.data.licenseShortName) !== null && _b !== void 0 ? _b : "") + "</span>"; - var image = "<img src='" + url + "' " + "alt='" + this._imageMeta.data.description + "' >"; - return "<div class='imgWithAttr'>" + - image + - "<div class='attribution'>" + - wikimediaLink + - attribution + - "</div>" + - "</div>"; - }; - WikimediaImage.allLicenseInfos = {}; - return WikimediaImage; -}(UIElement_1.UIElement)); -exports.WikimediaImage = WikimediaImage; diff --git a/UI/ImageUploadFlow.js b/UI/ImageUploadFlow.js deleted file mode 100644 index 469fc59..0000000 --- a/UI/ImageUploadFlow.js +++ /dev/null @@ -1,107 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageUploadFlow = void 0; -var UIElement_1 = require("./UIElement"); -var UIEventSource_1 = require("./UIEventSource"); -var jquery_1 = require("jquery"); -var Imgur_1 = require("../Logic/Imgur"); -var DropDown_1 = require("./Input/DropDown"); -var VariableUIElement_1 = require("./Base/VariableUIElement"); -var ImageUploadFlow = /** @class */ (function (_super) { - __extends(ImageUploadFlow, _super); - function ImageUploadFlow(userInfo, preferedLicense, uploadOptions) { - var _this = _super.call(this, undefined) || this; - _this._isUploading = new UIEventSource_1.UIEventSource(0); - _this._userdetails = userInfo; - _this.ListenTo(userInfo); - _this._uploadOptions = uploadOptions; - _this.ListenTo(_this._isUploading); - var licensePicker = new DropDown_1.DropDown("Jouw foto wordt gepubliceerd ", [ - { value: "CC0", shown: "in het publiek domein" }, - { value: "CC-BY-SA 4.0", shown: "onder een CC-BY-SA-licentie" }, - { value: "CC-BY 4.0", shown: "onder een CC-BY-licentie" } - ], preferedLicense); - _this._licensePicker = licensePicker; - _this._selectedLicence = licensePicker.selectedElement; - var licenseExplanations = { - "CC-BY-SA 4.0": "<b>Creative Commonse met naamsvermelding en gelijk delen</b><br/>" + - "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden én ze afgeleide werken met deze licentie en attributie delen.", - "CC-BY 4.0": "<b>Creative Commonse met naamsvermelding</b> <br/>" + - "Je foto mag door iedereen gratis gebruikt worden, als ze je naam vermelden", - "CC0": "<b>Geen copyright</b><br/> Je foto mag door iedereen voor alles gebruikt worden" - }; - _this._licenseExplanation = new VariableUIElement_1.VariableUiElement(_this._selectedLicence.map(function (license) { - return licenseExplanations[license]; - })); - return _this; - } - ImageUploadFlow.prototype.InnerRender = function () { - if (!this._userdetails.data.loggedIn) { - return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden</div>"; - } - if (this._isUploading.data == 1) { - return "<b>Bezig met een foto te uploaden...</b>"; - } - if (this._isUploading.data > 0) { - return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>"; - } - return "" + - "<div class='imageflow'>" + - "<label for='fileselector-" + this.id + "'>" + - "<div class='imageflow-file-input-wrapper'>" + - "<img src='./assets/camera-plus.svg' alt='upload image'/> " + - "<span class='imageflow-add-picture'>Voeg foto toe</span>" + - "<div class='break'></div>" + - "</div>" + - this._licensePicker.Render() + - "</label>" + - "<input id='fileselector-" + this.id + "' " + - "type='file' " + - "class='imageflow-file-input' " + - "accept='image/*' name='picField' size='24' multiple='multiple' alt=''" + - "/>" + - "</div>"; - }; - ImageUploadFlow.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - var user = this._userdetails.data; - htmlElement.onclick = function () { - if (!user.loggedIn) { - user.osmConnection.AttemptLogin(); - } - }; - this._licensePicker.Update(); - var selector = document.getElementById('fileselector-' + this.id); - var self = this; - if (selector != null) { - selector.onchange = function () { - var files = jquery_1.default(this).get(0).files; - self._isUploading.setData(files.length); - var opts = self._uploadOptions(self._selectedLicence.data); - Imgur_1.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"); - opts.allDone(); - }); - }; - } - }; - return ImageUploadFlow; -}(UIElement_1.UIElement)); -exports.ImageUploadFlow = ImageUploadFlow; diff --git a/UI/Img.js b/UI/Img.js deleted file mode 100644 index a88b7ed..0000000 --- a/UI/Img.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Img = void 0; -var Img = /** @class */ (function () { - function Img() { - } - Img.osmAbstractLogo = "<svg class='osm-logo' xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" width=\"24px\" version=\"1.1\" viewBox=\"0 0 66 64\">" + - " <g transform=\"translate(-0.849, -61)\">\n" + - " <path d=\"M0.849,61 L6.414,75.609 L0.849,90.217 L6.414,104.826 L0.849,119.435 L4.266,120.739 L22.831,102.183 L26.162,102.696 L30.205,98.652 C27.819,95.888 26.033,92.59 25.057,88.948 L26.953,87.391 C26.627,85.879 26.449,84.313 26.449,82.704 C26.449,74.67 30.734,67.611 37.136,63.696 L30.066,61 L15.457,66.565 L0.849,61 z\"></path>" + - " <path d=\"M48.71,64.617 C48.406,64.617 48.105,64.629 47.805,64.643 C47.52,64.657 47.234,64.677 46.953,64.704 C46.726,64.726 46.499,64.753 46.275,64.783 C46.039,64.814 45.811,64.847 45.579,64.887 C45.506,64.9 45.434,64.917 45.362,64.93 C45.216,64.958 45.072,64.987 44.927,65.017 C44.812,65.042 44.694,65.06 44.579,65.087 C44.442,65.119 44.307,65.156 44.17,65.191 C43.943,65.25 43.716,65.315 43.492,65.383 C43.323,65.433 43.155,65.484 42.988,65.539 C42.819,65.595 42.65,65.652 42.483,65.713 C42.475,65.716 42.466,65.719 42.457,65.722 C35.819,68.158 31.022,74.369 30.649,81.774 C30.633,82.083 30.622,82.391 30.622,82.704 C30.622,83.014 30.631,83.321 30.649,83.626 C30.649,83.629 30.648,83.632 30.649,83.635 C30.662,83.862 30.681,84.088 30.701,84.313 C31.466,93.037 38.377,99.948 47.101,100.713 C47.326,100.733 47.552,100.754 47.779,100.765 C47.782,100.765 47.785,100.765 47.788,100.765 C48.093,100.783 48.399,100.791 48.709,100.791 C53.639,100.791 58.096,98.833 61.353,95.652 C61.532,95.477 61.712,95.304 61.883,95.122 C61.913,95.09 61.941,95.058 61.97,95.026 C61.98,95.015 61.987,95.002 61.996,94.991 C62.132,94.845 62.266,94.698 62.396,94.548 C62.449,94.487 62.501,94.426 62.553,94.365 C62.594,94.316 62.634,94.267 62.675,94.217 C62.821,94.04 62.961,93.861 63.101,93.678 C63.279,93.444 63.456,93.199 63.622,92.956 C63.956,92.471 64.267,91.97 64.553,91.452 C64.661,91.257 64.757,91.06 64.857,90.861 C64.89,90.796 64.93,90.735 64.962,90.67 C64.98,90.633 64.996,90.594 65.014,90.556 C65.125,90.324 65.234,90.09 65.336,89.852 C65.349,89.82 65.365,89.789 65.379,89.756 C65.48,89.517 65.575,89.271 65.666,89.026 C65.678,88.994 65.689,88.962 65.701,88.93 C65.792,88.679 65.881,88.43 65.962,88.174 C65.97,88.148 65.98,88.122 65.988,88.096 C66.069,87.832 66.144,87.564 66.214,87.296 C66.219,87.275 66.226,87.255 66.231,87.235 C66.301,86.962 66.365,86.686 66.423,86.409 C66.426,86.391 66.428,86.374 66.431,86.356 C66.445,86.291 66.453,86.223 66.466,86.156 C66.511,85.925 66.552,85.695 66.588,85.461 C66.632,85.169 66.671,84.878 66.701,84.583 C66.701,84.574 66.701,84.565 66.701,84.556 C66.731,84.258 66.755,83.955 66.77,83.652 C66.77,83.646 66.77,83.641 66.77,83.635 C66.786,83.326 66.797,83.017 66.797,82.704 C66.797,72.69 58.723,64.617 48.71,64.617 z\"></path>" + - " <path d=\"M62.936,99.809 C59.074,103.028 54.115,104.965 48.71,104.965 C47.101,104.965 45.535,104.787 44.023,104.461 L42.466,106.357 C39.007,105.43 35.855,103.781 33.179,101.574 L28.996,105.765 L29.51,108.861 L13.953,124.426 L15.457,125 L30.066,119.435 L44.675,125 L59.283,119.435 L64.849,104.826 L62.936,99.809 z\"></path>" + - " </g>" + - "</svg>"; - return Img; -}()); -exports.Img = Img; diff --git a/UI/Input/DropDown.js b/UI/Input/DropDown.js deleted file mode 100644 index 0914165..0000000 --- a/UI/Input/DropDown.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DropDown = void 0; -var UIEventSource_1 = require("../UIEventSource"); -var UIElement_1 = require("../UIElement"); -var DropDown = /** @class */ (function (_super) { - __extends(DropDown, _super); - function DropDown(label, values, selectedElement) { - if (selectedElement === void 0) { selectedElement = undefined; } - var _this = _super.call(this, undefined) || this; - _this._label = label; - _this._values = values; - _this.selectedElement = selectedElement !== null && selectedElement !== void 0 ? selectedElement : new UIEventSource_1.UIEventSource(values[0].value); - if (selectedElement.data === undefined) { - _this.selectedElement.setData(values[0].value); - } - var self = _this; - _this.selectedElement.addCallback(function () { - self.InnerUpdate(); - }); - return _this; - } - DropDown.prototype.InnerRender = function () { - var options = ""; - for (var _i = 0, _a = this._values; _i < _a.length; _i++) { - var value = _a[_i]; - options += "<option value='" + value.value + "'>" + value.shown + "</option>"; - } - return "<form>" + - "<label for='dropdown-" + this.id + "'>" + this._label + "</label>" + - "<select name='dropdown-" + this.id + "' id='dropdown-" + this.id + "'>" + - options + - "</select>" + - "</form>"; - }; - DropDown.prototype.InnerUpdate = function () { - var self = this; - var e = document.getElementById("dropdown-" + this.id); - if (e === null) { - return; - } - // @ts-ignore - if (this.selectedElement.data !== e.value) { - // @ts-ignore - e.value = this.selectedElement.data; - } - e.onchange = function () { - // @ts-ignore - var selectedValue = e.options[e.selectedIndex].value; - console.log("Putting data", selectedValue); - self.selectedElement.setData(selectedValue); - }; - }; - return DropDown; -}(UIElement_1.UIElement)); -exports.DropDown = DropDown; diff --git a/UI/Input/FixedInputElement.js b/UI/Input/FixedInputElement.js deleted file mode 100644 index 8b88a6c..0000000 --- a/UI/Input/FixedInputElement.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FixedInputElement = void 0; -var InputElement_1 = require("./InputElement"); -var UIEventSource_1 = require("../UIEventSource"); -var FixedUiElement_1 = require("../Base/FixedUiElement"); -var FixedInputElement = /** @class */ (function (_super) { - __extends(FixedInputElement, _super); - function FixedInputElement(rendering, value) { - var _this = _super.call(this, undefined) || this; - _this.value = new UIEventSource_1.UIEventSource(value); - _this.rendering = typeof (rendering) === 'string' ? new FixedUiElement_1.FixedUiElement(rendering) : rendering; - return _this; - } - FixedInputElement.prototype.GetValue = function () { - return this.value; - }; - FixedInputElement.prototype.InnerRender = function () { - return this.rendering.Render(); - }; - FixedInputElement.prototype.IsValid = function (t) { - return t === this.value.data; - }; - return FixedInputElement; -}(InputElement_1.InputElement)); -exports.FixedInputElement = FixedInputElement; diff --git a/UI/Input/InputElement.js b/UI/Input/InputElement.js deleted file mode 100644 index f8715c7..0000000 --- a/UI/Input/InputElement.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InputElement = void 0; -var UIElement_1 = require("../UIElement"); -var InputElement = /** @class */ (function (_super) { - __extends(InputElement, _super); - function InputElement() { - return _super !== null && _super.apply(this, arguments) || this; - } - return InputElement; -}(UIElement_1.UIElement)); -exports.InputElement = InputElement; diff --git a/UI/Input/InputElementWrapper.js b/UI/Input/InputElementWrapper.js deleted file mode 100644 index 68547c3..0000000 --- a/UI/Input/InputElementWrapper.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.InputElementWrapper = void 0; -var InputElement_1 = require("./InputElement"); -var UIElement_1 = require("../UIElement"); -var InputElementWrapper = /** @class */ (function (_super) { - __extends(InputElementWrapper, _super); - function InputElementWrapper(pre, input, post) { - var _this = _super.call(this, undefined) || this; - _this.post = UIElement_1.UIElement.Fix(post); - _this.input = input; - _this.pre = UIElement_1.UIElement.Fix(pre); - return _this; - } - InputElementWrapper.prototype.GetValue = function () { - return this.input.GetValue(); - }; - InputElementWrapper.prototype.InnerRender = function () { - return this.pre.Render() + this.input.Render() + this.post.Render(); - }; - InputElementWrapper.prototype.IsValid = function (t) { - return this.input.IsValid(t); - }; - return InputElementWrapper; -}(InputElement_1.InputElement)); -exports.InputElementWrapper = InputElementWrapper; diff --git a/UI/Input/RadioButton.js b/UI/Input/RadioButton.js deleted file mode 100644 index accbb18..0000000 --- a/UI/Input/RadioButton.js +++ /dev/null @@ -1,122 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RadioButton = void 0; -var UIEventSource_1 = require("../UIEventSource"); -var InputElement_1 = require("./InputElement"); -var RadioButton = /** @class */ (function (_super) { - __extends(RadioButton, _super); - function RadioButton(elements, selectFirstAsDefault) { - if (selectFirstAsDefault === void 0) { selectFirstAsDefault = true; } - var _this = _super.call(this, undefined) || this; - _this._selectedElementIndex = new UIEventSource_1.UIEventSource(null); - _this._elements = elements; - _this._selectFirstAsDefault = selectFirstAsDefault; - var self = _this; - _this.value = - UIEventSource_1.UIEventSource.flatten(_this._selectedElementIndex.map(function (selectedIndex) { - if (selectedIndex !== undefined && selectedIndex !== null) { - return elements[selectedIndex].GetValue(); - } - }), elements.map(function (e) { return e.GetValue(); })); - _this.value.addCallback(function (t) { - self.SetCorrectValue(t); - }); - var _loop_1 = function (i) { - // If an element is clicked, the radio button corresponding with it should be selected as well - elements[i].onClick(function () { - self._selectedElementIndex.setData(i); - }); - }; - for (var i = 0; i < elements.length; i++) { - _loop_1(i); - } - return _this; - } - RadioButton.prototype.IsValid = function (t) { - for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { - var inputElement = _a[_i]; - if (inputElement.IsValid(t)) { - return true; - } - } - return false; - }; - RadioButton.prototype.GetValue = function () { - return this.value; - }; - RadioButton.prototype.IdFor = function (i) { - return 'radio-' + this.id + '-' + i; - }; - RadioButton.prototype.InnerRender = function () { - var body = ""; - var i = 0; - for (var _i = 0, _a = this._elements; _i < _a.length; _i++) { - var el = _a[_i]; - var htmlElement = '<input type="radio" id="' + this.IdFor(i) + '" name="radiogroup-' + this.id + '">' + - '<label for="' + this.IdFor(i) + '">' + el.Render() + '</label>' + - '<br>'; - body += htmlElement; - i++; - } - return "<form id='" + this.id + "-form'>" + body + "</form>"; - }; - RadioButton.prototype.SetCorrectValue = function (t) { - if (t === undefined) { - return; - } - // We check that what is selected matches the previous rendering - for (var i = 0; i < this._elements.length; i++) { - var e = this._elements[i]; - if (e.IsValid(t)) { - this._selectedElementIndex.setData(i); - e.GetValue().setData(t); - // @ts-ignore - document.getElementById(this.IdFor(i)).checked = true; - return; - } - } - }; - RadioButton.prototype.InnerUpdate = function (htmlElement) { - var self = this; - function checkButtons() { - for (var i = 0; i < self._elements.length; i++) { - var el_1 = document.getElementById(self.IdFor(i)); - // @ts-ignore - if (el_1.checked) { - self._selectedElementIndex.setData(i); - } - } - } - var el = document.getElementById(this.id); - el.addEventListener("change", function () { - checkButtons(); - }); - if (this._selectFirstAsDefault) { - this.SetCorrectValue(this.value.data); - if (this._selectedElementIndex.data === null || this._selectedElementIndex.data === undefined) { - var el_2 = document.getElementById(this.IdFor(0)); - if (el_2) { - // @ts-ignore - el_2.checked = true; - checkButtons(); - } - } - } - }; - ; - return RadioButton; -}(InputElement_1.InputElement)); -exports.RadioButton = RadioButton; diff --git a/UI/Input/TextField.js b/UI/Input/TextField.js deleted file mode 100644 index 3ab7557..0000000 --- a/UI/Input/TextField.js +++ /dev/null @@ -1,94 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TextField = void 0; -var UIEventSource_1 = require("../UIEventSource"); -var InputElement_1 = require("./InputElement"); -var FixedUiElement_1 = require("../Base/FixedUiElement"); -var TextField = /** @class */ (function (_super) { - __extends(TextField, _super); - function TextField(options) { - var _a, _b, _c, _d; - var _this = _super.call(this, undefined) || this; - /** - * Pings and has the value data - */ - _this.enterPressed = new UIEventSource_1.UIEventSource(undefined); - _this.value = new UIEventSource_1.UIEventSource(""); - _this.mappedValue = (_a = options === null || options === void 0 ? void 0 : options.value) !== null && _a !== void 0 ? _a : new UIEventSource_1.UIEventSource(undefined); - // @ts-ignore - _this._fromString = (_b = options.fromString) !== null && _b !== void 0 ? _b : (function (str) { return (str); }); - _this.value.addCallback(function (str) { return _this.mappedValue.setData(options.fromString(str)); }); - _this.mappedValue.addCallback(function (t) { return _this.value.setData(options.toString(t)); }); - _this._placeholder = - typeof (options.placeholder) === "string" ? new FixedUiElement_1.FixedUiElement(options.placeholder) : - ((_c = options.placeholder) !== null && _c !== void 0 ? _c : new FixedUiElement_1.FixedUiElement("")); - _this._toString = (_d = options.toString) !== null && _d !== void 0 ? _d : (function (t) { return ("" + t); }); - var self = _this; - _this.mappedValue.addCallback(function (t) { - if (t === undefined && t === null) { - return; - } - var field = document.getElementById('text-' + _this.id); - if (field === undefined || field === null) { - return; - } - // @ts-ignore - field.value = options.toString(t); - }); - return _this; - } - TextField.prototype.GetValue = function () { - return this.mappedValue; - }; - TextField.prototype.InnerRender = function () { - return "<form onSubmit='return false' class='form-text-field'>" + - "<input type='text' placeholder='" + this._placeholder.InnerRender() + "' id='text-" + this.id + "'>" + - "</form>"; - }; - TextField.prototype.InnerUpdate = function (htmlElement) { - var field = document.getElementById('text-' + this.id); - if (field === null) { - return; - } - var self = this; - field.oninput = function () { - // @ts-ignore - self.value.setData(field.value); - }; - field.addEventListener("keyup", function (event) { - if (event.key === "Enter") { - // @ts-ignore - self.enterPressed.setData(field.value); - } - }); - }; - TextField.prototype.IsValid = function (t) { - if (t === undefined || t === null) { - return false; - } - var result = this._toString(t); - return result !== undefined && result !== null; - }; - TextField.prototype.Clear = function () { - var field = document.getElementById('text-' + this.id); - if (field !== undefined) { - // @ts-ignore - field.value = ""; - } - }; - return TextField; -}(InputElement_1.InputElement)); -exports.TextField = TextField; diff --git a/UI/MessageBoxHandler.js b/UI/MessageBoxHandler.js deleted file mode 100644 index 1048e00..0000000 --- a/UI/MessageBoxHandler.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MessageBoxHandler = void 0; -/** - * Keeps 'messagebox' and 'messageboxmobile' in sync, shows a 'close' button on the latter one - */ -var UIEventSource_1 = require("./UIEventSource"); -var VariableUIElement_1 = require("./Base/VariableUIElement"); -var MessageBoxHandler = /** @class */ (function () { - function MessageBoxHandler(uielement, onClear) { - this._uielement = uielement; - this.listenTo(uielement); - this.update(); - window.onhashchange = function () { - if (location.hash === "") { - // No more element: back to the map! - uielement.setData(undefined); - onClear(); - } - }; - new VariableUIElement_1.VariableUiElement(new UIEventSource_1.UIEventSource("<h2>Naar de kaart</h2>"), function () { - document.getElementById("to-the-map").onclick = function () { - uielement.setData(undefined); - onClear(); - }; - }).AttachTo("to-the-map"); - } - MessageBoxHandler.prototype.listenTo = function (uiEventSource) { - var self = this; - uiEventSource.addCallback(function () { - self.update(); - }); - }; - MessageBoxHandler.prototype.update = function () { - var _a, _b, _c; - var wrapper = document.getElementById("messagesboxmobilewrapper"); - var gen = this._uielement.data; - console.log("Generator: ", gen); - if (gen === undefined) { - wrapper.classList.add("hidden"); - if (location.hash !== "") { - location.hash = ""; - } - return; - } - location.hash = "#element"; - wrapper.classList.remove("hidden"); - /* gen() - ?.HideOnEmpty(true) - ?.AttachTo("messagesbox") - ?.Activate();*/ - (_c = (_b = (_a = gen()) === null || _a === void 0 ? void 0 : _a.HideOnEmpty(true)) === null || _b === void 0 ? void 0 : _b.AttachTo("messagesboxmobile")) === null || _c === void 0 ? void 0 : _c.Activate(); - }; - return MessageBoxHandler; -}()); -exports.MessageBoxHandler = MessageBoxHandler; diff --git a/UI/PendingChanges.js b/UI/PendingChanges.js deleted file mode 100644 index b96e03b..0000000 --- a/UI/PendingChanges.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PendingChanges = void 0; -var UIElement_1 = require("./UIElement"); -var PendingChanges = /** @class */ (function (_super) { - __extends(PendingChanges, _super); - function PendingChanges(changes, countdown) { - var _this = _super.call(this, changes.pendingChangesES) || this; - _this.ListenTo(changes.isSaving); - _this.ListenTo(countdown); - _this._pendingChangesCount = changes.pendingChangesES; - _this._countdown = countdown; - _this._isSaving = changes.isSaving; - _this.onClick(function () { - changes.uploadAll(); - }); - return _this; - } - PendingChanges.prototype.InnerRender = function () { - if (this._isSaving.data) { - return "<span class='alert'>Saving</span>"; - } - if (this._pendingChangesCount.data == 0) { - return ""; - } - var restingSeconds = this._countdown.data / 1000; - var dots = ""; - while (restingSeconds > 0) { - dots += "."; - restingSeconds = restingSeconds - 1; - } - return "Saving " + this._pendingChangesCount.data; - }; - return PendingChanges; -}(UIElement_1.UIElement)); -exports.PendingChanges = PendingChanges; diff --git a/UI/QuestionPicker.js b/UI/QuestionPicker.js deleted file mode 100644 index 877172b..0000000 --- a/UI/QuestionPicker.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.QuestionPicker = void 0; -var UIElement_1 = require("./UIElement"); -var QuestionPicker = /** @class */ (function (_super) { - __extends(QuestionPicker, _super); - function QuestionPicker(questions, tags) { - var _this = _super.call(this, tags) || this; - _this._questions = questions; - _this.tags = tags.data; - _this.source = tags; - return _this; - } - QuestionPicker.prototype.InnerRender = function () { - var t = this.tags; - var highestPriority = Number.MIN_VALUE; - var highestQ; - for (var _i = 0, _a = this._questions; _i < _a.length; _i++) { - var q = _a[_i]; - if (!q.Applicable(t)) { - continue; - } - var priority = q.question.severity; - if (priority > highestPriority) { - highestPriority = priority; - highestQ = q; - } - } - if (highestQ === undefined) { - return "Er zijn geen vragen meer!"; - } - return "<div class='question'>" + - highestQ.CreateHtml(this.source).Render() + - "</div>"; - }; - return QuestionPicker; -}(UIElement_1.UIElement)); -exports.QuestionPicker = QuestionPicker; diff --git a/UI/SaveButton.js b/UI/SaveButton.js deleted file mode 100644 index 14f6e42..0000000 --- a/UI/SaveButton.js +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SaveButton = void 0; -var UIElement_1 = require("./UIElement"); -var SaveButton = /** @class */ (function (_super) { - __extends(SaveButton, _super); - function SaveButton(value) { - var _this = _super.call(this, value) || this; - if (value === undefined) { - throw "No event source for savebutton, something is wrong"; - } - _this._value = value; - return _this; - } - SaveButton.prototype.InnerRender = function () { - if (this._value.data === undefined || - this._value.data === null - || this._value.data === "") { - return "<span class='save-non-active'>Opslaan</span>"; - } - return "<span class='save'>Opslaan</span>"; - }; - return SaveButton; -}(UIElement_1.UIElement)); -exports.SaveButton = SaveButton; diff --git a/UI/SearchAndGo.js b/UI/SearchAndGo.js deleted file mode 100644 index 69ef5ed..0000000 --- a/UI/SearchAndGo.js +++ /dev/null @@ -1,83 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SearchAndGo = void 0; -var UIElement_1 = require("./UIElement"); -var TextField_1 = require("./Input/TextField"); -var UIEventSource_1 = require("./UIEventSource"); -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var Geocoding_1 = require("../Logic/Geocoding"); -var SearchAndGo = /** @class */ (function (_super) { - __extends(SearchAndGo, _super); - function SearchAndGo(map) { - var _this = _super.call(this, undefined) || this; - _this._placeholder = new UIEventSource_1.UIEventSource("Zoek naar een locatie..."); - _this._searchField = new TextField_1.TextField({ - placeholder: _this._placeholder - }); - _this._foundEntries = new UIEventSource_1.UIEventSource([]); - _this._goButton = new FixedUiElement_1.FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>"); - _this._map = map; - _this.ListenTo(_this._foundEntries); - var self = _this; - _this._searchField.enterPressed.addCallback(function () { - self.RunSearch(); - }); - _this._goButton.onClick(function () { - self.RunSearch(); - }); - return _this; - } - // Triggered by 'enter' or onclick - SearchAndGo.prototype.RunSearch = function () { - var _this = this; - var searchString = this._searchField.GetValue().data; - this._searchField.Clear(); - this._placeholder.setData("Bezig met zoeken..."); - var self = this; - Geocoding_1.Geocoding.Search(searchString, this._map, function (result) { - if (result.length == 0) { - _this._placeholder.setData("Niets gevonden"); - return; - } - var bb = result[0].boundingbox; - var bounds = [ - [bb[0], bb[2]], - [bb[1], bb[3]] - ]; - self._map.map.fitBounds(bounds); - _this._placeholder.setData("Zoek naar een locatie..."); - }, function () { - _this._placeholder.setData("Niets gevonden: er ging iets mis"); - }); - }; - SearchAndGo.prototype.InnerRender = function () { - // "<img class='search' src='./assets/search.svg' alt='Search'> " + - return this._searchField.Render() + - this._goButton.Render(); - }; - SearchAndGo.prototype.Update = function () { - _super.prototype.Update.call(this); - this._searchField.Update(); - this._goButton.Update(); - }; - SearchAndGo.prototype.Activate = function () { - _super.prototype.Activate.call(this); - this._searchField.Activate(); - this._goButton.Activate(); - }; - return SearchAndGo; -}(UIElement_1.UIElement)); -exports.SearchAndGo = SearchAndGo; diff --git a/UI/SimpleAddUI.js b/UI/SimpleAddUI.js deleted file mode 100644 index 83a5064..0000000 --- a/UI/SimpleAddUI.js +++ /dev/null @@ -1,84 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SimpleAddUI = void 0; -var UIElement_1 = require("./UIElement"); -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var Button_1 = require("./Base/Button"); -/** - * Asks to add a feature at the last clicked location, at least if zoom is sufficient - */ -var SimpleAddUI = /** @class */ (function (_super) { - __extends(SimpleAddUI, _super); - function SimpleAddUI(zoomlevel, lastClickLocation, changes, selectedElement, dataIsLoading, userDetails, addButtons) { - var _this = _super.call(this, zoomlevel) || this; - _this._zoomlevel = zoomlevel; - _this._lastClickLocation = lastClickLocation; - _this._changes = changes; - _this._selectedElement = selectedElement; - _this._dataIsLoading = dataIsLoading; - _this._userDetails = userDetails; - _this.ListenTo(userDetails); - _this.ListenTo(dataIsLoading); - _this._addButtons = []; - for (var _i = 0, addButtons_1 = addButtons; _i < addButtons_1.length; _i++) { - var option = addButtons_1[_i]; - // <button type='button'> looks SO retarded - // the default type of button is 'submit', which performs a POST and page reload - var button = new Button_1.Button(new FixedUiElement_1.FixedUiElement("Voeg hier een " + option.name + " toe"), _this.CreatePoint(option)); - _this._addButtons.push(button); - } - return _this; - } - SimpleAddUI.prototype.CreatePoint = function (option) { - var self = this; - return function () { - console.log("Creating a new ", option.name, " at last click location"); - var loc = self._lastClickLocation.data; - var feature = self._changes.createElement(option.tags, loc.lat, loc.lon); - option.layerToAddTo.AddNewElement(feature); - self._selectedElement.setData(feature.properties); - }; - }; - SimpleAddUI.prototype.InnerRender = function () { - var header = "<h2>Geen selectie</h2>" + - "Je klikte ergens waar er nog geen gezochte data is.<br/>"; - if (!this._userDetails.data.loggedIn) { - return header + "<a class='activate-osm-authentication'>Gelieve je aan te melden om een nieuw punt toe te voegen</a>"; - } - if (this._zoomlevel.data.zoom < 19) { - return header + "Zoom verder in om een element toe te voegen."; - } - if (this._dataIsLoading.data) { - return header + "De data is nog aan het laden. Nog even geduld, dan kan je een punt toevoegen"; - } - var html = ""; - for (var _i = 0, _a = this._addButtons; _i < _a.length; _i++) { - var button = _a[_i]; - html += button.Render(); - } - return header + html; - }; - SimpleAddUI.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - for (var _i = 0, _a = this._addButtons; _i < _a.length; _i++) { - var button = _a[_i]; - button.Update(); - } - this._userDetails.data.osmConnection.registerActivateOsmAUthenticationClass(); - }; - return SimpleAddUI; -}(UIElement_1.UIElement)); -exports.SimpleAddUI = SimpleAddUI; diff --git a/UI/SlideShow.js b/UI/SlideShow.js deleted file mode 100644 index e49f78e..0000000 --- a/UI/SlideShow.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SlideShow = void 0; -var UIElement_1 = require("./UIElement"); -var UIEventSource_1 = require("./UIEventSource"); -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var SlideShow = /** @class */ (function (_super) { - __extends(SlideShow, _super); - function SlideShow(embeddedElements, noImages) { - var _this = _super.call(this, embeddedElements) || this; - _this._currentSlide = new UIEventSource_1.UIEventSource(0); - _this._embeddedElements = embeddedElements; - _this.ListenTo(_this._currentSlide); - _this._noimages = noImages; - var self = _this; - _this._prev = new FixedUiElement_1.FixedUiElement("<div class='prev-button'>" + - "<div class='vspan'></div>" + - "<img src='assets/arrow-left-smooth.svg' alt='Prev'/>" + - "</div>") - .onClick(function () { - var current = self._currentSlide.data; - self.MoveTo(current - 1); - }); - _this._next = new FixedUiElement_1.FixedUiElement("<div class='next-button'>" + - "<div class='vspan'></div>" + - "<img src='assets/arrow-right-smooth.svg' alt='Next'/>" + - "</div>") - .onClick(function () { - var current = self._currentSlide.data; - self.MoveTo(current + 1); - }); - return _this; - } - SlideShow.prototype.InnerRender = function () { - if (this._embeddedElements.data.length == 0) { - return this._noimages.Render(); - } - if (this._embeddedElements.data.length == 1) { - return "<div class='image-slideshow'><div class='slides'><div class='slide'>" + - this._embeddedElements.data[0].Render() + - "</div></div></div>"; - } - var slides = ""; - for (var i = 0; i < this._embeddedElements.data.length; i++) { - var embeddedElement = this._embeddedElements.data[i]; - var state = "hidden"; - if (this._currentSlide.data === i) { - state = "active-slide"; - } - slides += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n"; - } - return "<div class='image-slideshow'>" - + this._prev.Render() - + "<div class='slides'>" + slides + "</div>" - + this._next.Render() - + "</div>"; - }; - SlideShow.prototype.MoveTo = function (index) { - if (index < 0) { - index = this._embeddedElements.data.length - 1; - } - index = index % this._embeddedElements.data.length; - this._currentSlide.setData(index); - }; - SlideShow.prototype.InnerUpdate = function (htmlElement) { - this._next.Update(); - this._prev.Update(); - }; - SlideShow.prototype.Activate = function () { - for (var _i = 0, _a = this._embeddedElements.data; _i < _a.length; _i++) { - var embeddedElement = _a[_i]; - embeddedElement.Activate(); - } - this._next.Update(); - this._prev.Update(); - }; - return SlideShow; -}(UIElement_1.UIElement)); -exports.SlideShow = SlideShow; diff --git a/UI/UIElement.js b/UI/UIElement.js deleted file mode 100644 index ba51902..0000000 --- a/UI/UIElement.js +++ /dev/null @@ -1,115 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UIElement = void 0; -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var UIElement = /** @class */ (function () { - function UIElement(source) { - this._hideIfEmpty = false; - this.id = "ui-element-" + UIElement.nextId; - this._source = source; - UIElement.nextId++; - this.ListenTo(source); - } - UIElement.prototype.ListenTo = function (source) { - if (source === undefined) { - return; - } - var self = this; - source.addCallback(function () { - self.Update(); - }); - }; - UIElement.prototype.onClick = function (f) { - this._onClick = f; - this.Update(); - return this; - }; - UIElement.Fix = function (element) { - if (typeof (element) === 'string') { - return new FixedUiElement_1.FixedUiElement(element); - } - return element; - }; - UIElement.prototype.Update = function () { - var element = document.getElementById(this.id); - if (element === null || element === undefined) { - // The element is not painted - return; - } - element.innerHTML = this.InnerRender(); - if (this._hideIfEmpty) { - if (element.innerHTML === "") { - element.parentElement.style.display = "none"; - } - else { - element.parentElement.style.display = undefined; - } - } - if (this._onClick !== undefined) { - var self_1 = this; - element.onclick = function () { - self_1._onClick(); - }; - element.style.pointerEvents = "all"; - element.style.cursor = "pointer"; - } - this.InnerUpdate(element); - for (var i in this) { - var child = this[i]; - if (child instanceof UIElement) { - child.Update(); - } - else if (child instanceof Array) { - for (var _i = 0, child_1 = child; _i < child_1.length; _i++) { - var ch = child_1[_i]; - if (ch instanceof UIElement) { - ch.Update(); - } - } - } - } - }; - UIElement.prototype.HideOnEmpty = function (hide) { - this._hideIfEmpty = hide; - this.Update(); - return this; - }; - // Called after the HTML has been replaced. Can be used for css tricks - UIElement.prototype.InnerUpdate = function (htmlElement) { }; - UIElement.prototype.Render = function () { - return "<span class='uielement' id='" + this.id + "'>" + this.InnerRender() + "</span>"; - }; - UIElement.prototype.AttachTo = function (divId) { - var element = document.getElementById(divId); - if (element === null) { - console.log("SEVERE: could not attach UIElement to ", divId); - return; - } - element.innerHTML = this.Render(); - this.Update(); - return this; - }; - UIElement.prototype.Activate = function () { - for (var i in this) { - var child = this[i]; - if (child instanceof UIElement) { - child.Activate(); - } - else if (child instanceof Array) { - for (var _i = 0, child_2 = child; _i < child_2.length; _i++) { - var ch = child_2[_i]; - if (ch instanceof UIElement) { - ch.Activate(); - } - } - } - } - }; - ; - UIElement.prototype.IsEmpty = function () { - return this.InnerRender() === ""; - }; - UIElement.nextId = 0; - return UIElement; -}()); -exports.UIElement = UIElement; diff --git a/UI/UIEventSource.js b/UI/UIEventSource.js deleted file mode 100644 index b81478c..0000000 --- a/UI/UIEventSource.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UIEventSource = void 0; -var UIEventSource = /** @class */ (function () { - function UIEventSource(data) { - this._callbacks = []; - this.data = data; - } - UIEventSource.prototype.addCallback = function (callback) { - this._callbacks.push(callback); - return this; - }; - UIEventSource.prototype.setData = function (t) { - if (this.data === t) { - return; - } - this.data = t; - this.ping(); - }; - UIEventSource.prototype.ping = function () { - for (var _i = 0, _a = this._callbacks; _i < _a.length; _i++) { - var callback = _a[_i]; - callback(this.data); - } - }; - UIEventSource.flatten = function (source, possibleSources) { - var _a; - var sink = new UIEventSource((_a = source.data) === null || _a === void 0 ? void 0 : _a.data); - source.addCallback(function (latestData) { - sink.setData(latestData === null || latestData === void 0 ? void 0 : latestData.data); - }); - for (var _i = 0, possibleSources_1 = possibleSources; _i < possibleSources_1.length; _i++) { - var possibleSource = possibleSources_1[_i]; - possibleSource.addCallback(function () { - var _a; - sink.setData((_a = source.data) === null || _a === void 0 ? void 0 : _a.data); - }); - } - return sink; - }; - UIEventSource.prototype.map = function (f, extraSources) { - if (extraSources === void 0) { extraSources = []; } - var self = this; - var update = function () { - newSource.setData(f(self.data)); - newSource.ping(); - }; - this.addCallback(update); - for (var _i = 0, extraSources_1 = extraSources; _i < extraSources_1.length; _i++) { - var extraSource = extraSources_1[_i]; - extraSource.addCallback(update); - } - var newSource = new UIEventSource(f(this.data)); - return newSource; - }; - return UIEventSource; -}()); -exports.UIEventSource = UIEventSource; diff --git a/UI/UserBadge.js b/UI/UserBadge.js deleted file mode 100644 index d6c8536..0000000 --- a/UI/UserBadge.js +++ /dev/null @@ -1,112 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.UserBadge = void 0; -var UIElement_1 = require("./UIElement"); -var leaflet_1 = require("leaflet"); -var FixedUiElement_1 = require("./Base/FixedUiElement"); -var VariableUIElement_1 = require("./Base/VariableUIElement"); -/** - * Handles and updates the user badge - */ -var UserBadge = /** @class */ (function (_super) { - __extends(UserBadge, _super); - function UserBadge(userDetails, pendingChanges, basemap) { - var _this = _super.call(this, userDetails) || this; - _this._userDetails = userDetails; - _this._pendingChanges = pendingChanges; - _this._basemap = basemap; - _this._logout = new FixedUiElement_1.FixedUiElement("<img src='assets/logout.svg' class='small-userbadge-icon' alt='logout'>") - .onClick(function () { - userDetails.data.osmConnection.LogOut(); - }); - userDetails.addCallback(function () { - var profilePic = document.getElementById("profile-pic"); - if (profilePic) { - profilePic.onload = function () { - profilePic.style.opacity = "1"; - }; - } - }); - _this._homeButton = new VariableUIElement_1.VariableUiElement(userDetails.map(function (userinfo) { - if (userinfo.home) { - return "<img id='home' src='./assets/home.svg' alt='home' class='small-userbadge-icon'> "; - } - return ""; - })).onClick(function () { - var _a; - var home = (_a = userDetails.data) === null || _a === void 0 ? void 0 : _a.home; - if (home === undefined) { - return; - } - basemap.map.flyTo([home.lat, home.lon], 18); - }); - return _this; - } - UserBadge.prototype.InnerRender = function () { - var user = this._userDetails.data; - if (!user.loggedIn) { - return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>"; - } - var messageSpan = "<span id='messages'>" + - " <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'>" + - user.totalMessages + - "</a></span>"; - if (user.unreadMessages > 0) { - messageSpan = "<span id='messages' class='alert'>" + - " <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='small-userbadge-icon' src='./assets/envelope.svg' alt='msgs'/>" + - " " + - "" + - user.unreadMessages.toString() + - "</a></span>"; - } - var dryrun = ""; - if (user.dryRun) { - dryrun = " <span class='alert'>TESTING</span>"; - } - if (user.home !== undefined) { - var icon = leaflet_1.default.icon({ - iconUrl: 'assets/home.svg', - iconSize: [20, 20], - iconAnchor: [10, 10] - }); - leaflet_1.default.marker([user.home.lat, user.home.lon], { icon: icon }).addTo(this._basemap.map); - } - var settings = "<a href='https://www.openstreetmap.org/user/" + encodeURIComponent(user.name) + "/account' target='_blank'>" + - "<img class='small-userbadge-icon' src='./assets/gear.svg' alt='settings'>" + - "</a> "; - return "<a href='https://www.openstreetmap.org/user/" + encodeURIComponent(user.name) + "' target='_blank'>" + - "<img id='profile-pic' src='" + user.img + "' alt='profile-pic'/> " + - "</a>" + - "<div id='usertext'>" + - "<p id='username'>" + - "<a href='https://www.openstreetmap.org/user/" + user.name + "' target='_blank'>" + user.name + "</a>" + - dryrun + - "</p> " + - "<p id='userstats'>" + - this._homeButton.Render() + - settings + - messageSpan + - "<span id='csCount'> " + - " <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='small-userbadge-icon' src='./assets/star.svg' alt='star'/> " + user.csCount + - "</a></span> " + - this._logout.Render() + - this._pendingChanges.Render() + - "</p>" + - "</div>"; - }; - return UserBadge; -}(UIElement_1.UIElement)); -exports.UserBadge = UserBadge; diff --git a/index.js b/index.js deleted file mode 100644 index 8bab00e..0000000 --- a/index.js +++ /dev/null @@ -1,187 +0,0 @@ -"use strict"; -var _a; -Object.defineProperty(exports, "__esModule", { value: true }); -var OsmConnection_1 = require("./Logic/OsmConnection"); -var Changes_1 = require("./Logic/Changes"); -var ElementStorage_1 = require("./Logic/ElementStorage"); -var UIEventSource_1 = require("./UI/UIEventSource"); -var UserBadge_1 = require("./UI/UserBadge"); -var Basemap_1 = require("./Logic/Basemap"); -var PendingChanges_1 = require("./UI/PendingChanges"); -var CenterMessageBox_1 = require("./UI/CenterMessageBox"); -var Helpers_1 = require("./Helpers"); -var TagsFilter_1 = require("./Logic/TagsFilter"); -var LayerUpdater_1 = require("./Logic/LayerUpdater"); -var MessageBoxHandler_1 = require("./UI/MessageBoxHandler"); -var FeatureInfoBox_1 = require("./UI/FeatureInfoBox"); -var GeoLocationHandler_1 = require("./Logic/GeoLocationHandler"); -var StrayClickHandler_1 = require("./Logic/StrayClickHandler"); -var SimpleAddUI_1 = require("./UI/SimpleAddUI"); -var VariableUIElement_1 = require("./UI/Base/VariableUIElement"); -var SearchAndGo_1 = require("./UI/SearchAndGo"); -var CollapseButton_1 = require("./UI/Base/CollapseButton"); -var AllKnownLayouts_1 = require("./Customizations/AllKnownLayouts"); -// --------------------- Read the URL parameters ----------------- -// @ts-ignore -if (location.href.startsWith("http://buurtnatuur.be")) { - // Reload the https version. This is important for the 'locate me' button - window.location.replace("https://buurtnatuur.be"); -} -var dryRun = false; -if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { - // Set to true if testing and changes should NOT be saved - dryRun = true; - // If you have a testfile somewhere, enable this to spoof overpass - // This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules - //Overpass.testUrl = "http://127.0.0.1:8080/streetwidths.geojson"; -} -// ----------------- SELECT THE RIGHT QUESTSET ----------------- -var defaultLayout = "buurtnatuur"; -// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default -for (var k in AllKnownLayouts_1.AllKnownLayouts.allSets) { - var layout = AllKnownLayouts_1.AllKnownLayouts.allSets[k]; - var possibleParts = (_a = layout.locationContains) !== null && _a !== void 0 ? _a : []; - for (var _i = 0, possibleParts_1 = possibleParts; _i < possibleParts_1.length; _i++) { - var locationMatch = possibleParts_1[_i]; - if (locationMatch === "") { - continue; - } - if (window.location.href.toLowerCase().indexOf(locationMatch.toLowerCase()) >= 0) { - defaultLayout = layout.name; - } - } -} -// Read the query string to grap settings -var paramDict = {}; -if (window.location.search) { - var params = window.location.search.substr(1).split("&"); - for (var _b = 0, params_1 = params; _b < params_1.length; _b++) { - var param = params_1[_b]; - var kv = param.split("="); - paramDict[kv[0]] = kv[1]; - } -} -if (paramDict.layout) { - defaultLayout = paramDict.layout; -} -if (paramDict.test) { - dryRun = paramDict.test === "true"; -} -var layoutToUse = AllKnownLayouts_1.AllKnownLayouts.allSets[defaultLayout]; -console.log("Using layout: ", layoutToUse.name); -document.title = layoutToUse.title; -// ----------------- Setup a few event sources ------------- -// The message that should be shown at the center of the screen -var centerMessage = new UIEventSource_1.UIEventSource(""); -// The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource -var secondsTillChangesAreSaved = new UIEventSource_1.UIEventSource(0); -var leftMessage = new UIEventSource_1.UIEventSource(undefined); -var selectedElement = new UIEventSource_1.UIEventSource(undefined); -var locationControl = new UIEventSource_1.UIEventSource({ - zoom: layoutToUse.startzoom, - lat: layoutToUse.startLat, - lon: layoutToUse.startLon -}); -// ----------------- Prepare the important objects ----------------- -var saveTimeout = 30000; // After this many milliseconds without changes, saves are sent of to OSM -var allElements = new ElementStorage_1.ElementStorage(); -var osmConnection = new OsmConnection_1.OsmConnection(dryRun); -var changes = new Changes_1.Changes("Beantwoorden van vragen met #MapComplete voor vragenset #" + layoutToUse.name, osmConnection, allElements); -var bm = new Basemap_1.Basemap("leafletDiv", locationControl, new VariableUIElement_1.VariableUiElement(locationControl.map(function (location) { - var mapComplete = "<a href='https://github.com/pietervdvn/MapComplete' target='_blank'>Mapcomple</a> " + - " " + - "<a href='https://github.com/pietervdvn/MapComplete/issues' target='_blank'><img src='./assets/bug.svg' alt='Report bug' class='small-userbadge-icon'></a>"; - var editHere = ""; - if (location !== undefined) { - editHere = " | " + - "<a href='https://www.openstreetmap.org/edit?editor=id#map=" + location.zoom + "/" + location.lat + "/" + location.lon + "' target='_blank'>" + - "<img src='./assets/pencil.svg' alt='edit here' class='small-userbadge-icon'>" + - "</a>"; - } - return mapComplete + editHere; -}))); -// ------------- Setup the layers ------------------------------- -var controls = {}; -var addButtons = []; -var flayers = []; -var minZoom = 0; -var _loop_1 = function (layer) { - var generateInfo = function (tagsES) { - return new FeatureInfoBox_1.FeatureInfoBox(tagsES, layer.title, layer.elementsToShow, changes, osmConnection.userDetails); - }; - minZoom = Math.max(minZoom, layer.minzoom); - var flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); - controls[layer.name] = flayer.isDisplayed; - var addButton = { - name: layer.name, - icon: layer.icon, - tags: layer.newElementTags, - layerToAddTo: flayer - }; - addButtons.push(addButton); - flayers.push(flayer); -}; -for (var _c = 0, _d = layoutToUse.layers; _c < _d.length; _c++) { - var layer = _d[_c]; - _loop_1(layer); -} -var layerUpdater = new LayerUpdater_1.LayerUpdater(bm, minZoom, flayers); -// ------------------ Setup various UI elements ------------ -new StrayClickHandler_1.StrayClickHandler(bm, selectedElement, leftMessage, function () { - return new SimpleAddUI_1.SimpleAddUI(bm.Location, bm.LastClickLocation, changes, selectedElement, layerUpdater.runningQuery, osmConnection.userDetails, addButtons); -}); -/** - * Show the questions and information for the selected element on the leftMessage - */ -selectedElement.addCallback(function (data) { - var _loop_2 = function (layer) { - var applicable = layer.overpassFilter.matches(TagsFilter_1.TagUtils.proprtiesToKV(data)); - if (applicable) { - // This layer is the layer that gives the questions - leftMessage.setData(function () { - return new FeatureInfoBox_1.FeatureInfoBox(allElements.getElement(data.id), layer.title, layer.elementsToShow, changes, osmConnection.userDetails); - }); - return "break"; - } - }; - // Which is the applicable set? - for (var _i = 0, _a = layoutToUse.layers; _i < _a.length; _i++) { - var layer = _a[_i]; - var state_1 = _loop_2(layer); - if (state_1 === "break") - break; - } -}); -var pendingChanges = new PendingChanges_1.PendingChanges(changes, secondsTillChangesAreSaved); -new UserBadge_1.UserBadge(osmConnection.userDetails, pendingChanges, bm) - .AttachTo('userbadge'); -new SearchAndGo_1.SearchAndGo(bm).AttachTo("searchbox"); -new CollapseButton_1.CollapseButton("messagesbox") - .AttachTo("collapseButton"); -var welcomeMessage = function () { - return new VariableUIElement_1.VariableUiElement(osmConnection.userDetails.map(function (userdetails) { - var login = layoutToUse.gettingStartedPlzLogin; - if (userdetails.loggedIn) { - login = layoutToUse.welcomeBackMessage; - } - return "<div id='welcomeMessage'>" + - layoutToUse.welcomeMessage + login + layoutToUse.welcomeTail + - "</div>"; - }), function () { - osmConnection.registerActivateOsmAUthenticationClass(); - }); -}; -leftMessage.setData(welcomeMessage); -welcomeMessage().AttachTo("messagesbox"); -var messageBox = new MessageBoxHandler_1.MessageBoxHandler(leftMessage, function () { - selectedElement.setData(undefined); -}); -new CenterMessageBox_1.CenterMessageBox(minZoom, centerMessage, osmConnection, locationControl, layerUpdater.runningQuery) - .AttachTo("centermessage"); -Helpers_1.Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved, saveTimeout); -Helpers_1.Helpers.LastEffortSave(changes); -osmConnection.registerActivateOsmAUthenticationClass(); -new GeoLocationHandler_1.GeoLocationHandler(bm).AttachTo("geolocate-button"); -// --------------- Send a ping to start various action -------- -locationControl.ping(); -messageBox.update(); diff --git a/test.js b/test.js deleted file mode 100644 index 9cf2691..0000000 --- a/test.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -var TextField_1 = require("./UI/Input/TextField"); -var FixedInputElement_1 = require("./UI/Input/FixedInputElement"); -var RadioButton_1 = require("./UI/Input/RadioButton"); -var buttons = new RadioButton_1.RadioButton([new FixedInputElement_1.FixedInputElement("Five", 5), - new FixedInputElement_1.FixedInputElement("Ten", 10), new TextField_1.TextField({ - fromString: function (str) { return parseInt(str); }, - toString: function (i) { return ("" + i); }, - }) -], false).AttachTo("maindiv"); -buttons.GetValue().addCallback(console.log); From 2f510e9143859c98b8c4526e82158269ac961325 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 20:06:00 +0200 Subject: [PATCH 16/29] Fix searchAndGo --- UI/SearchAndGo.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/UI/SearchAndGo.ts b/UI/SearchAndGo.ts index 1ddf995..918bdb3 100644 --- a/UI/SearchAndGo.ts +++ b/UI/SearchAndGo.ts @@ -4,13 +4,16 @@ import {UIEventSource} from "./UIEventSource"; import {FixedUiElement} from "./Base/FixedUiElement"; import {Geocoding} from "../Logic/Geocoding"; import {Basemap} from "../Logic/Basemap"; +import {VariableUiElement} from "./Base/VariableUIElement"; export class SearchAndGo extends UIElement { private _placeholder = new UIEventSource("Zoek naar een locatie...") private _searchField = new TextField<string>({ - placeholder: this._placeholder + placeholder: new VariableUiElement(this._placeholder), + fromString: str => str, + toString: str => str } ); @@ -61,7 +64,7 @@ export class SearchAndGo extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { // "<img class='search' src='./assets/search.svg' alt='Search'> " + return this._searchField.Render() + this._goButton.Render(); From 14a5c7406afef6776e251ed1d8276304fccd4f29 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 20:15:21 +0200 Subject: [PATCH 17/29] Remove JS --- Logic/Basemap.js | 70 --------- Logic/Changes.js | 222 ---------------------------- Logic/ElementStorage.js | 51 ------- Logic/FilteredLayer.js | 173 ---------------------- Logic/GeoLocationHandler.js | 117 --------------- Logic/GeoOperations.js | 185 ------------------------ Logic/Geocoding.js | 22 --- Logic/ImageSearcher.js | 185 ------------------------ Logic/Imgur.js | 90 ------------ Logic/LayerUpdater.js | 99 ------------- Logic/OsmConnection.js | 256 --------------------------------- Logic/OsmImageUploadHandler.js | 56 -------- Logic/OsmObject.js | 160 --------------------- Logic/Overpass.js | 47 ------ Logic/Overpass.ts | 4 +- Logic/StrayClickHandler.js | 42 ------ Logic/TagsFilter.js | 240 ------------------------------- Logic/Wikimedia.js | 135 ----------------- 18 files changed, 2 insertions(+), 2152 deletions(-) delete mode 100644 Logic/Basemap.js delete mode 100644 Logic/Changes.js delete mode 100644 Logic/ElementStorage.js delete mode 100644 Logic/FilteredLayer.js delete mode 100644 Logic/GeoLocationHandler.js delete mode 100644 Logic/GeoOperations.js delete mode 100644 Logic/Geocoding.js delete mode 100644 Logic/ImageSearcher.js delete mode 100644 Logic/Imgur.js delete mode 100644 Logic/LayerUpdater.js delete mode 100644 Logic/OsmConnection.js delete mode 100644 Logic/OsmImageUploadHandler.js delete mode 100644 Logic/OsmObject.js delete mode 100644 Logic/Overpass.js delete mode 100644 Logic/StrayClickHandler.js delete mode 100644 Logic/TagsFilter.js delete mode 100644 Logic/Wikimedia.js diff --git a/Logic/Basemap.js b/Logic/Basemap.js deleted file mode 100644 index 4f35171..0000000 --- a/Logic/Basemap.js +++ /dev/null @@ -1,70 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Basemap = void 0; -var leaflet_1 = require("leaflet"); -var UIEventSource_1 = require("../UI/UIEventSource"); -// Contains all setup and baselayers for Leaflet stuff -var Basemap = /** @class */ (function () { - function Basemap(leafletElementId, location, extraAttribution) { - this.LastClickLocation = new UIEventSource_1.UIEventSource(undefined); - this.aivLucht2013Layer = leaflet_1.default.tileLayer.wms('https://geoservices.informatievlaanderen.be/raadpleegdiensten/OGW/wms?s', { - layers: "OGWRGB13_15VL", - attribution: "Luchtfoto's van © AIV Vlaanderen (2013-2015) | " - }); - this.aivLuchtLatestLayer = leaflet_1.default.tileLayer("https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&" + - "LAYER=omwrgbmrvl&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileRow={y}&tileCol={x}", { - // omwrgbmrvl - attribution: 'Luchtfoto\'s van © AIV Vlaanderen (Laatste) © AGIV', - maxZoom: 20, - minZoom: 1, - wmts: true - }); - this.osmLayer = leaflet_1.default.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { - attribution: '', - maxZoom: 19, - minZoom: 1 - }); - this.osmBeLayer = leaflet_1.default.tileLayer("https://tile.osm.be/osmbe/{z}/{x}/{y}.png", { - attribution: '<a href="https://geo6.be/">Tile hosting courtesy of Geo6</a>', - maxZoom: 18, - minZoom: 1 - }); - this.grbLayer = leaflet_1.default.tileLayer("https://tile.informatievlaanderen.be/ws/raadpleegdiensten/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=grb_bsk&STYLE=&FORMAT=image/png&tileMatrixSet=GoogleMapsVL&tileMatrix={z}&tileCol={x}&tileRow={y}", { - attribution: 'Achtergrond <i>Grootschalig ReferentieBestand</i>(GRB) © AGIV', - maxZoom: 20, - minZoom: 1, - wmts: true - }); - this.baseLayers = { - "Luchtfoto Vlaanderen (recentste door AIV)": this.aivLuchtLatestLayer, - "Luchtfoto Vlaanderen (2013-2015, door AIV)": this.aivLucht2013Layer, - "Kaart van OpenStreetMap": this.osmLayer, - "Kaart Grootschalig ReferentieBestand Vlaanderen (GRB) door AIV": this.grbLayer - }; - this.map = leaflet_1.default.map(leafletElementId, { - center: [location.data.lat, location.data.lon], - zoom: location.data.zoom, - layers: [this.osmLayer], - }); - this.map.attributionControl.setPrefix(extraAttribution.Render() + " | <a href='https://osm.org'>OpenStreetMap</a>"); - this.Location = location; - var layerControl = leaflet_1.default.control.layers(this.baseLayers, null, { - position: 'bottomright', - hideSingleBase: true - }); - layerControl.addTo(this.map); - this.map.zoomControl.setPosition("bottomright"); - var self = this; - this.map.on("moveend", function () { - location.data.zoom = self.map.getZoom(); - location.data.lat = self.map.getCenter().lat; - location.data.lon = self.map.getCenter().lng; - location.ping(); - }); - this.map.on("click", function (e) { - self.LastClickLocation.setData({ lat: e.latlng.lat, lon: e.latlng.lng }); - }); - } - return Basemap; -}()); -exports.Basemap = Basemap; diff --git a/Logic/Changes.js b/Logic/Changes.js deleted file mode 100644 index 12edb7e..0000000 --- a/Logic/Changes.js +++ /dev/null @@ -1,222 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Changes = void 0; -var OsmObject_1 = require("./OsmObject"); -var UIEventSource_1 = require("../UI/UIEventSource"); -var TagsFilter_1 = require("./TagsFilter"); -var Changes = /** @class */ (function () { - function Changes(changesetComment, login, allElements) { - this._pendingChanges = []; // Gets reset on uploadAll - this.newElements = []; // Gets reset on uploadAll - this.pendingChangesES = new UIEventSource_1.UIEventSource(this._pendingChanges.length); - this.isSaving = new UIEventSource_1.UIEventSource(false); - this._changesetComment = changesetComment; - this.login = login; - this._allElements = allElements; - } - Changes.prototype.addTag = function (elementId, tagsFilter) { - if (tagsFilter instanceof TagsFilter_1.Tag) { - var tag = tagsFilter; - this.addChange(elementId, tag.key, tag.value); - return; - } - if (tagsFilter instanceof TagsFilter_1.And) { - var and = tagsFilter; - for (var _i = 0, _a = and.and; _i < _a.length; _i++) { - var tag = _a[_i]; - this.addTag(elementId, tag); - } - return; - } - console.log("Unsupported tagsfilter element to addTag", tagsFilter); - throw "Unsupported tagsFilter element"; - }; - /** - * Adds a change to the pending changes - * @param elementId - * @param key - * @param value - */ - Changes.prototype.addChange = function (elementId, key, value) { - console.log("Received change", key, value); - if (key === undefined || key === null) { - console.log("Invalid key"); - return; - } - if (value === undefined || value === null) { - console.log("Invalid value for ", key); - return; - } - var eventSource = this._allElements.getElement(elementId); - eventSource.data[key] = value; - eventSource.ping(); - // We get the id from the event source, as that ID might be rewritten - this._pendingChanges.push({ elementId: eventSource.data.id, key: key, value: value }); - this.pendingChangesES.setData(this._pendingChanges.length); - }; - /** - * Create a new node element at the given lat/long. - * An internal OsmObject is created to upload later on, a geojson represention is returned. - * Note that the geojson version shares the tags (properties) by pointer, but has _no_ id in properties - */ - Changes.prototype.createElement = function (basicTags, lat, lon) { - var osmNode = new OsmObject_1.OsmNode(Changes._nextId); - this.newElements.push(osmNode); - Changes._nextId--; - var id = "node/" + osmNode.id; - osmNode.lat = lat; - osmNode.lon = lon; - var properties = { id: id }; - var geojson = { - "type": "Feature", - "properties": properties, - "id": id, - "geometry": { - "type": "Point", - "coordinates": [ - lon, - lat - ] - } - }; - this._allElements.addOrGetElement(geojson); - // The basictags are COPIED, the id is included in the properties - // The tags are not yet written into the OsmObject, but this is applied onto a - for (var _i = 0, basicTags_1 = basicTags; _i < basicTags_1.length; _i++) { - var kv = basicTags_1[_i]; - this.addChange(id, kv.key, kv.value); // We use the call, to trigger all the other machinery (including updating the geojson itsel - properties[kv.key] = kv.value; - } - return geojson; - }; - Changes.prototype.uploadAll = function (optionalContinuation) { - if (optionalContinuation === void 0) { optionalContinuation = undefined; } - var self = this; - this.isSaving.setData(true); - var optionalContinuationWrapped = function () { - self.isSaving.setData(false); - if (optionalContinuation) { - optionalContinuation(); - } - }; - var pending = this._pendingChanges; - this._pendingChanges = []; - this.pendingChangesES.setData(this._pendingChanges.length); - var newElements = this.newElements; - this.newElements = []; - var knownElements = {}; // maps string --> OsmObject - function DownloadAndContinue(neededIds, continuation) { - // local function which downloads all the objects one by one - // this is one big loop, running one download, then rerunning the entire function - if (neededIds.length == 0) { - continuation(); - return; - } - var neededId = neededIds.pop(); - if (neededId in knownElements) { - DownloadAndContinue(neededIds, continuation); - return; - } - console.log("Downloading ", neededId); - OsmObject_1.OsmObject.DownloadObject(neededId, function (element) { - knownElements[neededId] = element; // assign the element for later, continue downloading the next element - DownloadAndContinue(neededIds, continuation); - }); - } - var neededIds = []; - for (var _i = 0, pending_1 = pending; _i < pending_1.length; _i++) { - var change = pending_1[_i]; - var id = change.elementId; - if (parseFloat(id.split("/")[1]) < 0) { - console.log("Detected a new element! Exciting!"); - } - else { - neededIds.push(id); - } - } - DownloadAndContinue(neededIds, function () { - // Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements' - // We apply the changes on them - for (var _i = 0, pending_2 = pending; _i < pending_2.length; _i++) { - var change = pending_2[_i]; - if (parseInt(change.elementId.split("/")[1]) < 0) { - // This is a new element - we should apply this on one of the new elements - for (var _a = 0, newElements_1 = newElements; _a < newElements_1.length; _a++) { - var newElement = newElements_1[_a]; - if (newElement.type + "/" + newElement.id === change.elementId) { - newElement.addTag(change.key, change.value); - } - } - } - else { - console.log(knownElements, change.elementId); - knownElements[change.elementId].addTag(change.key, change.value); - // note: addTag will flag changes with 'element.changed' internally - } - } - // Small sanity check for duplicate information - var changedElements = []; - for (var elementId in knownElements) { - var element = knownElements[elementId]; - if (element.changed) { - changedElements.push(element); - } - } - if (changedElements.length == 0 && newElements.length == 0) { - console.log("No changes in any object"); - return; - } - var handleMapping = function (idMapping) { - for (var oldId in idMapping) { - var newId = idMapping[oldId]; - var element = self._allElements.getElement(oldId); - element.data.id = newId; - self._allElements.addElementById(newId, element); - element.ping(); - } - }; - console.log("Beginning upload..."); - // At last, we build the changeset and upload - self.login.UploadChangeset(self._changesetComment, function (csId) { - var modifications = ""; - for (var _i = 0, changedElements_1 = changedElements; _i < changedElements_1.length; _i++) { - var element = changedElements_1[_i]; - if (!element.changed) { - continue; - } - modifications += element.ChangesetXML(csId) + "\n"; - } - var creations = ""; - for (var _a = 0, newElements_2 = newElements; _a < newElements_2.length; _a++) { - var newElement = newElements_2[_a]; - creations += newElement.ChangesetXML(csId); - } - var changes = "<osmChange version='0.6' generator='Mapcomplete 0.0.1'>"; - if (creations.length > 0) { - changes += - "<create>" + - creations + - "</create>"; - } - if (modifications.length > 0) { - changes += - "<modify>" + - modifications + - "</modify>"; - } - changes += "</osmChange>"; - return changes; - }, handleMapping, optionalContinuationWrapped); - }); - }; - Changes.prototype.asQuestions = function (qs) { - var ls = []; - for (var i in qs) { - ls.push(new Question(this, qs[i])); - } - return ls; - }; - Changes._nextId = -1; // New assined ID's are negative - return Changes; -}()); -exports.Changes = Changes; diff --git a/Logic/ElementStorage.js b/Logic/ElementStorage.js deleted file mode 100644 index d8517a8..0000000 --- a/Logic/ElementStorage.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ElementStorage = void 0; -/** - * Keeps track of a dictionary 'elementID' -> element - */ -var UIEventSource_1 = require("../UI/UIEventSource"); -var ElementStorage = /** @class */ (function () { - function ElementStorage() { - this._elements = []; - } - ElementStorage.prototype.addElementById = function (id, eventSource) { - this._elements[id] = eventSource; - }; - ElementStorage.prototype.addElement = function (element) { - var eventSource = new UIEventSource_1.UIEventSource(element.properties); - this._elements[element.properties.id] = eventSource; - return eventSource; - }; - ElementStorage.prototype.addOrGetElement = function (element) { - var elementId = element.properties.id; - if (elementId in this._elements) { - var es = this._elements[elementId]; - var keptKeys = es.data; - // The element already exists - // We add all the new keys to the old keys - for (var k in element.properties) { - var v = element.properties[k]; - if (keptKeys[k] !== v) { - keptKeys[k] = v; - es.ping(); - } - } - return es; - } - else { - return this.addElement(element); - } - }; - ElementStorage.prototype.getElement = function (elementId) { - if (elementId in this._elements) { - return this._elements[elementId]; - } - console.log("Can not find eventsource with id ", elementId); - }; - ElementStorage.prototype.removeId = function (oldId) { - delete this._elements[oldId]; - }; - return ElementStorage; -}()); -exports.ElementStorage = ElementStorage; diff --git a/Logic/FilteredLayer.js b/Logic/FilteredLayer.js deleted file mode 100644 index ec11e51..0000000 --- a/Logic/FilteredLayer.js +++ /dev/null @@ -1,173 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FilteredLayer = void 0; -var TagsFilter_1 = require("./TagsFilter"); -var UIEventSource_1 = require("../UI/UIEventSource"); -var leaflet_1 = require("leaflet"); -var GeoOperations_1 = require("./GeoOperations"); -/*** - * A filtered layer is a layer which offers a 'set-data' function - * It is initialized with a tagfilter. - * - * When geojson-data is given to 'setData', all the geojson matching the filter, is rendered on this layer. - * If it is not rendered, it is returned in a 'leftOver'-geojson; which can be consumed by the next layer. - * - * This also makes sure that no objects are rendered twice if they are applicable on two layers - */ -var FilteredLayer = /** @class */ (function () { - function FilteredLayer(name, map, storage, changes, filters, maxAllowedOverlap, style, selectedElement, showOnPopup) { - this.isDisplayed = new UIEventSource_1.UIEventSource(true); - /** List of new elements, geojson features - */ - this._newElements = []; - this._selectedElement = selectedElement; - this._showOnPopup = showOnPopup; - if (style === undefined) { - style = function () { - return {}; - }; - } - this.name = name; - this._map = map; - this.filters = filters; - this._style = style; - this._storage = storage; - this._maxAllowedOverlap = maxAllowedOverlap; - var self = this; - this.isDisplayed.addCallback(function (isDisplayed) { - if (self._geolayer !== undefined && self._geolayer !== null) { - if (isDisplayed) { - self._geolayer.addTo(self._map.map); - } - else { - self._map.map.removeLayer(self._geolayer); - } - } - }); - } - /** - * The main function to load data into this layer. - * The data that is NOT used by this layer, is returned as a geojson object; the other data is rendered - */ - FilteredLayer.prototype.SetApplicableData = function (geojson) { - var leftoverFeatures = []; - var selfFeatures = []; - for (var _i = 0, _a = geojson.features; _i < _a.length; _i++) { - var feature = _a[_i]; - // feature.properties contains all the properties - var tags = TagsFilter_1.TagUtils.proprtiesToKV(feature.properties); - if (this.filters.matches(tags)) { - selfFeatures.push(feature); - } - else { - leftoverFeatures.push(feature); - } - } - this.RenderLayer({ - type: "FeatureCollection", - features: selfFeatures - }); - var notShadowed = []; - for (var _b = 0, leftoverFeatures_1 = leftoverFeatures; _b < leftoverFeatures_1.length; _b++) { - var feature = leftoverFeatures_1[_b]; - if (this._maxAllowedOverlap !== undefined && this._maxAllowedOverlap > 0) { - if (GeoOperations_1.GeoOperations.featureIsContainedInAny(feature, selfFeatures, this._maxAllowedOverlap)) { - // This feature is filtered away - continue; - } - } - notShadowed.push(feature); - } - return { - type: "FeatureCollection", - features: notShadowed - }; - }; - FilteredLayer.prototype.AddNewElement = function (element) { - this._newElements.push(element); - console.log("Element added"); - this.RenderLayer(this._dataFromOverpass); // Update the layer - }; - FilteredLayer.prototype.RenderLayer = function (data) { - var self = this; - if (this._geolayer !== undefined && this._geolayer !== null) { - this._map.map.removeLayer(this._geolayer); - } - this._dataFromOverpass = data; - var fusedFeatures = []; - var idsFromOverpass = []; - for (var _i = 0, _a = data.features; _i < _a.length; _i++) { - var feature = _a[_i]; - idsFromOverpass.push(feature.properties.id); - fusedFeatures.push(feature); - } - for (var _b = 0, _c = this._newElements; _b < _c.length; _b++) { - var feature = _c[_b]; - if (idsFromOverpass.indexOf(feature.properties.id) < 0) { - // This element is not yet uploaded or not yet visible in overpass - // We include it in the layer - fusedFeatures.push(feature); - } - } - // We use a new, fused dataset - data = { - type: "FeatureCollection", - features: fusedFeatures - }; - // The data is split in two parts: the poinst and the rest - // The points get a special treatment in order to render them properly - // Note that some features might get a point representation as well - this._geolayer = leaflet_1.default.geoJSON(data, { - style: function (feature) { - return self._style(feature.properties); - }, - pointToLayer: function (feature, latLng) { - var style = self._style(feature.properties); - var marker; - if (style.icon === undefined) { - marker = leaflet_1.default.circle(latLng, { - radius: 25, - color: style.color - }); - } - else { - marker = leaflet_1.default.marker(latLng, { - icon: style.icon - }); - } - return marker; - }, - onEachFeature: function (feature, layer) { - var eventSource = self._storage.addOrGetElement(feature); - eventSource.addCallback(function () { - if (layer.setIcon) { - layer.setIcon(self._style(feature.properties).icon); - } - else { - console.log("UPdating", layer); - self._geolayer.setStyle(function (feature) { - return self._style(feature.properties); - }); - } - }); - layer.on("click", function (e) { - console.log("Selected ", feature); - self._selectedElement.setData(feature.properties); - var uiElement = self._showOnPopup(eventSource); - var popup = leaflet_1.default.popup() - .setContent(uiElement.Render()) - .setLatLng(e.latlng) - .openOn(self._map.map); - uiElement.Update(); - uiElement.Activate(); - leaflet_1.default.DomEvent.stop(e); // Marks the event as consumed - }); - } - }); - if (this.isDisplayed.data) { - this._geolayer.addTo(this._map.map); - } - }; - return FilteredLayer; -}()); -exports.FilteredLayer = FilteredLayer; diff --git a/Logic/GeoLocationHandler.js b/Logic/GeoLocationHandler.js deleted file mode 100644 index 75c23d4..0000000 --- a/Logic/GeoLocationHandler.js +++ /dev/null @@ -1,117 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoLocationHandler = void 0; -var UIEventSource_1 = require("../UI/UIEventSource"); -var UIElement_1 = require("../UI/UIElement"); -var leaflet_1 = require("leaflet"); -var Helpers_1 = require("../Helpers"); -var GeoLocationHandler = /** @class */ (function (_super) { - __extends(GeoLocationHandler, _super); - function GeoLocationHandler(map) { - var _this = _super.call(this, undefined) || this; - _this.currentLocation = new UIEventSource_1.UIEventSource(undefined); - _this._isActive = new UIEventSource_1.UIEventSource(false); - _this._permission = new UIEventSource_1.UIEventSource(""); - _this._map = map; - _this.ListenTo(_this.currentLocation); - _this.ListenTo(_this._isActive); - _this.ListenTo(_this._permission); - var self = _this; - function onAccuratePositionProgress(e) { - console.log(e.accuracy); - console.log(e.latlng); - self.currentLocation.setData({ latlng: e.latlng, accuracy: e.accuracy }); - } - function onAccuratePositionFound(e) { - console.log(e.accuracy); - console.log(e.latlng); - self.currentLocation.setData({ latlng: e.latlng, accuracy: e.accuracy }); - } - function onAccuratePositionError(e) { - console.log("onerror", e.message); - } - map.map.on('accuratepositionprogress', onAccuratePositionProgress); - map.map.on('accuratepositionfound', onAccuratePositionFound); - map.map.on('accuratepositionerror', onAccuratePositionError); - var icon = leaflet_1.default.icon({ - iconUrl: './assets/crosshair-blue.svg', - iconSize: [40, 40], - iconAnchor: [20, 20], - }); - _this.currentLocation.addCallback(function (location) { - var newMarker = leaflet_1.default.marker(location.latlng, { icon: icon }); - newMarker.addTo(map.map); - if (self._marker !== undefined) { - map.map.removeLayer(self._marker); - } - self._marker = newMarker; - }); - navigator.permissions.query({ name: 'geolocation' }) - .then(function (status) { - console.log("Geolocation is already", status); - if (status.state === "granted") { - self.StartGeolocating(); - } - self._permission.setData(status.state); - status.onchange = function () { - self._permission.setData(status.state); - }; - }); - _this.HideOnEmpty(true); - return _this; - } - GeoLocationHandler.prototype.InnerRender = function () { - if (this.currentLocation.data) { - return "<img src='./assets/crosshair-blue.svg' alt='locate me'>"; - } - if (this._isActive.data) { - return "<img src='./assets/crosshair-blue-center.svg' alt='locate me'>"; - } - return "<img src='./assets/crosshair.svg' alt='locate me'>"; - }; - GeoLocationHandler.prototype.StartGeolocating = function () { - var self = this; - if (self._permission.data === "denied") { - return ""; - } - if (self.currentLocation.data !== undefined) { - self._map.map.flyTo(self.currentLocation.data.latlng, 18); - } - console.log("Searching location using GPS"); - self._map.map.findAccuratePosition({ - maxWait: 10000, - desiredAccuracy: 50 // defaults to 20 - }); - if (!self._isActive.data) { - self._isActive.setData(true); - Helpers_1.Helpers.DoEvery(60000, function () { - self._map.map.findAccuratePosition({ - maxWait: 10000, - desiredAccuracy: 50 // defaults to 20 - }); - }); - } - }; - GeoLocationHandler.prototype.InnerUpdate = function (htmlElement) { - _super.prototype.InnerUpdate.call(this, htmlElement); - var self = this; - htmlElement.onclick = function () { - self.StartGeolocating(); - }; - }; - return GeoLocationHandler; -}(UIElement_1.UIElement)); -exports.GeoLocationHandler = GeoLocationHandler; diff --git a/Logic/GeoOperations.js b/Logic/GeoOperations.js deleted file mode 100644 index f3d1f4e..0000000 --- a/Logic/GeoOperations.js +++ /dev/null @@ -1,185 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.GeoOperations = void 0; -var turf = require("turf"); -var GeoOperations = /** @class */ (function () { - function GeoOperations() { - } - GeoOperations.surfaceAreaInSqMeters = function (feature) { - return turf.area(feature); - }; - GeoOperations.featureIsContainedInAny = function (feature, shouldNotContain, maxOverlapPercentage) { - // Returns 'false' if no problematic intersection is found - if (feature.geometry.type === "Point") { - var coor = feature.geometry.coordinates; - for (var _i = 0, shouldNotContain_1 = shouldNotContain; _i < shouldNotContain_1.length; _i++) { - var shouldNotContainElement = shouldNotContain_1[_i]; - var shouldNotContainBBox = BBox.get(shouldNotContainElement); - var featureBBox = BBox.get(feature); - if (!featureBBox.overlapsWith(shouldNotContainBBox)) { - continue; - } - if (this.inside(coor, shouldNotContainElement)) { - return true; - } - } - return false; - } - if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") { - var poly = feature; - var featureBBox = BBox.get(feature); - var featureSurface = GeoOperations.surfaceAreaInSqMeters(poly); - for (var _a = 0, shouldNotContain_2 = shouldNotContain; _a < shouldNotContain_2.length; _a++) { - var shouldNotContainElement = shouldNotContain_2[_a]; - var shouldNotContainBBox = BBox.get(shouldNotContainElement); - var overlaps = featureBBox.overlapsWith(shouldNotContainBBox); - if (!overlaps) { - continue; - } - // Calculate the surface area of the intersection - // If it is too big, refuse - try { - var intersection = turf.intersect(poly, shouldNotContainElement); - if (intersection == null) { - continue; - } - var intersectionSize = turf.area(intersection); - var ratio = intersectionSize / featureSurface; - if (ratio * 100 >= maxOverlapPercentage) { - console.log("Refused", poly.id, " due to ", shouldNotContainElement.id, "intersection ratio is ", ratio, "which is bigger then the target ratio of ", (maxOverlapPercentage / 100)); - return true; - } - } - catch (exception) { - console.log("EXCEPTION CAUGHT WHILE INTERSECTING: ", exception); - // We assume that this failed due to an intersection - return true; - } - } - return false; // No problematic intersections found - } - return false; - }; - /** - * Simple check: that every point of the polygon is inside the container - * @param polygon - * @param container - */ - GeoOperations.isPolygonInside = function (polygon, container) { - for (var _i = 0, _a = polygon.geometry.coordinates[0]; _i < _a.length; _i++) { - var coor = _a[_i]; - if (!GeoOperations.inside(coor, container)) { - return false; - } - } - return true; - }; - /** - * Simple check: one point of the polygon is inside the container - * @param polygon - * @param container - */ - GeoOperations.isPolygonTouching = function (polygon, container) { - for (var _i = 0, _a = polygon.geometry.coordinates[0]; _i < _a.length; _i++) { - var coor = _a[_i]; - if (GeoOperations.inside(coor, container)) { - return true; - } - } - return false; - }; - GeoOperations.inside = function (pointCoordinate, feature) { - // ray-casting algorithm based on - // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - if (feature.geometry.type === "Point") { - return false; - } - var x = pointCoordinate[0]; - var y = pointCoordinate[1]; - var poly = feature.geometry.coordinates[0]; - var inside = false; - for (var i = 0, j = poly.length - 1; i < poly.length; j = i++) { - var coori = poly[i]; - var coorj = poly[j]; - var xi = coori[0]; - var yi = coori[1]; - var xj = coorj[0]; - var yj = coorj[1]; - var intersect = ((yi > y) != (yj > y)) - && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - if (intersect) { - inside = !inside; - } - } - return inside; - }; - ; - return GeoOperations; -}()); -exports.GeoOperations = GeoOperations; -var BBox = /** @class */ (function () { - function BBox(coordinates) { - this.maxLat = Number.MIN_VALUE; - this.maxLon = Number.MIN_VALUE; - this.minLat = Number.MAX_VALUE; - this.minLon = Number.MAX_VALUE; - for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) { - var coordinate = coordinates_1[_i]; - this.maxLon = Math.max(this.maxLon, coordinate[0]); - this.maxLat = Math.max(this.maxLat, coordinate[1]); - this.minLon = Math.min(this.minLon, coordinate[0]); - this.minLat = Math.min(this.minLat, coordinate[1]); - } - this.check(); - } - BBox.prototype.check = function () { - if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { - console.log(this); - throw "BBOX has NAN"; - } - }; - BBox.prototype.overlapsWith = function (other) { - this.check(); - other.check(); - if (this.maxLon < other.minLon) { - return false; - } - if (this.maxLat < other.minLat) { - return false; - } - if (this.minLon > other.maxLon) { - return false; - } - if (this.minLat > other.maxLat) { - return false; - } - return true; - }; - BBox.get = function (feature) { - if (feature.bbox === undefined) { - if (feature.geometry.type === "MultiPolygon") { - var coordinates = []; - for (var _i = 0, _a = feature.geometry.coordinates; _i < _a.length; _i++) { - var coorlist = _a[_i]; - coordinates = coordinates.concat(coorlist[0]); - } - feature.bbox = new BBox(coordinates); - } - else if (feature.geometry.type === "Polygon") { - feature.bbox = new BBox(feature.geometry.coordinates[0]); - } - else if (feature.geometry.type === "LineString") { - feature.bbox = new BBox(feature.geometry.coordinates); - } - else if (feature.geometry.type === "Point") { - // Point - feature.bbox = new BBox([feature.geometry.coordinates]); - } - else { - throw "Cannot calculate bbox, unknown type " + feature.geometry.type; - } - } - return feature.bbox; - }; - return BBox; -}()); diff --git a/Logic/Geocoding.js b/Logic/Geocoding.js deleted file mode 100644 index afde3b4..0000000 --- a/Logic/Geocoding.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Geocoding = void 0; -var $ = require("jquery"); -var Geocoding = /** @class */ (function () { - function Geocoding() { - } - Geocoding.Search = function (query, basemap, handleResult, onFail) { - var b = basemap.map.getBounds(); - console.log(b); - $.getJSON(Geocoding.host + "format=json&limit=1&viewbox=" + - (b.getEast() + "," + b.getNorth() + "," + b.getWest() + "," + b.getSouth()) + - "&accept-language=nl&q=" + query, function (data) { - handleResult(data); - }).fail(function () { - onFail(); - }); - }; - Geocoding.host = "https://nominatim.openstreetmap.org/search?"; - return Geocoding; -}()); -exports.Geocoding = Geocoding; diff --git a/Logic/ImageSearcher.js b/Logic/ImageSearcher.js deleted file mode 100644 index e107489..0000000 --- a/Logic/ImageSearcher.js +++ /dev/null @@ -1,185 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageSearcher = void 0; -var UIEventSource_1 = require("../UI/UIEventSource"); -var Wikimedia_1 = require("./Wikimedia"); -var WikimediaImage_1 = require("../UI/Image/WikimediaImage"); -var SimpleImageElement_1 = require("../UI/Image/SimpleImageElement"); -var ImgurImage_1 = require("../UI/Image/ImgurImage"); -/** - * There are multiple way to fetch images for an object - * 1) There is an image tag - * 2) There is an image tag, the image tag contains multiple ';'-seperated URLS - * 3) there are multiple image tags, e.g. 'image', 'image:0', 'image:1', and 'image_0', 'image_1' - however, these are pretty rare so we are gonna ignore them - * 4) There is a wikimedia_commons-tag, which either has a 'File': or a 'category:' containing images - * 5) There is a wikidata-tag, and the wikidata item either has an 'image' attribute or has 'a link to a wikimedia commons category' - * 6) There is a wikipedia article, from which we can deduct the wikidata item - * - * For some images, author and license should be shown - */ -/** - * Class which search for all the possible locations for images and which builds a list of UI-elements for it. - * Note that this list is embedded into an UIEVentSource, ready to put it into a carousel - */ -var ImageSearcher = /** @class */ (function (_super) { - __extends(ImageSearcher, _super); - function ImageSearcher(tags, changes) { - var _this = _super.call(this, []) || this; - _this._wdItem = new UIEventSource_1.UIEventSource(""); - _this._commons = new UIEventSource_1.UIEventSource(""); - _this._activated = false; - _this._deletedImages = new UIEventSource_1.UIEventSource([]); - _this._tags = tags; - _this._changes = changes; - var self = _this; - _this._wdItem.addCallback(function () { - // Load the wikidata item, then detect usage on 'commons' - var wikidataId = self._wdItem.data; - // @ts-ignore - if (wikidataId.startsWith("Q")) { - wikidataId = wikidataId.substr(1); - } - Wikimedia_1.Wikimedia.GetWikiData(parseInt(wikidataId), function (wd) { - self.AddImage(wd.image); - Wikimedia_1.Wikimedia.GetCategoryFiles(wd.commonsWiki, function (images) { - for (var _i = 0, _a = images.images; _i < _a.length; _i++) { - var image = _a[_i]; - // @ts-ignore - if (image.startsWith("File:")) { - self.AddImage(image); - } - } - }); - }); - }); - _this._commons.addCallback(function () { - var commons = self._commons.data; - // @ts-ignore - if (commons.startsWith("Category:")) { - Wikimedia_1.Wikimedia.GetCategoryFiles(commons, function (images) { - for (var _i = 0, _a = images.images; _i < _a.length; _i++) { - var image = _a[_i]; - // @ts-ignore - if (image.startsWith("File:")) { - self.AddImage(image); - } - } - }); - } - else { // @ts-ignore - if (commons.startsWith("File:")) { - self.AddImage(commons); - } - } - }); - return _this; - } - ImageSearcher.prototype.AddImage = function (url) { - if (url === undefined || url === null || url === "") { - return; - } - for (var _i = 0, _a = this.data; _i < _a.length; _i++) { - var el = _a[_i]; - if (el === url) { - return; - } - } - this.data.push(url); - this.ping(); - }; - ImageSearcher.prototype.ImageKey = function (url) { - var tgs = this._tags.data; - for (var key in tgs) { - if (tgs[key] === url) { - return key; - } - } - return undefined; - }; - ImageSearcher.prototype.IsDeletable = function (url) { - return this.ImageKey(url) !== undefined; - }; - ImageSearcher.prototype.Delete = function (url) { - var key = this.ImageKey(url); - if (key === undefined) { - return; - } - console.log("Deleting image...", key, " --> ", url); - this._changes.addChange(this._tags.data.id, key, ""); - this._deletedImages.data.push(url); - this._deletedImages.ping(); - }; - ImageSearcher.prototype.Activate = function () { - if (this._activated) { - return; - } - this._activated = true; - this.LoadImages(); - var self = this; - this._tags.addCallback(function () { return self.LoadImages(); }); - }; - ImageSearcher.prototype.LoadImages = function () { - if (!this._activated) { - return; - } - var imageTag = this._tags.data.image; - if (imageTag !== undefined) { - var bareImages = imageTag.split(";"); - for (var _i = 0, bareImages_1 = bareImages; _i < bareImages_1.length; _i++) { - var bareImage = bareImages_1[_i]; - this.AddImage(bareImage); - } - } - for (var key in this._tags.data) { - // @ts-ignore - if (key.startsWith("image:")) { - var url = this._tags.data[key]; - this.AddImage(url); - } - } - var wdItem = this._tags.data.wikidata; - if (wdItem !== undefined) { - this._wdItem.setData(wdItem); - } - var commons = this._tags.data.wikimedia_commons; - if (commons !== undefined) { - this._commons.setData(commons); - } - }; - /*** - * Creates either a 'simpleimage' or a 'wikimediaimage' based on the string - * @param url - * @constructor - */ - ImageSearcher.CreateImageElement = function (url) { - // @ts-ignore - if (url.startsWith("File:")) { - return new WikimediaImage_1.WikimediaImage(url); - } - else if (url.startsWith("https://commons.wikimedia.org/wiki/")) { - var commons = url.substr("https://commons.wikimedia.org/wiki/".length); - return new WikimediaImage_1.WikimediaImage(commons); - } - else if (url.startsWith("https://i.imgur.com/")) { - return new ImgurImage_1.ImgurImage(url); - } - else { - return new SimpleImageElement_1.SimpleImageElement(new UIEventSource_1.UIEventSource(url)); - } - }; - return ImageSearcher; -}(UIEventSource_1.UIEventSource)); -exports.ImageSearcher = ImageSearcher; diff --git a/Logic/Imgur.js b/Logic/Imgur.js deleted file mode 100644 index 3577753..0000000 --- a/Logic/Imgur.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Imgur = void 0; -var jquery_1 = require("jquery"); -var Wikimedia_1 = require("./Wikimedia"); -var Imgur = /** @class */ (function () { - function Imgur() { - } - Imgur.uploadMultiple = function (title, description, blobs, handleSuccessfullUpload, allDone, offset) { - if (offset === void 0) { offset = 0; } - if (blobs.length == offset) { - allDone(); - return; - } - var blob = blobs.item(offset); - var self = this; - this.uploadImage(title, description, blob, function (imageUrl) { - handleSuccessfullUpload(imageUrl); - self.uploadMultiple(title, description, blobs, handleSuccessfullUpload, allDone, offset + 1); - }); - }; - Imgur.getDescriptionOfImage = function (url, handleDescription) { - var hash = url.substr("https://i.imgur.com/".length).split(".jpg")[0]; - var apiUrl = 'https://api.imgur.com/3/image/' + hash; - var apiKey = '7070e7167f0a25a'; - var settings = { - async: true, - crossDomain: true, - processData: false, - contentType: false, - type: 'GET', - url: apiUrl, - headers: { - Authorization: 'Client-ID ' + apiKey, - Accept: 'application/json', - }, - }; - jquery_1.default.ajax(settings).done(function (response) { - var descr = response.data.description; - var data = {}; - for (var _i = 0, _a = descr.split("\n"); _i < _a.length; _i++) { - var tag = _a[_i]; - var kv = tag.split(":"); - var k = kv[0]; - var v = kv[1].replace("\r", ""); - data[k] = v; - } - console.log(data); - var licenseInfo = new Wikimedia_1.LicenseInfo(); - licenseInfo.licenseShortName = data.license; - licenseInfo.artist = data.author; - handleDescription(licenseInfo); - }).fail(function (reason) { - console.log("Getting metadata from to IMGUR failed", reason); - }); - }; - Imgur.uploadImage = function (title, description, blob, handleSuccessfullUpload) { - var apiUrl = 'https://api.imgur.com/3/image'; - var apiKey = '7070e7167f0a25a'; - var settings = { - async: true, - crossDomain: true, - processData: false, - contentType: false, - type: 'POST', - url: apiUrl, - headers: { - Authorization: 'Client-ID ' + apiKey, - Accept: 'application/json', - }, - mimeType: 'multipart/form-data', - }; - var formData = new FormData(); - formData.append('image', blob); - formData.append("title", title); - formData.append("description", description); - // @ts-ignore - settings.data = formData; - // Response contains stringified JSON - // Image URL available at response.data.link - jquery_1.default.ajax(settings).done(function (response) { - response = JSON.parse(response); - handleSuccessfullUpload(response.data.link); - }).fail(function (reason) { - console.log("Uploading to IMGUR failed", reason); - }); - }; - return Imgur; -}()); -exports.Imgur = Imgur; diff --git a/Logic/LayerUpdater.js b/Logic/LayerUpdater.js deleted file mode 100644 index 583cf31..0000000 --- a/Logic/LayerUpdater.js +++ /dev/null @@ -1,99 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LayerUpdater = void 0; -var Overpass_1 = require("./Overpass"); -var TagsFilter_1 = require("./TagsFilter"); -var UIEventSource_1 = require("../UI/UIEventSource"); -var LayerUpdater = /** @class */ (function () { - /** - * The most important layer should go first, as that one gets first pick for the questions - * @param map - * @param minzoom - * @param layers - */ - function LayerUpdater(map, minzoom, layers) { - this.runningQuery = new UIEventSource_1.UIEventSource(false); - this._map = map; - this._layers = layers; - this._minzoom = minzoom; - var filters = []; - for (var _i = 0, layers_1 = layers; _i < layers_1.length; _i++) { - var layer = layers_1[_i]; - filters.push(layer.filters); - } - this._overpass = new Overpass_1.Overpass(new TagsFilter_1.Or(filters)); - var self = this; - map.Location.addCallback(function () { - self.update(); - }); - } - LayerUpdater.prototype.handleData = function (geojson) { - this.runningQuery.setData(false); - for (var _i = 0, _a = this._layers; _i < _a.length; _i++) { - var layer = _a[_i]; - geojson = layer.SetApplicableData(geojson); - } - if (geojson.features.length > 0) { - console.log("Got some leftovers: ", geojson); - } - }; - LayerUpdater.prototype.handleFail = function (reason) { - console.log("QUERY FAILED", reason); - console.log("Retrying in 1s"); - this.previousBounds = undefined; - var self = this; - window.setTimeout(function () { self.update(); }, 1000); - }; - LayerUpdater.prototype.update = function () { - if (this.IsInBounds()) { - return; - } - console.log("Zoom level: ", this._map.map.getZoom(), "Least needed zoom:", this._minzoom); - if (this._map.map.getZoom() < this._minzoom || this._map.Location.data.zoom < this._minzoom) { - console.log("Not running query: zoom not sufficient"); - return; - } - if (this.runningQuery.data) { - console.log("Still running a query, skip"); - } - var bbox = this.buildBboxFor(); - this.runningQuery.setData(true); - var self = this; - this._overpass.queryGeoJson(bbox, function (data) { - self.handleData(data); - }, function (reason) { - self.handleFail(reason); - }); - }; - LayerUpdater.prototype.buildBboxFor = function () { - var b = this._map.map.getBounds(); - var diff = 0.07; - var n = b.getNorth() + diff; - var e = b.getEast() + diff; - var s = b.getSouth() - diff; - var w = b.getWest() - diff; - this.previousBounds = { north: n, east: e, south: s, west: w }; - return "[bbox:" + s + "," + w + "," + n + "," + e + "]"; - }; - LayerUpdater.prototype.IsInBounds = function () { - if (this.previousBounds === undefined) { - return false; - } - var b = this._map.map.getBounds(); - if (b.getSouth() < this.previousBounds.south) { - return false; - } - if (b.getNorth() > this.previousBounds.north) { - return false; - } - if (b.getEast() > this.previousBounds.east) { - return false; - } - if (b.getWest() < this.previousBounds.west) { - return false; - } - return true; - }; - return LayerUpdater; -}()); -exports.LayerUpdater = LayerUpdater; diff --git a/Logic/OsmConnection.js b/Logic/OsmConnection.js deleted file mode 100644 index f56f9cf..0000000 --- a/Logic/OsmConnection.js +++ /dev/null @@ -1,256 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OsmConnection = exports.UserDetails = void 0; -// @ts-ignore -var osm_auth_1 = require("osm-auth"); -var UIEventSource_1 = require("../UI/UIEventSource"); -var UserDetails = /** @class */ (function () { - function UserDetails() { - this.loggedIn = false; - this.name = "Not logged in"; - this.csCount = 0; - this.unreadMessages = 0; - this.totalMessages = 0; - } - return UserDetails; -}()); -exports.UserDetails = UserDetails; -var OsmConnection = /** @class */ (function () { - function OsmConnection(dryRun) { - this.auth = new osm_auth_1.default({ - oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', - oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', - auto: true // show a login form if the user is not authenticated and - // you try to do a call - }); - this.preferences = new UIEventSource_1.UIEventSource({}); - this.preferenceSources = {}; - this.userDetails = new UIEventSource_1.UIEventSource(new UserDetails()); - this.userDetails.data.osmConnection = this; - this.userDetails.data.dryRun = dryRun; - this._dryRun = dryRun; - if (this.auth.authenticated()) { - this.AttemptLogin(); // Also updates the user badge - } - else { - console.log("Not authenticated"); - } - if (dryRun) { - console.log("DRYRUN ENABLED"); - } - } - OsmConnection.prototype.LogOut = function () { - this.auth.logout(); - this.userDetails.data.loggedIn = false; - this.userDetails.ping(); - console.log("Logged out"); - }; - OsmConnection.prototype.AttemptLogin = function () { - var self = this; - this.auth.xhr({ - method: 'GET', - path: '/api/0.6/user/details' - }, function (err, details) { - var _a; - if (err != null) { - console.log(err); - self.auth.logout(); - self.userDetails.data.loggedIn = false; - self.userDetails.ping(); - } - if (details == null) { - return; - } - self.UpdatePreferences(); - // details is an XML DOM of user details - var userInfo = details.getElementsByTagName("user")[0]; - // let moreDetails = new DOMParser().parseFromString(userInfo.innerHTML, "text/xml"); - var data = self.userDetails.data; - data.loggedIn = true; - console.log("Login completed, userinfo is ", userInfo); - data.name = userInfo.getAttribute('display_name'); - data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count"); - data.img = undefined; - var imgEl = userInfo.getElementsByTagName("img"); - if (imgEl !== undefined && imgEl[0] !== undefined) { - data.img = imgEl[0].getAttribute("href"); - } - data.img = (_a = data.img) !== null && _a !== void 0 ? _a : "./assets/osm-logo.svg"; - var homeEl = userInfo.getElementsByTagName("home"); - if (homeEl !== undefined && homeEl[0] !== undefined) { - var lat = parseFloat(homeEl[0].getAttribute("lat")); - var lon = parseFloat(homeEl[0].getAttribute("lon")); - data.home = { lat: lat, lon: lon }; - } - var messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0]; - data.unreadMessages = parseInt(messages.getAttribute("unread")); - data.totalMessages = parseInt(messages.getAttribute("count")); - self.userDetails.ping(); - }); - }; - /** - * All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate - */ - OsmConnection.prototype.registerActivateOsmAUthenticationClass = function () { - var self = this; - var authElements = document.getElementsByClassName("activate-osm-authentication"); - for (var i = 0; i < authElements.length; i++) { - var element = authElements.item(i); - // @ts-ignore - element.onclick = function () { - self.AttemptLogin(); - }; - } - }; - OsmConnection.prototype.GetPreference = function (key) { - var _this = this; - if (this.preferenceSources[key] !== undefined) { - return this.preferenceSources[key]; - } - if (this.userDetails.data.loggedIn) { - this.UpdatePreferences(); - } - var pref = new UIEventSource_1.UIEventSource(this.preferences.data[key]); - pref.addCallback(function (v) { - _this.SetPreference(key, v); - }); - this.preferences.addCallback(function (prefs) { - if (prefs[key] !== undefined) { - pref.setData(prefs[key]); - } - }); - this.preferenceSources[key] = pref; - return pref; - }; - OsmConnection.prototype.UpdatePreferences = function () { - var self = this; - this.auth.xhr({ - method: 'GET', - path: '/api/0.6/user/preferences' - }, function (error, value) { - if (error) { - console.log("Could not load preferences", error); - return; - } - var prefs = value.getElementsByTagName("preference"); - for (var i = 0; i < prefs.length; i++) { - var pref = prefs[i]; - var k = pref.getAttribute("k"); - var v = pref.getAttribute("v"); - self.preferences.data[k] = v; - } - self.preferences.ping(); - }); - }; - OsmConnection.prototype.SetPreference = function (k, v) { - if (!this.userDetails.data.loggedIn) { - console.log("Not saving preference: user not logged in"); - return; - } - if (this.preferences.data[k] === v) { - console.log("Not updating preference", k, " to ", v, "not changed"); - return; - } - console.log("Updating preference", k, " to ", v); - this.preferences.data[k] = v; - this.preferences.ping(); - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/user/preferences/' + k, - options: { header: { 'Content-Type': 'text/plain' } }, - content: v - }, function (error, result) { - if (error) { - console.log("Could not set preference", error); - return; - } - console.log("Preference written!", result == "" ? "OK" : result); - }); - }; - OsmConnection.parseUploadChangesetResponse = function (response) { - var nodes = response.getElementsByTagName("node"); - var mapping = {}; - // @ts-ignore - for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) { - var node = nodes_1[_i]; - var oldId = parseInt(node.attributes.old_id.value); - var newId = parseInt(node.attributes.new_id.value); - if (oldId !== undefined && newId !== undefined && - !isNaN(oldId) && !isNaN(newId)) { - mapping["node/" + oldId] = "node/" + newId; - } - } - return mapping; - }; - OsmConnection.prototype.UploadChangeset = function (comment, generateChangeXML, handleMapping, continuation) { - if (this._dryRun) { - console.log("NOT UPLOADING as dryrun is true"); - var changesetXML = generateChangeXML("123456"); - console.log(changesetXML); - continuation(); - return; - } - var self = this; - this.OpenChangeset(comment, function (csId) { - var changesetXML = generateChangeXML(csId); - self.AddChange(csId, changesetXML, function (csId, mapping) { - self.CloseChangeset(csId, continuation); - handleMapping(mapping); - }); - }); - this.userDetails.data.csCount++; - this.userDetails.ping(); - }; - OsmConnection.prototype.OpenChangeset = function (comment, continuation) { - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/create', - options: { header: { 'Content-Type': 'text/xml' } }, - content: '<osm><changeset>' + - '<tag k="created_by" v="MapComplete 0.0.0" />' + - '<tag k="comment" v="' + comment + '"/>' + - '</changeset></osm>' - }, function (err, response) { - if (response === undefined) { - console.log("err", err); - return; - } - else { - continuation(response); - } - }); - }; - OsmConnection.prototype.AddChange = function (changesetId, changesetXML, continuation) { - this.auth.xhr({ - method: 'POST', - options: { header: { 'Content-Type': 'text/xml' } }, - path: '/api/0.6/changeset/' + changesetId + '/upload', - content: changesetXML - }, function (err, response) { - if (response == null) { - console.log("err", err); - return; - } - var mapping = OsmConnection.parseUploadChangesetResponse(response); - console.log("Uplaoded changeset ", changesetId); - continuation(changesetId, mapping); - }); - }; - OsmConnection.prototype.CloseChangeset = function (changesetId, continuation) { - console.log("closing"); - this.auth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changesetId + '/close', - }, function (err, response) { - if (response == null) { - console.log("err", err); - } - console.log("Closed changeset ", changesetId); - if (continuation !== undefined) { - continuation(); - } - }); - }; - return OsmConnection; -}()); -exports.OsmConnection = OsmConnection; diff --git a/Logic/OsmImageUploadHandler.js b/Logic/OsmImageUploadHandler.js deleted file mode 100644 index 0af13f5..0000000 --- a/Logic/OsmImageUploadHandler.js +++ /dev/null @@ -1,56 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OsmImageUploadHandler = void 0; -var ImageUploadFlow_1 = require("../UI/ImageUploadFlow"); -var OsmImageUploadHandler = /** @class */ (function () { - function OsmImageUploadHandler(tags, userdetails, preferedLicense, changeHandler, slideShow) { - this._slideShow = slideShow; // To move the slideshow (if any) to the last, just added element - if (tags === undefined || userdetails === undefined || changeHandler === undefined) { - throw "Something is undefined"; - } - this._tags = tags; - this._changeHandler = changeHandler; - this._userdetails = userdetails; - this._preferedLicense = preferedLicense; - } - OsmImageUploadHandler.prototype.generateOptions = function (license) { - var _a; - var tags = this._tags.data; - var self = this; - var title = (_a = tags.name) !== null && _a !== void 0 ? _a : "Unknown area"; - var description = [ - "author:" + this._userdetails.data.name, - "license:" + license, - "wikidata:" + tags.wikidata, - "osmid:" + tags.id, - "name:" + tags.name - ].join("\n"); - var changes = this._changeHandler; - return { - title: title, - description: description, - handleURL: function (url) { - var freeIndex = 0; - while (tags["image:" + freeIndex] !== undefined) { - freeIndex++; - } - console.log("Adding image:" + freeIndex, url); - changes.addChange(tags.id, "image:" + freeIndex, url); - self._slideShow.MoveTo(-1); // set the last (thus newly added) image) to view - }, - allDone: function () { - changes.uploadAll(function () { - console.log("Writing changes..."); - }); - } - }; - }; - OsmImageUploadHandler.prototype.getUI = function () { - var self = this; - return new ImageUploadFlow_1.ImageUploadFlow(this._userdetails, this._preferedLicense, function (license) { - return self.generateOptions(license); - }); - }; - return OsmImageUploadHandler; -}()); -exports.OsmImageUploadHandler = OsmImageUploadHandler; diff --git a/Logic/OsmObject.js b/Logic/OsmObject.js deleted file mode 100644 index e9d4948..0000000 --- a/Logic/OsmObject.js +++ /dev/null @@ -1,160 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.OsmRelation = exports.OsmWay = exports.OsmNode = exports.OsmObject = void 0; -var $ = require("jquery"); -var OsmObject = /** @class */ (function () { - function OsmObject(type, id) { - this.tags = {}; - this.changed = false; - this.id = id; - this.type = type; - } - OsmObject.DownloadObject = function (id, continuation) { - var splitted = id.split("/"); - var type = splitted[0]; - var idN = splitted[1]; - switch (type) { - case ("node"): - return new OsmNode(idN).Download(continuation); - case ("way"): - return new OsmWay(idN).Download(continuation); - case ("relation"): - return new OsmRelation(idN).Download(continuation); - } - }; - /** - * Replaces all '"' (double quotes) by '"' - * Bugfix where names containing '"' were not uploaded, such as '"Het Zwin" nature reserve' - * @param string - * @constructor - */ - OsmObject.prototype.Escape = function (string) { - while (string.indexOf('"') >= 0) { - string = string.replace('"', '"'); - } - return string; - }; - /** - * Generates the changeset-XML for tags - * @constructor - */ - OsmObject.prototype.TagsXML = function () { - var tags = ""; - for (var key in this.tags) { - var v = this.tags[key]; - if (v !== "") { - tags += ' <tag k="' + this.Escape(key) + '" v="' + this.Escape(this.tags[key]) + '"/>\n'; - } - } - return tags; - }; - OsmObject.prototype.Download = function (continuation) { - var self = this; - $.getJSON("https://www.openstreetmap.org/api/0.6/" + this.type + "/" + this.id, function (data) { - var element = data.elements[0]; - self.tags = element.tags; - self.version = element.version; - self.SaveExtraData(element); - continuation(self); - }); - return this; - }; - OsmObject.prototype.addTag = function (k, v) { - if (k in this.tags) { - var oldV = this.tags[k]; - if (oldV == v) { - return; - } - console.log("WARNING: overwriting ", oldV, " with ", v, " for key ", k); - } - this.tags[k] = v; - this.changed = true; - }; - OsmObject.prototype.VersionXML = function () { - if (this.version === undefined) { - return ""; - } - return 'version="' + this.version + '"'; - }; - return OsmObject; -}()); -exports.OsmObject = OsmObject; -var OsmNode = /** @class */ (function (_super) { - __extends(OsmNode, _super); - function OsmNode(id) { - return _super.call(this, "node", id) || this; - } - OsmNode.prototype.ChangesetXML = function (changesetId) { - var tags = this.TagsXML(); - var change = ' <node id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + ' lat="' + this.lat + '" lon="' + this.lon + '">\n' + - tags + - ' </node>\n'; - return change; - }; - OsmNode.prototype.SaveExtraData = function (element) { - this.lat = element.lat; - this.lon = element.lon; - }; - return OsmNode; -}(OsmObject)); -exports.OsmNode = OsmNode; -var OsmWay = /** @class */ (function (_super) { - __extends(OsmWay, _super); - function OsmWay(id) { - return _super.call(this, "way", id) || this; - } - OsmWay.prototype.ChangesetXML = function (changesetId) { - var tags = this.TagsXML(); - var nds = ""; - for (var node in this.nodes) { - nds += ' <nd ref="' + this.nodes[node] + '"/>\n'; - } - var change = ' <way id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + '>\n' + - nds + - tags + - ' </way>\n'; - return change; - }; - OsmWay.prototype.SaveExtraData = function (element) { - this.nodes = element.nodes; - }; - return OsmWay; -}(OsmObject)); -exports.OsmWay = OsmWay; -var OsmRelation = /** @class */ (function (_super) { - __extends(OsmRelation, _super); - function OsmRelation(id) { - return _super.call(this, "relation", id) || this; - } - OsmRelation.prototype.ChangesetXML = function (changesetId) { - var members = ""; - for (var memberI in this.members) { - var member = this.members[memberI]; - members += ' <member type="' + member.type + '" ref="' + member.ref + '" role="' + member.role + '"/>\n'; - } - var tags = this.TagsXML(); - var change = ' <relation id="' + this.id + '" changeset="' + changesetId + '" ' + this.VersionXML() + '>\n' + - members + - tags + - ' </relation>\n'; - return change; - }; - OsmRelation.prototype.SaveExtraData = function (element) { - this.members = element.members; - }; - return OsmRelation; -}(OsmObject)); -exports.OsmRelation = OsmRelation; diff --git a/Logic/Overpass.js b/Logic/Overpass.js deleted file mode 100644 index 0a0881c..0000000 --- a/Logic/Overpass.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Overpass = void 0; -var OsmToGeoJson = require("osmtogeojson"); -var $ = require("jquery"); -/** - * Interfaces overpass to get all the latest data - */ -var Overpass = /** @class */ (function () { - function Overpass(filter) { - this._filter = filter; - } - Overpass.prototype.buildQuery = function (bbox) { - var filters = this._filter.asOverpass(); - var filter = ""; - for (var _i = 0, filters_1 = filters; _i < filters_1.length; _i++) { - var filterOr = filters_1[_i]; - filter += 'nwr' + filterOr + ';'; - } - var query = '[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;'; - console.log(query); - return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query); - }; - Overpass.prototype.queryGeoJson = function (bbox, continuation, onFail) { - var query = this.buildQuery(bbox); - if (Overpass.testUrl !== null) { - console.log("Using testing URL"); - query = Overpass.testUrl; - } - $.getJSON(query, function (json, status) { - if (status !== "success") { - console.log("Query failed"); - onFail(status); - } - if (json.elements === [] && json.remarks.indexOf("runtime error") > 0) { - console.log("Timeout or other runtime error"); - return; - } - // @ts-ignore - var geojson = OsmToGeoJson.default(json); - continuation(geojson); - }).fail(onFail); - }; - Overpass.testUrl = null; - return Overpass; -}()); -exports.Overpass = Overpass; diff --git a/Logic/Overpass.ts b/Logic/Overpass.ts index 28efa0b..f9eafaf 100644 --- a/Logic/Overpass.ts +++ b/Logic/Overpass.ts @@ -32,7 +32,7 @@ export class Overpass { queryGeoJson(bbox: string, continuation: ((any) => void), onFail: ((reason) => void)): void { let query = this.buildQuery(bbox); - + if(Overpass.testUrl !== null){ console.log("Using testing URL") query = Overpass.testUrl; @@ -44,7 +44,7 @@ export class Overpass { console.log("Query failed") onFail(status); } - + if(json.elements === [] && json.remarks.indexOf("runtime error") > 0){ console.log("Timeout or other runtime error"); return; diff --git a/Logic/StrayClickHandler.js b/Logic/StrayClickHandler.js deleted file mode 100644 index d93fb09..0000000 --- a/Logic/StrayClickHandler.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.StrayClickHandler = void 0; -var leaflet_1 = require("leaflet"); -/** - * The stray-click-hanlders adds a marker to the map if no feature was clicked. - * Shows the given uiToShow-element in the messagebox - */ -var StrayClickHandler = /** @class */ (function () { - function StrayClickHandler(basemap, selectElement, leftMessage, uiToShow) { - var _this = this; - this._basemap = basemap; - this._leftMessage = leftMessage; - this._uiToShow = uiToShow; - var self = this; - var map = basemap.map; - basemap.LastClickLocation.addCallback(function (lastClick) { - selectElement.setData(undefined); - if (self._lastMarker !== undefined) { - map.removeLayer(self._lastMarker); - } - self._lastMarker = leaflet_1.default.marker([lastClick.lat, lastClick.lon]); - var uiElement = uiToShow(); - var popup = leaflet_1.default.popup().setContent(uiElement.Render()); - uiElement.Activate(); - uiElement.Update(); - self._lastMarker.addTo(map); - self._lastMarker.bindPopup(popup).openPopup(); - self._lastMarker.on("click", function () { - leftMessage.setData(self._uiToShow); - }); - }); - selectElement.addCallback(function () { - if (self._lastMarker !== undefined) { - map.removeLayer(self._lastMarker); - _this._lastMarker = undefined; - } - }); - } - return StrayClickHandler; -}()); -exports.StrayClickHandler = StrayClickHandler; diff --git a/Logic/TagsFilter.js b/Logic/TagsFilter.js deleted file mode 100644 index 5c6aac1..0000000 --- a/Logic/TagsFilter.js +++ /dev/null @@ -1,240 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TagUtils = exports.Not = exports.And = exports.Or = exports.Tag = exports.Regex = exports.TagsFilter = void 0; -var TagsFilter = /** @class */ (function () { - function TagsFilter() { - } - TagsFilter.prototype.matchesProperties = function (properties) { - return this.matches(TagUtils.proprtiesToKV(properties)); - }; - return TagsFilter; -}()); -exports.TagsFilter = TagsFilter; -var Regex = /** @class */ (function (_super) { - __extends(Regex, _super); - function Regex(k, r) { - var _this = _super.call(this) || this; - _this._k = k; - _this._r = r; - return _this; - } - Regex.prototype.asOverpass = function () { - return ["['" + this._k + "'~'" + this._r + "']"]; - }; - Regex.prototype.matches = function (tags) { - var _a; - if (!(tags instanceof Array)) { - throw "You used 'matches' on something that is not a list. Did you mean to use 'matchesProperties'?"; - } - for (var _i = 0, tags_1 = tags; _i < tags_1.length; _i++) { - var tag = tags_1[_i]; - if (tag.k === this._k) { - if (tag.v === "") { - // This tag has been removed - return false; - } - if (this._r === "*") { - // Any is allowed - return true; - } - var matchCount = (_a = tag.v.match(this._r)) === null || _a === void 0 ? void 0 : _a.length; - return (matchCount !== null && matchCount !== void 0 ? matchCount : 0) > 0; - } - } - return false; - }; - Regex.prototype.substituteValues = function (tags) { - throw "Substituting values is not supported on regex tags"; - }; - return Regex; -}(TagsFilter)); -exports.Regex = Regex; -var Tag = /** @class */ (function (_super) { - __extends(Tag, _super); - function Tag(key, value) { - var _this = _super.call(this) || this; - _this.key = key; - _this.value = value; - return _this; - } - Tag.prototype.matches = function (tags) { - for (var _i = 0, tags_2 = tags; _i < tags_2.length; _i++) { - var tag = tags_2[_i]; - if (tag.k === this.key) { - if (tag.v === "") { - // This tag has been removed - return this.value === ""; - } - if (this.value === "*") { - // Any is allowed - return true; - } - return this.value === tag.v; - } - } - if (this.value === "") { - return true; - } - return false; - }; - Tag.prototype.asOverpass = function () { - if (this.value === "*") { - return ['["' + this.key + '"]']; - } - if (this.value === "") { - // NOT having this key - return ['[!"' + this.key + '"]']; - } - return ['["' + this.key + '"="' + this.value + '"]']; - }; - Tag.prototype.substituteValues = function (tags) { - return new Tag(this.key, TagUtils.ApplyTemplate(this.value, tags)); - }; - return Tag; -}(TagsFilter)); -exports.Tag = Tag; -var Or = /** @class */ (function (_super) { - __extends(Or, _super); - function Or(or) { - var _this = _super.call(this) || this; - _this.or = or; - return _this; - } - Or.prototype.matches = function (tags) { - for (var _i = 0, _a = this.or; _i < _a.length; _i++) { - var tagsFilter = _a[_i]; - if (tagsFilter.matches(tags)) { - return true; - } - } - return false; - }; - Or.prototype.asOverpass = function () { - var choices = []; - for (var _i = 0, _a = this.or; _i < _a.length; _i++) { - var tagsFilter = _a[_i]; - var subChoices = tagsFilter.asOverpass(); - for (var _b = 0, subChoices_1 = subChoices; _b < subChoices_1.length; _b++) { - var subChoice = subChoices_1[_b]; - choices.push(subChoice); - } - } - return choices; - }; - Or.prototype.substituteValues = function (tags) { - var newChoices = []; - for (var _i = 0, _a = this.or; _i < _a.length; _i++) { - var c = _a[_i]; - newChoices.push(c.substituteValues(tags)); - } - return new Or(newChoices); - }; - return Or; -}(TagsFilter)); -exports.Or = Or; -var And = /** @class */ (function (_super) { - __extends(And, _super); - function And(and) { - var _this = _super.call(this) || this; - _this.and = and; - return _this; - } - And.prototype.matches = function (tags) { - for (var _i = 0, _a = this.and; _i < _a.length; _i++) { - var tagsFilter = _a[_i]; - if (!tagsFilter.matches(tags)) { - return false; - } - } - return true; - }; - And.prototype.combine = function (filter, choices) { - var values = []; - for (var _i = 0, choices_1 = choices; _i < choices_1.length; _i++) { - var or = choices_1[_i]; - values.push(filter + or); - } - return values; - }; - And.prototype.asOverpass = function () { - var allChoices = null; - for (var _i = 0, _a = this.and; _i < _a.length; _i++) { - var andElement = _a[_i]; - var andElementFilter = andElement.asOverpass(); - if (allChoices === null) { - allChoices = andElementFilter; - continue; - } - var newChoices = []; - for (var _b = 0, allChoices_1 = allChoices; _b < allChoices_1.length; _b++) { - var choice = allChoices_1[_b]; - newChoices.push(this.combine(choice, andElementFilter)); - } - allChoices = newChoices; - } - return allChoices; - }; - And.prototype.substituteValues = function (tags) { - var newChoices = []; - for (var _i = 0, _a = this.and; _i < _a.length; _i++) { - var c = _a[_i]; - newChoices.push(c.substituteValues(tags)); - } - return new And(newChoices); - }; - return And; -}(TagsFilter)); -exports.And = And; -var Not = /** @class */ (function (_super) { - __extends(Not, _super); - function Not(not) { - var _this = _super.call(this) || this; - _this.not = not; - return _this; - } - Not.prototype.asOverpass = function () { - throw "Not supported yet"; - }; - Not.prototype.matches = function (tags) { - return !this.not.matches(tags); - }; - Not.prototype.substituteValues = function (tags) { - return new Not(this.not.substituteValues(tags)); - }; - return Not; -}(TagsFilter)); -exports.Not = Not; -var TagUtils = /** @class */ (function () { - function TagUtils() { - } - TagUtils.proprtiesToKV = function (properties) { - var result = []; - for (var k in properties) { - result.push({ k: k, v: properties[k] }); - } - return result; - }; - TagUtils.ApplyTemplate = function (template, tags) { - for (var k in tags) { - while (template.indexOf("{" + k + "}") >= 0) { - template = template.replace("{" + k + "}", tags[k]); - } - } - return template; - }; - return TagUtils; -}()); -exports.TagUtils = TagUtils; diff --git a/Logic/Wikimedia.js b/Logic/Wikimedia.js deleted file mode 100644 index b9f880f..0000000 --- a/Logic/Wikimedia.js +++ /dev/null @@ -1,135 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.LicenseInfo = exports.ImagesInCategory = exports.Wikidata = exports.Wikimedia = void 0; -var $ = require("jquery"); -/** - * This module provides endpoints for wikipedia/wikimedia and others - */ -var Wikimedia = /** @class */ (function () { - function Wikimedia() { - } - Wikimedia.ImageNameToUrl = function (filename, width, height) { - if (width === void 0) { width = 500; } - if (height === void 0) { height = 200; } - filename = encodeURIComponent(filename); - return "https://commons.wikimedia.org/wiki/Special:FilePath/" + filename + "?width=" + width + "&height=" + height; - }; - Wikimedia.LicenseData = function (filename, handle) { - if (filename in this.knownLicenses) { - return this.knownLicenses[filename]; - } - if (filename === "") { - return; - } - var url = "https://en.wikipedia.org/w/" + - "api.php?action=query&prop=imageinfo&iiprop=extmetadata&" + - "titles=" + filename + - "&format=json&origin=*"; - $.getJSON(url, function (data, status) { - var _a, _b, _c, _d, _e, _f, _g, _h; - var licenseInfo = new LicenseInfo(); - var license = data.query.pages[-1].imageinfo[0].extmetadata; - licenseInfo.artist = (_a = license.Artist) === null || _a === void 0 ? void 0 : _a.value; - licenseInfo.license = (_b = license.License) === null || _b === void 0 ? void 0 : _b.value; - licenseInfo.copyrighted = (_c = license.Copyrighted) === null || _c === void 0 ? void 0 : _c.value; - licenseInfo.attributionRequired = (_d = license.AttributionRequired) === null || _d === void 0 ? void 0 : _d.value; - licenseInfo.usageTerms = (_e = license.UsageTerms) === null || _e === void 0 ? void 0 : _e.value; - licenseInfo.licenseShortName = (_f = license.LicenseShortName) === null || _f === void 0 ? void 0 : _f.value; - licenseInfo.credit = (_g = license.Credit) === null || _g === void 0 ? void 0 : _g.value; - licenseInfo.description = (_h = license.ImageDescription) === null || _h === void 0 ? void 0 : _h.value; - Wikimedia.knownLicenses[filename] = licenseInfo; - handle(licenseInfo); - }); - }; - Wikimedia.GetCategoryFiles = function (categoryName, handleCategory, alreadyLoaded, continueParameter) { - var _this = this; - if (alreadyLoaded === void 0) { alreadyLoaded = 0; } - if (continueParameter === void 0) { continueParameter = undefined; } - if (categoryName === undefined || categoryName === null || categoryName === "") { - return; - } - // @ts-ignore - if (!categoryName.startsWith("Category:")) { - categoryName = "Category:" + categoryName; - } - var url = "https://commons.wikimedia.org/w/api.php?" + - "action=query&list=categorymembers&format=json&" + - "&origin=*" + - "&cmtitle=" + encodeURIComponent(categoryName); - if (continueParameter !== undefined) { - url = url + "&" + continueParameter.k + "=" + continueParameter.param; - } - $.getJSON(url, function (response) { - var _a; - var imageOverview = new ImagesInCategory(); - var members = (_a = response.query) === null || _a === void 0 ? void 0 : _a.categorymembers; - if (members === undefined) { - members = []; - } - for (var _i = 0, members_1 = members; _i < members_1.length; _i++) { - var member = members_1[_i]; - imageOverview.images.push(member.title); - } - if (response.continue === undefined || alreadyLoaded > 30) { - handleCategory(imageOverview); - } - else { - console.log("Recursive load for ", categoryName); - _this.GetCategoryFiles(categoryName, function (recursiveImages) { - for (var _i = 0, _a = imageOverview.images; _i < _a.length; _i++) { - var image = _a[_i]; - recursiveImages.images.push(image); - } - handleCategory(recursiveImages); - }, alreadyLoaded + 10, { k: "cmcontinue", param: response.continue.cmcontinue }); - } - }); - }; - Wikimedia.GetWikiData = function (id, handleWikidata) { - var url = "https://www.wikidata.org/wiki/Special:EntityData/Q" + id + ".json"; - $.getJSON(url, function (response) { - var _a, _b, _c, _d; - var entity = response.entities["Q" + id]; - var commons = entity.sitelinks.commonswiki; - var wd = new Wikidata(); - wd.commonsWiki = commons === null || commons === void 0 ? void 0 : commons.title; - // P18 is the claim 'depicted in this image' - var image = (_d = (_c = (_b = (_a = entity.claims.P18) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.mainsnak) === null || _c === void 0 ? void 0 : _c.datavalue) === null || _d === void 0 ? void 0 : _d.value; - if (image) { - wd.image = "File:" + image; - } - handleWikidata(wd); - }); - }; - Wikimedia.knownLicenses = {}; - return Wikimedia; -}()); -exports.Wikimedia = Wikimedia; -var Wikidata = /** @class */ (function () { - function Wikidata() { - } - return Wikidata; -}()); -exports.Wikidata = Wikidata; -var ImagesInCategory = /** @class */ (function () { - function ImagesInCategory() { - // Filenames of relevant images - this.images = []; - } - return ImagesInCategory; -}()); -exports.ImagesInCategory = ImagesInCategory; -var LicenseInfo = /** @class */ (function () { - function LicenseInfo() { - this.artist = ""; - this.license = ""; - this.licenseShortName = ""; - this.usageTerms = ""; - this.attributionRequired = false; - this.copyrighted = false; - this.credit = ""; - this.description = ""; - } - return LicenseInfo; -}()); -exports.LicenseInfo = LicenseInfo; From 8026e99824c0d04f37e59407ecbac2ecf50e5bcb Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 21:03:55 +0200 Subject: [PATCH 18/29] End of huge refactoring: cleaner input elements --- Customizations/Layers/Bookcases.ts | 5 ++++- Customizations/Questions/NameInline.ts | 3 ++- Customizations/TagRendering.ts | 15 +++++++-------- UI/Input/FixedInputElement.ts | 10 ++++++++-- UI/Input/InputElement.ts | 2 ++ UI/Input/InputElementWrapper.ts | 4 ++++ UI/Input/RadioButton.ts | 22 +++++++++++++++------- UI/Input/TextField.ts | 17 +++++++++++++++-- UI/UIEventSource.ts | 2 +- test.ts | 1 + 10 files changed, 59 insertions(+), 22 deletions(-) diff --git a/Customizations/Layers/Bookcases.ts b/Customizations/Layers/Bookcases.ts index 16f78d9..7a2311c 100644 --- a/Customizations/Layers/Bookcases.ts +++ b/Customizations/Layers/Bookcases.ts @@ -119,7 +119,10 @@ export class Bookcases extends LayerDefinition { key: "ref", template: "Het referentienummer is $$$", renderTemplate: "Gekend als {brand} <b>{ref}</b>" - } + }, + mappings: [ + {k: new And([new Tag("brand",""), new Tag("nobrand","yes"), new Tag("ref", "")]), txt: "Maakt geen deel uit van een groter netwerk"} + ] }).OnlyShowIf(new Tag("brand","*")), new TagRenderingOptions({ diff --git a/Customizations/Questions/NameInline.ts b/Customizations/Questions/NameInline.ts index 83458a5..5c9d4e6 100644 --- a/Customizations/Questions/NameInline.ts +++ b/Customizations/Questions/NameInline.ts @@ -1,5 +1,6 @@ import {TagRenderingOptions} from "../TagRendering"; import {And, Tag} from "../../Logic/TagsFilter"; +import {UIElement} from "../../UI/UIElement"; export class NameInline extends TagRenderingOptions{ @@ -8,7 +9,7 @@ export class NameInline extends TagRenderingOptions{ return string.charAt(0).toUpperCase() + string.slice(1); } - constructor(category: string) { + constructor(category: string ) { super({ question: "", diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 2be71e0..231c820 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -262,8 +262,8 @@ class TagRendering extends UIElement implements TagDependantUIElement { if (this._question !== undefined) { this._editButton = new FixedUiElement("<img class='editbutton' src='./assets/pencil.svg' alt='edit'>") .onClick(() => { - self._questionElement.GetValue().setData(self.CurrentValue()); self._editMode.setData(true); + self._questionElement.ShowValue(self.CurrentValue()); }); } @@ -297,7 +297,6 @@ class TagRendering extends UIElement implements TagDependantUIElement { const previousTexts= []; for (const mapping of options.mappings) { - console.log(mapping); if(mapping.k === null){ continue; } @@ -306,7 +305,6 @@ class TagRendering extends UIElement implements TagDependantUIElement { } previousTexts.push(mapping.txt); - console.log("PUshed") elements.push(this.InputElementForMapping(mapping)); } } @@ -349,8 +347,8 @@ class TagRendering extends UIElement implements TagDependantUIElement { return tag; } return new And([ - freeform.extraTags, - tag + tag, + freeform.extraTags ] ); }; @@ -362,7 +360,6 @@ class TagRendering extends UIElement implements TagDependantUIElement { } else if (tag instanceof Tag) { return tag.value } - console.log("Could not decode tag to string", tag) return undefined; } @@ -392,10 +389,11 @@ class TagRendering extends UIElement implements TagDependantUIElement { } private CurrentValue(): TagsFilter { + console.log("Creating a current value...") const tags = TagUtils.proprtiesToKV(this._source.data); for (const oneOnOneElement of this._mapping.concat(this._renderMapping)) { - if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tags)) { + if (oneOnOneElement.k !== null && oneOnOneElement.k.matches(tags)) { return oneOnOneElement.k; } } @@ -403,6 +401,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { return undefined; } + console.log("Got a freeform tag:", new Tag(this._freeform.key, this._source.data[this._freeform.key])) return new Tag(this._freeform.key, this._source.data[this._freeform.key]); } @@ -469,7 +468,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { return "<div class='question'>" + "<span class='question-text'>" + this._question + "</span>" + (this._question !== "" ? "<br/>" : "") + - this._questionElement.Render() + + "<div>"+ this._questionElement.Render() +"</div>"+ this._skipButton.Render() + this._saveButton.Render() + "</div>" diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index d3ea566..0cada25 100644 --- a/UI/Input/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -17,13 +17,19 @@ export class FixedInputElement<T> extends InputElement<T> { GetValue(): UIEventSource<T> { return this.value; } + + ShowValue(t: T): boolean { + return false; + } - protected InnerRender(): string { + InnerRender(): string { return this.rendering.Render(); } IsValid(t: T): boolean { - return t === this.value.data; + return t == this.value.data; } + + } \ No newline at end of file diff --git a/UI/Input/InputElement.ts b/UI/Input/InputElement.ts index 62e0969..20d3c45 100644 --- a/UI/Input/InputElement.ts +++ b/UI/Input/InputElement.ts @@ -7,5 +7,7 @@ export abstract class InputElement<T> extends UIElement{ abstract GetValue() : UIEventSource<T>; abstract IsValid(t: T) : boolean; + + abstract ShowValue(t: T) : boolean; } \ No newline at end of file diff --git a/UI/Input/InputElementWrapper.ts b/UI/Input/InputElementWrapper.ts index 979a712..905a948 100644 --- a/UI/Input/InputElementWrapper.ts +++ b/UI/Input/InputElementWrapper.ts @@ -25,6 +25,10 @@ export class InputElementWrapper<T> extends InputElement<T>{ GetValue(): UIEventSource<T> { return this.input.GetValue(); } + + ShowValue(t: T) { + return this.input.ShowValue(t); + } InnerRender(): string { return this.pre.Render() + this.input.Render() + this.post.Render(); diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index 647fda1..3cec9c6 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -31,7 +31,7 @@ export class RadioButton<T> extends InputElement<T> { ; this.value.addCallback((t) => { - self.SetCorrectValue(t); + self.ShowValue(t); }) @@ -79,11 +79,13 @@ export class RadioButton<T> extends InputElement<T> { return "<form id='" + this.id + "-form'>" + body + "</form>"; } - private SetCorrectValue(t: T) { + public ShowValue(t: T): boolean { if (t === undefined) { - return; + return false; + } + if (!this.IsValid(t)) { + return false; } - console.log("Trying to find an option for", t) // We check that what is selected matches the previous rendering for (let i = 0; i < this._elements.length; i++) { const e = this._elements[i]; @@ -119,9 +121,15 @@ export class RadioButton<T> extends InputElement<T> { checkButtons(); } ); - - if (this._selectFirstAsDefault) { - this.SetCorrectValue(this.value.data); + if (this._selectedElementIndex.data !== null) { + const el = document.getElementById(this.IdFor(this._selectedElementIndex.data)); + if (el) { + // @ts-ignore + el.checked = true; + checkButtons(); + } + } else if (this._selectFirstAsDefault) { + this.ShowValue(this.value.data); if (this._selectedElementIndex.data === null || this._selectedElementIndex.data === undefined) { const el = document.getElementById(this.IdFor(0)); if (el) { diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 090dc45..bc1d857 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -60,7 +60,14 @@ export class TextField<T> extends InputElement<T> { return this.mappedValue; } - InnerRender(): string { + ShowValue(t: T): boolean { + if (!this.IsValid(t)) { + return false; + } + this.mappedValue.setData(t); + } + + InnerRender(): string { return "<form onSubmit='return false' class='form-text-field'>" + "<input type='text' placeholder='" + this._placeholder.InnerRender() + "' id='text-" + this.id + "'>" + "</form>"; @@ -68,7 +75,7 @@ export class TextField<T> extends InputElement<T> { InnerUpdate(htmlElement: HTMLElement) { const field = document.getElementById('text-' + this.id); - if(field === null){ + if (field === null) { return; } const self = this; @@ -84,10 +91,16 @@ export class TextField<T> extends InputElement<T> { } }); + if (this.IsValid(this.mappedValue.data)) { + // @ts-ignore + field.value = this._toString(this.mappedValue.data); + } + } IsValid(t: T): boolean { + console.log("TXT IS valid?",t,this._toString(t)) if(t === undefined || t === null){ return false; } diff --git a/UI/UIEventSource.ts b/UI/UIEventSource.ts index 1e4aeb5..54902a0 100644 --- a/UI/UIEventSource.ts +++ b/UI/UIEventSource.ts @@ -1,6 +1,6 @@ export class UIEventSource<T>{ - public data : T; + public data: T; private _callbacks = []; constructor(data: T) { diff --git a/test.ts b/test.ts index d71a3ff..18b43e1 100644 --- a/test.ts +++ b/test.ts @@ -22,4 +22,5 @@ const buttons = new RadioButton<number>( ], false ).AttachTo("maindiv"); + buttons.GetValue().addCallback(console.log); \ No newline at end of file From 00d95c4be1498266438bd6631bd78a6ed98cf4af Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 21:39:07 +0200 Subject: [PATCH 19/29] Finishing refactoring --- Customizations/TagRendering.ts | 2 +- UI/ImageUploadFlow.ts | 4 +- UI/Input/DropDown.ts | 94 ++++++++++++++++++++++------------ UI/Input/InputElement.ts | 2 - UI/Input/RadioButton.ts | 3 +- UI/Input/TextField.ts | 13 +++-- UI/UIElement.ts | 3 +- test.ts | 22 ++++---- 8 files changed, 86 insertions(+), 57 deletions(-) diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 231c820..5f45a26 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -263,7 +263,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { this._editButton = new FixedUiElement("<img class='editbutton' src='./assets/pencil.svg' alt='edit'>") .onClick(() => { self._editMode.setData(true); - self._questionElement.ShowValue(self.CurrentValue()); + self._questionElement.GetValue().setData(self.CurrentValue()); }); } diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index e332bc2..5a55f53 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -41,7 +41,7 @@ export class ImageUploadFlow extends UIElement { preferedLicense ); this._licensePicker = licensePicker; - this._selectedLicence = licensePicker.selectedElement; + this._selectedLicence = licensePicker.GetValue(); const licenseExplanations = { @@ -62,7 +62,7 @@ export class ImageUploadFlow extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { if (!this._userdetails.data.loggedIn) { return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden</div>"; diff --git a/UI/Input/DropDown.ts b/UI/Input/DropDown.ts index 78e2896..fb15f02 100644 --- a/UI/Input/DropDown.ts +++ b/UI/Input/DropDown.ts @@ -1,35 +1,61 @@ import {UIEventSource} from "../UIEventSource"; import {UIElement} from "../UIElement"; +import {InputElement} from "./InputElement"; +import instantiate = WebAssembly.instantiate; +import {FixedUiElement} from "../Base/FixedUiElement"; -export class DropDown extends UIElement { +export class DropDown<T> extends InputElement<T> { - selectedElement: UIEventSource<string> - private _label: string; - private _values: { value: string; shown: string }[]; + private readonly _label: string; + private readonly _values: { value: T; shown: UIElement }[]; - constructor(label: string, values: { value: string, shown: string }[], - selectedElement: UIEventSource<string> = undefined) { + private readonly _value; + + constructor(label: string, + values: { value: T, shown: string | UIElement }[], + value: UIEventSource<T> = undefined) { super(undefined); + this._value = value ?? new UIEventSource<T>(undefined); this._label = label; - this._values = values; - this.selectedElement = selectedElement ?? new UIEventSource<string>(values[0].value); - if(selectedElement.data === undefined){ - this.selectedElement.setData(values[0].value) + this._values = values.map(v => { + return { + value: v.value, + shown: v.shown instanceof UIElement ? v.shown : new FixedUiElement(v.shown) + } + } + ); + this.ListenTo(this._value) + + } + + GetValue(): UIEventSource<T> { + return this._value; + } + + ShowValue(t: T): boolean { + if (!this.IsValid(t)) { + return false; } - const self = this; - this.selectedElement.addCallback(() => { - self.InnerUpdate(); - }); + this._value.setData(t); + } + + IsValid(t: T): boolean { + for (const value of this._values) { + if (value.value === t) { + return true; + } + } + return false } - protected InnerRender(): string { + InnerRender(): string { let options = ""; - for (const value of this._values) { - options += "<option value='" + value.value + "'>" + value.shown + "</option>" - } + for (let i = 0; i < this._values.length; i++) { + options += "<option value='" + i + "'>" + this._values[i].shown.InnerRender() + "</option>" + } return "<form>" + "<label for='dropdown-" + this.id + "'>" + this._label + "</label>" + "<select name='dropdown-" + this.id + "' id='dropdown-" + this.id + "'>" + @@ -38,24 +64,26 @@ export class DropDown extends UIElement { "</form>"; } - InnerUpdate() { + protected InnerUpdate(element) { + + var e = document.getElementById("dropdown-" + this.id); const self = this; - const e = document.getElementById("dropdown-" + this.id); - if(e === null){ - return; - } - // @ts-ignore - if (this.selectedElement.data !== e.value) { + e.onchange = (() => { // @ts-ignore - e.value = this.selectedElement.data; + var index = parseInt(e.selectedIndex); + self._value.setData(self._values[index].value); + + }); + + var t = this._value.data; + for (let i = 0; i < this._values.length ; i++) { + const value = this._values[i]; + if (value.value == t) { + // @ts-ignore + e.selectedIndex = i; + } } - e.onchange = function () { - // @ts-ignore - const selectedValue = e.options[e.selectedIndex].value; - console.log("Putting data", selectedValue) - self.selectedElement.setData(selectedValue); - } - + } } \ No newline at end of file diff --git a/UI/Input/InputElement.ts b/UI/Input/InputElement.ts index 20d3c45..8278314 100644 --- a/UI/Input/InputElement.ts +++ b/UI/Input/InputElement.ts @@ -8,6 +8,4 @@ export abstract class InputElement<T> extends UIElement{ abstract IsValid(t: T) : boolean; - abstract ShowValue(t: T) : boolean; - } \ No newline at end of file diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index 3cec9c6..8d8b5cc 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -27,8 +27,7 @@ export class RadioButton<T> extends InputElement<T> { return elements[selectedIndex].GetValue() } } - ), elements.map(e => e.GetValue())) - ; + ), elements.map(e => e.GetValue())); this.value.addCallback((t) => { self.ShowValue(t); diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index bc1d857..0d7eee2 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -23,9 +23,11 @@ export class TextField<T> extends InputElement<T> { value?: UIEventSource<T> }) { super(undefined); + const self = this; this.value = new UIEventSource<string>(""); - this.mappedValue = options?.value ?? new UIEventSource<T>(undefined); + this.mappedValue = options?.value ?? new UIEventSource<T>(undefined); + this.mappedValue.addCallback(() => self.InnerUpdate()); // @ts-ignore this._fromString = options.fromString ?? ((str) => (str)) @@ -42,7 +44,6 @@ export class TextField<T> extends InputElement<T> { this._toString = options.toString ?? ((t) => ("" + t)); - const self = this; this.mappedValue.addCallback((t) => { if (t === undefined || t === null) { return; @@ -73,7 +74,7 @@ export class TextField<T> extends InputElement<T> { "</form>"; } - InnerUpdate(htmlElement: HTMLElement) { + InnerUpdate() { const field = document.getElementById('text-' + this.id); if (field === null) { return; @@ -92,8 +93,12 @@ export class TextField<T> extends InputElement<T> { }); if (this.IsValid(this.mappedValue.data)) { + const expected = this._toString(this.mappedValue.data); // @ts-ignore - field.value = this._toString(this.mappedValue.data); + if (field.value !== expected) { + // @ts-ignore + field.value = expected; + } } diff --git a/UI/UIElement.ts b/UI/UIElement.ts index 700cc5c..a0b9d49 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -83,7 +83,8 @@ export abstract class UIElement { } // Called after the HTML has been replaced. Can be used for css tricks - InnerUpdate(htmlElement : HTMLElement){} + protected InnerUpdate(htmlElement: HTMLElement) { + } Render(): string { return "<span class='uielement' id='" + this.id + "'>" + this.InnerRender() + "</span>" diff --git a/test.ts b/test.ts index 18b43e1..c031b57 100644 --- a/test.ts +++ b/test.ts @@ -10,17 +10,15 @@ import {VariableUiElement} from "./UI/Base/VariableUIElement"; import {TextField} from "./UI/Input/TextField"; import {FixedInputElement} from "./UI/Input/FixedInputElement"; import {RadioButton} from "./UI/Input/RadioButton"; +import {DropDown} from "./UI/Input/DropDown"; +import {FixedUiElement} from "./UI/Base/FixedUiElement"; - -const buttons = new RadioButton<number>( - [new FixedInputElement("Five", 5), - new FixedInputElement("Ten", 10), - new TextField<number>({ - fromString: (str) => parseInt(str), - toString: (i) => ("" + i), - }) - ], false +const dropdown = new RadioButton<string>( + [new FixedInputElement("5","5"), + new TextField({ + toString: ((str) => str), + fromString: ((str) => str), + })] ).AttachTo("maindiv"); - - -buttons.GetValue().addCallback(console.log); \ No newline at end of file +const value = dropdown.GetValue(); +value.setData("asldkjvmqlksjdf") \ No newline at end of file From 9d077890bde7a016559afd4a2619034de5e6dd01 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Mon, 20 Jul 2020 21:39:26 +0200 Subject: [PATCH 20/29] Add clean script --- clean.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 clean.sh diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..4fac85d --- /dev/null +++ b/clean.sh @@ -0,0 +1,12 @@ +#! /bin/bash +rm *.js +rm Logic/*.js +rm Logic/*.js +rm Logic/*/*.js +rm Logic/*/*/*.js +rm UI/*.js +rm UI/*/*.js +rm UI/*/*/*.js +rm Customizations/*.js +rm Customizations/*/*.js +rm Customizations/*/*/*.js From 2c92f5bbe2c71ca555e3dcf62118bc150d5f2a03 Mon Sep 17 00:00:00 2001 From: Pieter Fiers <pieter@pfiers.net> Date: Mon, 20 Jul 2020 23:43:42 +0200 Subject: [PATCH 21/29] Add translations + bike Shops --- Customizations/Layers/BikeParkings.ts | 9 +- Customizations/Layers/BikeShops.ts | 50 ++++++++ Customizations/Layers/BikeStations.ts | 7 +- Customizations/Layouts/Cyclofix.ts | 3 +- .../Questions/bike/ParkingOperator.ts | 27 ++++ Customizations/Questions/bike/ParkingType.ts | 42 +++---- .../Questions/bike/PumpManometer.ts | 10 +- Customizations/Questions/bike/PumpManual.ts | 8 +- .../Questions/bike/PumpOperational.ts | 8 +- Customizations/Questions/bike/PumpValves.ts | 17 +-- Customizations/Questions/bike/ShopPump.ts | 19 +++ Customizations/Questions/bike/ShopRental.ts | 19 +++ Customizations/Questions/bike/ShopRepair.ts | 19 +++ Customizations/Questions/bike/ShopRetail.ts | 19 +++ Customizations/Questions/bike/StationBrand.ts | 2 + Customizations/Questions/bike/StationChain.ts | 8 +- .../Questions/bike/StationOperator.ts | 20 +-- .../Questions/bike/StationPumpTools.ts | 10 +- Customizations/Questions/bike/StationStand.ts | 4 +- UI/CenterMessageBox.ts | 7 +- UI/ImageUploadFlow.ts | 16 +-- UI/i18n/Locale.ts | 7 +- UI/i18n/Translation.ts | 6 +- UI/i18n/Translations.ts | 119 +++++++++++++++++- index.css | 3 + index.ts | 14 ++- 26 files changed, 390 insertions(+), 83 deletions(-) create mode 100644 Customizations/Layers/BikeShops.ts create mode 100644 Customizations/Questions/bike/ParkingOperator.ts create mode 100644 Customizations/Questions/bike/ShopPump.ts create mode 100644 Customizations/Questions/bike/ShopRental.ts create mode 100644 Customizations/Questions/bike/ShopRepair.ts create mode 100644 Customizations/Questions/bike/ShopRetail.ts diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index ccd737e..141ba29 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -5,12 +5,15 @@ import * as L from "leaflet"; import FixedText from "../Questions/FixedText"; import ParkingType from "../Questions/bike/ParkingType"; import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; +import BikeStationOperator from "../Questions/bike/StationOperator"; +import Translations from "../../UI/i18n/Translations"; +import ParkingOperator from "../Questions/bike/ParkingOperator"; export default class BikeParkings extends LayerDefinition { constructor() { super(); - this.name = "bike_parking"; + this.name = Translations.t.cylofix.parking.name.txt; this.icon = "./assets/bike/parking.svg"; this.overpassFilter = new Tag("amenity", "bicycle_parking"); this.newElementTags = [ @@ -20,10 +23,10 @@ export default class BikeParkings extends LayerDefinition { this.minzoom = 13; this.style = this.generateStyleFunction(); - this.title = new FixedText("Fietsparking"); + this.title = new FixedText(Translations.t.cylofix.parking.title.txt) this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), - new OperatorTag(), + //new ParkingOperator(), new ParkingType() ]; diff --git a/Customizations/Layers/BikeShops.ts b/Customizations/Layers/BikeShops.ts new file mode 100644 index 0000000..d829eeb --- /dev/null +++ b/Customizations/Layers/BikeShops.ts @@ -0,0 +1,50 @@ +import { LayerDefinition } from "../LayerDefinition"; +import Translations from "../../UI/i18n/Translations"; +import { Tag } from "../../Logic/TagsFilter"; +import FixedText from "../Questions/FixedText"; +import { ImageCarouselWithUploadConstructor } from "../../UI/Image/ImageCarouselWithUpload"; +import * as L from "leaflet"; +import ShopRetail from "../Questions/bike/ShopRetail"; +import ShopPump from "../Questions/bike/ShopPump"; +import ShopRental from "../Questions/bike/ShopRental"; +import ShopRepair from "../Questions/bike/ShopRepair"; + + +export default class BikeShops extends LayerDefinition { + constructor() { + super(); + this.name = Translations.t.cylofix.shop.name.txt; + this.icon = "./assets/bike/shop.svg"; + this.overpassFilter = new Tag("shop", "bicycle"); + this.newElementTags = [ + new Tag("shop", "bicycle"), + ]; + this.maxAllowedOverlapPercentage = 10; + + this.minzoom = 13; + this.style = this.generateStyleFunction(); + this.title = new FixedText(Translations.t.cylofix.shop.title.txt) + this.elementsToShow = [ + new ImageCarouselWithUploadConstructor(), + //new ParkingOperator(), + new ShopRetail(), + new ShopRental(), + new ShopRepair(), + new ShopPump(), + ]; + + } + + private generateStyleFunction() { + const self = this; + return function (properties: any) { + return { + color: "#00bb00", + icon: L.icon({ + iconUrl: self.icon, + iconSize: [50, 50] + }) + }; + }; + } +} diff --git a/Customizations/Layers/BikeStations.ts b/Customizations/Layers/BikeStations.ts index ca25fac..65b43f0 100644 --- a/Customizations/Layers/BikeStations.ts +++ b/Customizations/Layers/BikeStations.ts @@ -12,6 +12,7 @@ import PumpManometer from "../Questions/bike/PumpManometer"; import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; import PumpOperational from "../Questions/bike/PumpOperational"; import PumpValves from "../Questions/bike/PumpValves"; +import Translations from "../../UI/i18n/Translations"; export default class BikeStations extends LayerDefinition { @@ -22,7 +23,7 @@ export default class BikeStations extends LayerDefinition { constructor() { super(); - this.name = "bike station or pump"; + this.name = Translations.t.cylofix.station.name.txt; this.icon = "./assets/wrench.svg"; this.overpassFilter = new And([ @@ -37,7 +38,7 @@ export default class BikeStations extends LayerDefinition { this.minzoom = 13; this.style = this.generateStyleFunction(); - this.title = new FixedText("Bike station"); + this.title = new FixedText(Translations.t.cylofix.station.title.txt) this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), @@ -51,7 +52,7 @@ export default class BikeStations extends LayerDefinition { new PumpValves().OnlyShowIf(this.pump), new PumpOperational().OnlyShowIf(this.pump), - new BikeStationOperator(), + // new BikeStationOperator(), // new BikeStationBrand() DISABLED ]; } diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 328acda..9c9148b 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -1,6 +1,7 @@ import {Layout} from "../Layout"; import BikeParkings from "../Layers/BikeParkings"; import BikeServices from "../Layers/BikeStations"; +import BikeShops from "../Layers/BikeShops"; import {GhostBike} from "../Layers/GhostBike"; import Translations from "../../UI/i18n/Translations"; @@ -10,7 +11,7 @@ export default class Cyclofix extends Layout { super( "pomp", Translations.t.cylofix.title, - [new GhostBike(), new BikeServices(), new BikeParkings()], + [/*new GhostBike(),*/ new BikeServices(), new BikeParkings(), new BikeShops], 16, 50.8465573, 4.3516970, diff --git a/Customizations/Questions/bike/ParkingOperator.ts b/Customizations/Questions/bike/ParkingOperator.ts new file mode 100644 index 0000000..f554444 --- /dev/null +++ b/Customizations/Questions/bike/ParkingOperator.ts @@ -0,0 +1,27 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag, And} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ParkingOperator extends TagRenderingOptions { + constructor() { + const to = Translations.t.cylofix.parking.operator + super({ + priority: 15, + question: to.question.Render(), + freeform: { + key: "operator", + template: to.template.txt, + renderTemplate: to.render.txt, + placeholder: Translations.t.cylofix.freeFormPlaceholder.txt + }, + mappings: [ + {k: new Tag("operator", "KU Leuven"), txt: "KU Leuven"}, + {k: new Tag("operator", "Stad Halle"), txt: "Stad Halle"}, + {k: new Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis"}, + {k: new Tag("operator", "Jette"), txt: "Jette"}, + {k: new And([new Tag("operator", ""), new Tag("operator:type", "private")]), txt: to.private.Render()} + ] + }); + } +} diff --git a/Customizations/Questions/bike/ParkingType.ts b/Customizations/Questions/bike/ParkingType.ts index 4b5074f..f621b8d 100644 --- a/Customizations/Questions/bike/ParkingType.ts +++ b/Customizations/Questions/bike/ParkingType.ts @@ -1,37 +1,37 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class ParkingType extends TagRenderingOptions { - private static images = { - stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", - wall_loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", - handlebar_holder: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", - shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", - "two-tier": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" - } - - private static toImgTxt(url: string) { - return `<img src=${url}>` - } - constructor() { + const images = { + stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", + loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", + handlebar: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", + shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", + rack: "https://wiki.openstreetmap.org/w/images/thumb/4/41/Triton_Bike_Rack.png/100px-Triton_Bike_Rack.png", + double: "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" + } + const toImg = (url) => `<img src=${url}>` + const to = Translations.t.cylofix.parking.type super({ priority: 5, - question: "Van welk type is deze fietsenparking?", + question: to.question.Render(), freeform: { key: "bicycle_parking", extraTags: new Tag("fixme", "Freeform bicycle_parking= tag used: possibly a wrong value"), - template: "Iets anders: $$$", - renderTemplate: "Dit is een fietsenparking van het type: {bicycle_parking}", - placeholder: "Specifieer" + template: to.template.txt, + renderTemplate: to.render.txt, + placeholder: Translations.t.cylofix.freeFormPlaceholder.txt, }, mappings: [ - {k: new Tag("bicycle_parking", "stands"), txt: ParkingType.toImgTxt(ParkingType.images.stands)}, - {k: new Tag("bicycle_parking", "wall_loops"), txt: ParkingType.toImgTxt(ParkingType.images.wall_loops)}, - {k: new Tag("bicycle_parking", "handlebar_holder"), txt: ParkingType.toImgTxt(ParkingType.images.handlebar_holder)}, - {k: new Tag("bicycle_parking", "shed"), txt: ParkingType.toImgTxt(ParkingType.images.shed)}, - {k: new Tag("bicycle_parking", "two-tier"), txt: ParkingType.toImgTxt(ParkingType.images["two-tier"])} + {k: new Tag("bicycle_parking", "stands"), txt: `${to.stands.Render()}, bijvoorbeeld: ${toImg(images.stands)}`}, + {k: new Tag("bicycle_parking", "wall_loops"), txt: `${to.loops.Render()}, bijvoorbeeld: ${toImg(images.loops)}`}, + {k: new Tag("bicycle_parking", "handlebar_holder"), txt: `${to.handlebar.Render()}, bijvoorbeeld: ${toImg(images.handlebar)}`}, + {k: new Tag("bicycle_parking", "shed"), txt: `${to.shed.Render()}, bijvoorbeeld: ${toImg(images.shed)}`}, + {k: new Tag("bicycle_parking", "rack"), txt: `${to.rack.Render()}, bijvoorbeeld: ${toImg(images.rack)}`}, + {k: new Tag("bicycle_parking", "two-tier"), txt: `${to.double.Render()}, bijvoorbeeld: ${toImg(images.double)}`} ] }); } diff --git a/Customizations/Questions/bike/PumpManometer.ts b/Customizations/Questions/bike/PumpManometer.ts index bc97a03..6d5e0a7 100644 --- a/Customizations/Questions/bike/PumpManometer.ts +++ b/Customizations/Questions/bike/PumpManometer.ts @@ -1,15 +1,17 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class PumpManometer extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.manometer super({ - question: "Does the pump have a pressure indicator or manometer?", + question: to.question.Render(), mappings: [ - {k: new Tag("manometer", "yes"), txt: "Yes, there is a manometer"}, - {k: new Tag("manometer","broken"), txt: "Yes, but it is broken"}, - {k: new Tag("manometer", "yes"), txt: "No"} + {k: new Tag("manometer", "yes"), txt: to.yes.Render()}, + {k: new Tag("manometer", "no"), txt: to.no.Render()}, + {k: new Tag("manometer", "broken"), txt: to.broken.Render()} ] }); } diff --git a/Customizations/Questions/bike/PumpManual.ts b/Customizations/Questions/bike/PumpManual.ts index 18b79e6..4346b67 100644 --- a/Customizations/Questions/bike/PumpManual.ts +++ b/Customizations/Questions/bike/PumpManual.ts @@ -1,15 +1,17 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class PumpManual extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.electric super({ priority: 5, - question: "Is this an electric bike pump?", + question: to.question.Render(), mappings: [ - {k: new Tag("manual", "yes"), txt: "Manual pump"}, - {k: new Tag("manual", "no"), txt: "Electric pump"} + {k: new Tag("manual", "yes"), txt: to.manual.Render()}, + {k: new Tag("manual", "no"), txt: to.electric.Render()} ] }); } diff --git a/Customizations/Questions/bike/PumpOperational.ts b/Customizations/Questions/bike/PumpOperational.ts index d7abe0f..8aa052b 100644 --- a/Customizations/Questions/bike/PumpOperational.ts +++ b/Customizations/Questions/bike/PumpOperational.ts @@ -1,14 +1,16 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class PumpOperational extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.operational super({ - question: "Is the bicycle pump still operational?", + question: to.question.Render(), mappings: [ - {k: new Tag("service:bicycle:pump:operational_status","broken"), txt: "This pump is broken"}, - {k: new Tag("service:bicycle:pump:operational_status",""), txt: "This pump is operational"} + {k: new Tag("service:bicycle:pump:operational_status","broken"), txt: to.broken.txt}, + {k: new Tag("service:bicycle:pump:operational_status",""), txt: to.operational.txt} ] }); } diff --git a/Customizations/Questions/bike/PumpValves.ts b/Customizations/Questions/bike/PumpValves.ts index ae9ebb4..5789ae9 100644 --- a/Customizations/Questions/bike/PumpValves.ts +++ b/Customizations/Questions/bike/PumpValves.ts @@ -1,24 +1,27 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class PumpValves extends TagRenderingOptions{ constructor() { + const to = Translations.t.cylofix.station.valves super({ - question: "What valves are supported?", + question: to.question.Render(), mappings: [ { k: new Tag("valves", " sclaverand;schrader;dunlop"), - txt: "There is a default head, so Presta, Dunlop and Auto" + txt: to.default.Render() }, - {k: new Tag("valves", "dunlop"), txt: "Only dunlop"}, - {k: new Tag("valves", "sclaverand"), txt: "Only Sclaverand (also known as Dunlop)"}, - {k: new Tag("valves", "auto"), txt: "Only auto"}, + {k: new Tag("valves", "dunlop"), txt: to.dunlop.Render()}, + {k: new Tag("valves", "sclaverand"), txt: to.sclaverand.Render()}, + {k: new Tag("valves", "auto"), txt: to.auto.Render()}, ], freeform: { + extraTags: new Tag("fixme", "Freeform valves= tag used: possibly a wrong value"), key: "valves", - template: "Supported valves are $$$", - renderTemplate: "Supported valves are {valves}" + template: to.template.txt, + renderTemplate: to.render.txt } }); } diff --git a/Customizations/Questions/bike/ShopPump.ts b/Customizations/Questions/bike/ShopPump.ts new file mode 100644 index 0000000..5c3882b --- /dev/null +++ b/Customizations/Questions/bike/ShopPump.ts @@ -0,0 +1,19 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopPump extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:pump' + const to = Translations.t.cylofix.shop.pump + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + ] + }); + } +} diff --git a/Customizations/Questions/bike/ShopRental.ts b/Customizations/Questions/bike/ShopRental.ts new file mode 100644 index 0000000..899100f --- /dev/null +++ b/Customizations/Questions/bike/ShopRental.ts @@ -0,0 +1,19 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopRental extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:rental' + const to = Translations.t.cylofix.shop.rental + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + ] + }); + } +} diff --git a/Customizations/Questions/bike/ShopRepair.ts b/Customizations/Questions/bike/ShopRepair.ts new file mode 100644 index 0000000..7ad84d0 --- /dev/null +++ b/Customizations/Questions/bike/ShopRepair.ts @@ -0,0 +1,19 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopRepair extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:repair' + const to = Translations.t.cylofix.shop.repair + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + ] + }); + } +} diff --git a/Customizations/Questions/bike/ShopRetail.ts b/Customizations/Questions/bike/ShopRetail.ts new file mode 100644 index 0000000..37eb35a --- /dev/null +++ b/Customizations/Questions/bike/ShopRetail.ts @@ -0,0 +1,19 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopRetail extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:retail' + const to = Translations.t.cylofix.shop.retail + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + ] + }); + } +} diff --git a/Customizations/Questions/bike/StationBrand.ts b/Customizations/Questions/bike/StationBrand.ts index 61dbc44..b763bf6 100644 --- a/Customizations/Questions/bike/StationBrand.ts +++ b/Customizations/Questions/bike/StationBrand.ts @@ -4,6 +4,8 @@ import {Tag} from "../../../Logic/TagsFilter"; /** * Currently not used in Cyclofix because it's a little vague + * + * TODO: Translations */ export default class BikeStationBrand extends TagRenderingOptions { private static options = { diff --git a/Customizations/Questions/bike/StationChain.ts b/Customizations/Questions/bike/StationChain.ts index eb5c36a..00893b7 100644 --- a/Customizations/Questions/bike/StationChain.ts +++ b/Customizations/Questions/bike/StationChain.ts @@ -1,15 +1,17 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class StationChain extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.chain super({ priority: 5, - question: "Does this bike station have a special tool to repair your bike chain?", + question: to.question.Render(), mappings: [ - {k: new Tag("service:bicycle:chain_tool", "yes"), txt: "There is a chain tool."}, - {k: new Tag("service:bicycle:chain_tool", "no"), txt: "There is no chain tool."}, + {k: new Tag("service:bicycle:chain_tool", "yes"), txt: to.yes.Render()}, + {k: new Tag("service:bicycle:chain_tool", "no"), txt: to.no.Render()}, ] }); } diff --git a/Customizations/Questions/bike/StationOperator.ts b/Customizations/Questions/bike/StationOperator.ts index 001f920..3013723 100644 --- a/Customizations/Questions/bike/StationOperator.ts +++ b/Customizations/Questions/bike/StationOperator.ts @@ -1,25 +1,27 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class BikeStationOperator extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.operator super({ priority: 15, - question: "Who operates this bike station (name of university, shop, city...)?", - freeform: { - key: "operator", - template: "This bike station is operated by $$$", - renderTemplate: "This bike station is operated by {operator}", - placeholder: "organisatie" - }, + question: to.question.Render(), mappings: [ {k: new Tag("operator", "KU Leuven"), txt: "KU Leuven"}, {k: new Tag("operator", "Stad Halle"), txt: "Stad Halle"}, {k: new Tag("operator", "Saint Gilles - Sint Gillis"), txt: "Saint Gilles - Sint Gillis"}, {k: new Tag("operator", "Jette"), txt: "Jette"}, - {k: new Tag("operator", "private"), txt: "Beheer door een privépersoon"} - ] + {k: new Tag("operator", "private"), txt: to.private.Render()} + ], + freeform: { + key: "operator", + template: to.template.txt, + renderTemplate: to.render.txt, + placeholder: "organisatie" + } }); } } diff --git a/Customizations/Questions/bike/StationPumpTools.ts b/Customizations/Questions/bike/StationPumpTools.ts index be81c8b..26866aa 100644 --- a/Customizations/Questions/bike/StationPumpTools.ts +++ b/Customizations/Questions/bike/StationPumpTools.ts @@ -1,16 +1,18 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag, And} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class BikeStationPumpTools extends TagRenderingOptions { constructor() { + const to = Translations.t.cylofix.station.services super({ priority: 15, - question: "Which services are available at this bike station?", + question: to.question.Render(), mappings: [ - {k: new And([new Tag("service:bicycle:tools", "no"), new Tag("service:bicycle:pump", "yes")]), txt: "There is only a pump available."}, - {k: new And([new Tag("service:bicycle:tools", "yes"), new Tag("service:bicycle:pump", "no")]), txt: "There are only tools (screwdrivers, pliers...) available."}, - {k: new And([new Tag("service:bicycle:tools", "yes"), new Tag("service:bicycle:pump", "yes")]), txt: "There are both tools and a pump available."} + {k: new And([new Tag("service:bicycle:tools", "no"), new Tag("service:bicycle:pump", "yes")]), txt: to.pump.Render()}, + {k: new And([new Tag("service:bicycle:tools", "yes"), new Tag("service:bicycle:pump", "no")]), txt: to.tools.Render()}, + {k: new And([new Tag("service:bicycle:tools", "yes"), new Tag("service:bicycle:pump", "yes")]), txt: to.both.Render()} ] }); } diff --git a/Customizations/Questions/bike/StationStand.ts b/Customizations/Questions/bike/StationStand.ts index 0b6fce1..be6ac4a 100644 --- a/Customizations/Questions/bike/StationStand.ts +++ b/Customizations/Questions/bike/StationStand.ts @@ -1,14 +1,16 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; export default class BikeStationStand extends TagRenderingOptions { constructor() { + const to = Translations super({ priority: 10, question: "Does this bike station have a hook to suspend your bike with or a stand to elevate it?", mappings: [ - {k: new Tag("service:bicycle:stand", "yes"), txt: "There is a hook or stand."}, + {k: new Tag("service:bicycle:stand", "yes"), txt: "There is a hook or stand"}, {k: new Tag("service:bicycle:stand", "no"), txt: "There is no hook or stand"}, ] }); diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts index b08d0d2..19c5d1a 100644 --- a/UI/CenterMessageBox.ts +++ b/UI/CenterMessageBox.ts @@ -1,6 +1,7 @@ import {UIElement} from "./UIElement"; import {UIEventSource} from "./UIEventSource"; import {OsmConnection} from "../Logic/OsmConnection"; +import Translations from "./i18n/Translations"; export class CenterMessageBox extends UIElement { @@ -41,11 +42,11 @@ export class CenterMessageBox extends UIElement { } if (this._zoomInMore.data) { - return "Zoom in om de data te zien en te bewerken"; + return Translations.t.centerMessage.zoomIn.txt; } else if (this._queryRunning.data) { - return "Data wordt geladen..."; + return Translations.t.centerMessage.loadingData.txt; } - return "Klaar!"; + return Translations.t.centerMessage.ready.txt; } diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 6542824..b04070a 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -5,6 +5,7 @@ import {Imgur} from "../Logic/Imgur"; import {UserDetails} from "../Logic/OsmConnection"; import {DropDownUI} from "./Base/DropDownUI"; import {VariableUiElement} from "./Base/VariableUIElement"; +import Translations from "./i18n/Translations"; export class ImageUploadFlow extends UIElement { private _licensePicker: UIElement; @@ -31,12 +32,11 @@ export class ImageUploadFlow extends UIElement { this._uploadOptions = uploadOptions; this.ListenTo(this._isUploading); - const licensePicker = new DropDownUI("Jouw foto wordt gepubliceerd ", - + const licensePicker = new DropDownUI(Translations.t.image.willBePublished.txt, [ - {value: "CC0", shown: "in het publiek domein"}, - {value: "CC-BY-SA 4.0", shown: "onder een CC-BY-SA-licentie"}, - {value: "CC-BY 4.0", shown: "onder een CC-BY-licentie"} + {value: "CC0", shown: Translations.t.image.cco.txt}, + {value: "CC-BY-SA 4.0", shown: Translations.t.image.ccbs.txt}, + {value: "CC-BY 4.0", shown: Translations.t.image.ccb.txt} ], preferedLicense ); @@ -65,10 +65,10 @@ export class ImageUploadFlow extends UIElement { protected InnerRender(): string { if (!this._userdetails.data.loggedIn) { - return "<div class='activate-osm-authentication'>Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden</div>"; + return `<div class='activate-osm-authentication'>${Translations.t.image.pleaseLogin.Render()}</div>`; } if (this._isUploading.data == 1) { - return "<b>Bezig met een foto te uploaden...</b>" + return `<b>${Translations.t.image.uploadingPicture.Render()}</b>` } if (this._isUploading.data > 0) { return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>" @@ -81,7 +81,7 @@ export class ImageUploadFlow extends UIElement { "<div class='imageflow-file-input-wrapper'>" + "<img src='./assets/camera-plus.svg' alt='upload image'/> " + - "<span class='imageflow-add-picture'>Voeg foto toe</span>" + + `<span class='imageflow-add-picture'>${Translations.t.image.addPicture.Render()}</span>` + "<div class='break'></div>"+ "</div>" + this._licensePicker.Render() + diff --git a/UI/i18n/Locale.ts b/UI/i18n/Locale.ts index 52c45bb..466d889 100644 --- a/UI/i18n/Locale.ts +++ b/UI/i18n/Locale.ts @@ -1,18 +1,23 @@ import { UIEventSource } from "../UIEventSource"; +import ParkingType from "../../Customizations/Questions/bike/ParkingType"; const LANGUAGE_KEY = 'language' export default class Locale { + const public static language: UIEventSource<string> = new UIEventSource(Locale.getInitialLanguage()) public static init() { Locale.language.addCallback(data => { localStorage.setItem(LANGUAGE_KEY, data) + if (window.confirm('In order to change the displayed language, the page needs to be reloaded. Reload now?')) { + location.reload() + } }) } private static getInitialLanguage() { - return localStorage.getItem(LANGUAGE_KEY) + return localStorage.getItem(LANGUAGE_KEY) || 'en' } } diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 4b53b17..74e12db 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -3,10 +3,14 @@ import Locale from "./Locale" export default class Translation extends UIElement{ - protected InnerRender(): string { + get txt(): string { return this.translations[Locale.language.data] } + protected InnerRender(): string { + return this.txt + } + public readonly translations: object constructor(translations: object) { diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index edf0561..91b6d9b 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -1,18 +1,129 @@ -import Translation from "./Translation"; +import T from "./Translation"; export default class Translations { static t = { cylofix: { - title: new Translation({en: 'Cyclofix bicycle infrastructure', nl: 'Cyclofix fietsinfrastructuur', fr: 'TODO: FRENCH TRANSLATION'}), - description: new Translation({ + title: new T({en: 'Cyclofix bicycle infrastructure', nl: 'Cyclofix fietsinfrastructuur', fr: 'TODO: FRENCH TRANSLATION'}), + description: new T({ en: "On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.", nl: "Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.", fr: "Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins." - }) + }), + freeFormPlaceholder: new T({en: 'specify', nl: 'specifieer', fr: 'TODO: fr'}), + parking: { + name: new T({en: 'bike parking', nl: 'fietsparking', fr: 'TODO: fr'}), + title: new T({en: 'Bike parking', nl: 'Fietsparking', fr: 'TODO: fr'}), + type: { + render: new T({en: 'This is a bicycle parking of the type: {bicycle_parking}', nl: 'Dit is een fietsenparking van het type: {bicycle_parking}', fr: 'TODO: fr'}), + template: new T({en: 'Some other type: $$$', nl: 'Een ander type: $$$', fr: 'TODO: fr'}), + question: new T({en: 'What is the type of this bicycle parking?', nl: 'Van welk type is deze fietsenparking?', fr: 'TODO: fr'}), + stands: new T({en: 'Staple racks', nl: 'Nietjes', fr: 'TODO: fr'}), + loops: new T({en: 'Wheel rack/loops', nl: 'Wielrek/lussen', fr: 'TODO: fr'}), + handlebar: new T({en: 'Handlebar holder', nl: 'Stuurhouder', fr: 'TODO: fr'}), + shed: new T({en: 'Shed', nl: 'Schuur', fr: 'TODO: fr'}), + rack: new T({en: 'Rack', nl: 'Rek', fr: 'TODO: fr'}), + double: new T({en: 'Two-tiered', nl: 'Dubbel (twee verdiepingen)', fr: 'TODO: fr'}), + }, + operator: { + render: new T({en: 'This bike parking is operated by {operator}', nl: 'Deze fietsenparking wordt beheerd door {operator}', fr: 'TODO: fr'}), + template: new T({en: 'A different operator: $$$', nl: 'Een andere beheerder: $$$', fr: 'TODO: fr'}), + question: new T({en: 'Who operates this bike station (name of university, shop, city...)?', nl: 'Wie beheert deze fietsenparking (naam universiteit, winkel, stad...)?', fr: 'TODO: fr'}), + private: new T({en: 'Operated by a private person', nl: 'Wordt beheerd door een privépersoon', fr: 'TODO: fr'}), + } + }, + station: { + name: new T({en: 'bike station (repair, pump or both)', nl: 'fietsstation (herstel, pomp of allebei)', fr: 'TODO: fr'}), + title: new T({en: 'Bike station', nl: 'Fietsstation', fr: 'TODO: fr'}), + manometer: { + question: new T({en: 'Does the pump have a pressure indicator or manometer?', nl: 'Heeft deze pomp een luchtdrukmeter?', fr: 'TODO: fr'}), + yes: new T({en: 'There is a manometer', nl: 'Er is een luchtdrukmeter', fr: 'TODO: fr'}), + no: new T({en: 'There is no manometer', nl: 'Er is geen luchtdrukmeter', fr: 'TODO: fr'}), + broken: new T({en: 'There is manometer but it is broken', nl: 'Er is een luchtdrukmeter maar die is momenteel defect', fr: 'TODO: fr'}) + }, + electric: { + question: new T({en: 'Is this an electric bike pump?', nl: 'Is dit een electrische fietspomp?', fr: 'TODO: fr'}), + manual: new T({en: 'Manual pump', nl: 'Manuele pomp', fr: 'TODO: fr'}), + electric: new T({en: 'Electrical pump', nl: 'Electrische pomp', fr: 'TODO: fr'}) + }, + operational: { + question: new T({en: 'Is the bike pump still operational?', nl: 'Werkt de fietspomp nog?', fr: 'TODO: fr'}), + operational: new T({en: 'The bike pump is operational', nl: 'De fietspomp werkt nog', fr: 'TODO: fr'}), + broken: new T({en: 'The bike pump is broken', nl: 'De fietspomp is kapot', fr: 'TODO: fr'}) + }, + valves: { + question: new T({en: 'What valves are supported?', nl: 'Welke ventielen werken er met de pomp?', fr: 'TODO: fr'}), + default: new T({en: 'There is a default head, so Dunlop, Sclaverand and auto', nl: 'Er is een standaard aansluiting, die dus voor Dunlop, Sclaverand en auto\'s werkt', fr: 'TODO: fr'}), + dunlop: new T({en: 'Only Dunlop', nl: 'Enkel Dunlop', fr: 'TODO: fr'}), + sclaverand: new T({en: 'Only Sclaverand (also known as Presta)', nl: 'Enkel Sclaverand (ook gekend als Presta)', fr: 'TODO: fr'}), + auto: new T({en: 'Only for cars', nl: 'Enkel voor auto\'s', fr: 'TODO: fr'}), + render: new T({en: 'This pump supports the following valves: {valves}', nl: 'Deze pomp werkt met de volgende ventielen: {valves}', fr: 'TODO: fr'}), + template: new T({en: 'Some other valve(s): $$$', nl: 'Een ander type ventiel(en): $$$', fr: 'TODO: fr'}) + }, + chain: { + question: new T({en: 'Does this bike station have a special tool to repair your bike chain?', nl: 'Heeft dit fietsstation een speciale reparatieset voor je ketting?', fr: 'TODO: fr'}), + yes: new T({en: 'There is a chain tool', nl: 'Er is een reparatieset voor je ketting', fr: 'TODO: fr'}), + no: new T({en: 'There is no chain tool', nl: 'Er is geen reparatieset voor je ketting', fr: 'TODO: fr'}), + }, + operator: { + render: new T({en: 'This bike station is operated by {operator}', nl: 'Dit fietsstation wordt beheerd door {operator}', fr: 'TODO: fr'}), + template: new T({en: 'A different operator: $$$', nl: 'Een andere beheerder: $$$', fr: 'TODO: fr'}), + question: new T({en: 'Who operates this bike station (name of university, shop, city...)?', nl: 'Wie beheert dit fietsstation (naam universiteit, winkel, stad...)?', fr: 'TODO: fr'}), + private: new T({en: 'Operated by a private person', nl: 'Wordt beheerd door een privépersoon', fr: 'TODO: fr'}), + }, + services: { + question: new T({en: 'Which services are available at this bike station?', nl: 'Welke functies biedt dit fietsstation?', fr: 'TODO: fr'}), + pump: new T({en: 'There is only a pump available', nl: 'Er is enkel een pomp beschikbaar', fr: 'TODO: fr'}), + tools: new T({en: 'There are only tools (screwdrivers, pliers...) available', nl: 'Er is enkel gereedschap beschikbaar (schroevendraaier, tang...)', fr: 'TODO: fr'}), + both: new T({en: 'There are both tools and a pump available', nl: 'Er is zowel een pomp als gereedschap beschikbaar', fr: 'TODO: fr'}), + }, + stand: { + question: new T({en: 'Does this bike station have a hook to suspend your bike with or a stand to elevate it?', nl: 'Heeft dit fietsstation een haak of standaard om je fiets op te hangen/zetten?', fr: 'TODO: fr'}), + yes: new T({en: 'There is a hook or stand', nl: 'Er is een haak of standaard', fr: 'TODO: fr'}), + no: new T({en: 'There is no hook or stand', nl: 'Er is geen haak of standaard', fr: 'TODO: fr'}), + } + }, + shop: { + name: new T({en: 'bike shop', nl: 'fietswinkel', fr: 'TODO: fr'}), + title: new T({en: 'Bike shop', nl: 'Fietswinkel', fr: 'TODO: fr'}), + retail: { + question: new T({en: 'Does this shop sell bikes?', nl: 'Verkoopt deze winkel fietsen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop sells bikes', nl: 'Deze winkel verkoopt fietsen', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t sell bikes', nl: 'Deze winkel verkoopt geen fietsen', fr: 'TODO: fr'}), + }, + repair: { + question: new T({en: 'Does this shop repair bikes?', nl: 'Verkoopt deze winkel fietsen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop repairs bikes', nl: 'Deze winkel herstelt fietsen', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t repair bikes', nl: 'Deze winkel herstelt geen fietsen', fr: 'TODO: fr'}), + }, + rental: { + question: new T({en: 'Does this shop rent out bikes?', nl: 'Verhuurt deze winkel fietsen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop rents out bikes', nl: 'Deze winkel verhuurt fietsen', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t rent out bikes', nl: 'Deze winkel verhuurt geen fietsen', fr: 'TODO: fr'}), + }, + pump: { + question: new T({en: 'Does this shop offer a bike pump for use by anyone?', nl: 'Biedt deze winkel een fietspomp aan voor iedereen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop offers a bike pump for anyone', nl: 'Deze winkel biedt geen fietspomp aan voor eender wie', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t offer a bike pump for anyone', nl: 'Deze winkel biedt een fietspomp aan voor iedereen', fr: 'TODO: fr'}), + } + } + }, + image: { + addPicture: new T({en: 'Add picture', nl: 'Voeg foto toe', fr: 'TODO: fr'}), + uploadingPicture: new T({en: 'Uploading your picture...', nl: 'Bezig met een foto te uploaden...', fr: 'TODO: fr'}), + pleaseLogin: new T({en: 'Please login to add a picure or to answer questions', nl: 'Gelieve je aan te melden om een foto toe te voegen of vragen te beantwoorden', fr: 'TODO: fr'}), + willBePublished: new T({en: 'Your picture will be published: ', nl: 'Jouw foto wordt gepubliceerd: ', fr: 'TODO: fr'}), + cco: new T({en: 'in the public domain', nl: 'in het publiek domein', fr: 'TODO: fr'}), + ccbs: new T({en: 'under the CC-BY-SA-license', nl: 'onder de CC-BY-SA-licentie', fr: 'TODO: fr'}), + ccb: new T({en: 'under the CC-BY-license', nl: 'onder de CC-BY-licentie', fr: 'TODO: fr'}) + }, + centerMessage: { + loadingData: new T({en: 'Loading data...', nl: 'Data wordt geladen...', fr: 'TODO: fr'}), + zoomIn: new T({en: 'Zoom in to view or edit the data', nl: 'Zoom in om de data te zien en te bewerken', fr: 'TODO: fr'}), + ready: new T({en: 'Done!', nl: 'Klaar!', fr: 'TODO: fr'}), } } } diff --git a/index.css b/index.css index 672e558..3c7afcf 100644 --- a/index.css +++ b/index.css @@ -241,6 +241,9 @@ form { #language-select { pointer-events: all; cursor: pointer; + position: absolute; + margin-left: 2em; + margin-top: 0.5em; } #messagesbox-wrapper { diff --git a/index.ts b/index.ts index 0e41b1f..e4ad766 100644 --- a/index.ts +++ b/index.ts @@ -26,6 +26,7 @@ import Translations from "./UI/i18n/Translations"; import Translation from "./UI/i18n/Translation"; import Locale from "./UI/i18n/Locale"; import { Layout } from "./Customizations/Layout"; +import ParkingType from "./Customizations/Questions/bike/ParkingType"; @@ -94,9 +95,10 @@ if (paramDict.test) { const layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout]; console.log("Using layout: ", layoutToUse.name); -document.title = layoutToUse.title.Render(); +const uiElToTxt = el => el instanceof Translation ? el.txt : el.Render() +document.title = uiElToTxt(layoutToUse.title) Locale.language.addCallback(e => { - document.title = layoutToUse.title.Render(); + document.title = uiElToTxt(layoutToUse.title) }) @@ -294,5 +296,9 @@ window.setLanguage = function(language:string) { Locale.language.setData(language) } -// const eLanguageSelect = document.getElementById('language-select') as HTMLOptionElement -// eLanguageSelect.addEventListener('selectionchange') +const eLanguageSelect = document.getElementById('language-select') as HTMLOptionElement +eLanguageSelect.addEventListener('input', e => { + // @ts-ignore + const selectedLanguage = e.target.value as string + Locale.language.setData(selectedLanguage.toLowerCase()) +}) From 369c19a58aeb31bbd597f1e7814d0b45b4abac8c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 00:07:04 +0200 Subject: [PATCH 22/29] Translations --- Customizations/Layers/BikeShop.ts | 12 +- Customizations/Layout.ts | 3 + Customizations/Layouts/All.ts | 1 + Customizations/Layouts/Bookcases.ts | 1 + Customizations/Layouts/Cyclofix.ts | 9 +- Customizations/Layouts/GRB.ts | 1 + Customizations/Layouts/Groen.ts | 1 + Customizations/Layouts/MetaMap.ts | 1 + Customizations/Layouts/Natuurpunt.ts | 1 + Customizations/Layouts/Statues.ts | 1 + Customizations/Layouts/StreetWidth.ts | 1 + Customizations/Layouts/Toilets.ts | 1 + Customizations/Layouts/WalkByBrussels.ts | 1 + Customizations/TagRendering.ts | 139 +++++++++++------------ Logic/OsmConnection.ts | 1 + Logic/StrayClickHandler.ts | 8 +- UI/CenterMessageBox.ts | 2 +- UI/FeatureInfoBox.ts | 4 +- UI/Image/ImageCarouselWithUpload.ts | 4 +- UI/ImageUploadFlow.ts | 3 +- UI/Input/DropDown.ts | 9 +- UI/Input/TextField.ts | 1 - UI/MessageBoxHandler.ts | 21 ++-- UI/PendingChanges.ts | 2 +- UI/UIElement.ts | 3 +- UI/UIEventSource.ts | 11 ++ UI/UserBadge.ts | 8 +- UI/i18n/Locale.ts | 28 +++-- UI/i18n/Translation.ts | 24 +++- UI/i18n/Translations.ts | 48 ++++++-- index.css | 15 ++- index.html | 6 +- index.ts | 79 ++++++++----- test.ts | 10 +- 34 files changed, 287 insertions(+), 173 deletions(-) diff --git a/Customizations/Layers/BikeShop.ts b/Customizations/Layers/BikeShop.ts index 3e4413a..d1b1bea 100644 --- a/Customizations/Layers/BikeShop.ts +++ b/Customizations/Layers/BikeShop.ts @@ -1,6 +1,6 @@ import {TagRenderingOptions} from "../TagRendering"; import {LayerDefinition} from "../LayerDefinition"; -import {Tag} from "../../Logic/TagsFilter"; +import {And, Tag} from "../../Logic/TagsFilter"; import L from "leaflet"; import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; import {NameQuestion} from "../Questions/NameQuestion"; @@ -24,6 +24,16 @@ export class BikeShop extends LayerDefinition { this.title = new TagRenderingOptions({ mappings: [ + {k: new And([new Tag("name", "*"), this.sellsBikes]), txt: "Bicycle shop {name}"}, + { + k: new And([new Tag("name", "*"), new Tag("service:bicycle:retail", "no")]), + txt: "Bicycle repair {name}", + }, + { + k: new And([new Tag("name", "*"), new Tag("service:bicycle:retail", "")]), + txt: "Bicycle repair {name}" + }, + {k: this.sellsBikes, txt: "Bicycle shop"}, {k: new Tag("service:bicycle:retail", "no"), txt: "Bicycle repair"}, {k: new Tag("service:bicycle:retail", ""), txt: "Bicycle repair/shop"}, diff --git a/Customizations/Layout.ts b/Customizations/Layout.ts index a1d888e..a883bd8 100644 --- a/Customizations/Layout.ts +++ b/Customizations/Layout.ts @@ -14,6 +14,7 @@ export class Layout { public welcomeBackMessage: string; public startzoom: number; + public supportedLanguages: string[]; public startLon: number; public startLat: number; public welcomeTail: string; @@ -35,6 +36,7 @@ export class Layout { */ constructor( name: string, + supportedLanguages: string[], title: UIElement | string, layers: LayerDefinition[], startzoom: number, @@ -45,6 +47,7 @@ export class Layout { welcomeBackMessage: string = "You are logged in. Welcome back!", welcomeTail: string = "" ) { + this.supportedLanguages = supportedLanguages; this.title = typeof(title) === 'string' ? new FixedUiElement(title) : title; this.startLon = startLon; this.startLat = startLat; diff --git a/Customizations/Layouts/All.ts b/Customizations/Layouts/All.ts index a415c2e..54065b4 100644 --- a/Customizations/Layouts/All.ts +++ b/Customizations/Layouts/All.ts @@ -4,6 +4,7 @@ export class All extends Layout{ constructor() { super( "all", + ["en"], "All quest layers", [], 15, diff --git a/Customizations/Layouts/Bookcases.ts b/Customizations/Layouts/Bookcases.ts index 833150b..d833d6e 100644 --- a/Customizations/Layouts/Bookcases.ts +++ b/Customizations/Layouts/Bookcases.ts @@ -4,6 +4,7 @@ import * as Layer from "../Layers/Bookcases"; export class Bookcases extends Layout{ constructor() { super( "bookcases", + ["nl"], "Open Bookcase Map", [new Layer.Bookcases()], 14, diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 860149d..c4fc3e7 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -4,21 +4,22 @@ import BikeServices from "../Layers/BikeStations"; import {GhostBike} from "../Layers/GhostBike"; import Translations from "../../UI/i18n/Translations"; import {DrinkingWater} from "../Layers/DrinkingWater"; -import {BikeShop} from "../Layers/BikeShop"; +import {BikeShop} from "../Layers/BikeShop" export default class Cyclofix extends Layout { constructor() { super( "pomp", - Translations.t.cylofix.title, + ["en", "nl", "fr"], + Translations.cylofix.title, [new BikeServices(), new BikeShop(), new DrinkingWater(), new BikeParkings()], 16, 50.8465573, 4.3516970, - "<h3>" + Translations.t.cylofix.title.Render() + "</h3>\n" + + "<h3>" + Translations.cylofix.title.Render() + "</h3>\n" + "\n" + - `<p>${Translations.t.cylofix.description.Render()}</p>` + `<p>${Translations.cylofix.description.Render()}</p>` , "", ""); } diff --git a/Customizations/Layouts/GRB.ts b/Customizations/Layouts/GRB.ts index 93514a1..21ba2ab 100644 --- a/Customizations/Layouts/GRB.ts +++ b/Customizations/Layouts/GRB.ts @@ -4,6 +4,7 @@ import {GrbToFix} from "../Layers/GrbToFix"; export class GRB extends Layout { constructor() { super("grb", + ["en"], "Grb import fix tool", [new GrbToFix()], 15, diff --git a/Customizations/Layouts/Groen.ts b/Customizations/Layouts/Groen.ts index 7f3d0f9..991a618 100644 --- a/Customizations/Layouts/Groen.ts +++ b/Customizations/Layouts/Groen.ts @@ -7,6 +7,7 @@ export class Groen extends Layout { constructor() { super("buurtnatuur", + ["nl"], "Buurtnatuur", [new NatureReserves(), new Park(), new Bos()], 10, diff --git a/Customizations/Layouts/MetaMap.ts b/Customizations/Layouts/MetaMap.ts index 8302f98..c174bc6 100644 --- a/Customizations/Layouts/MetaMap.ts +++ b/Customizations/Layouts/MetaMap.ts @@ -5,6 +5,7 @@ import {Map} from "../Layers/Map"; export class MetaMap extends Layout{ constructor() { super( "metamap", + ["en"], "Open Map Map", [new Map()], 1, diff --git a/Customizations/Layouts/Natuurpunt.ts b/Customizations/Layouts/Natuurpunt.ts index 8216fcb..78c0c7a 100644 --- a/Customizations/Layouts/Natuurpunt.ts +++ b/Customizations/Layouts/Natuurpunt.ts @@ -7,6 +7,7 @@ export class Natuurpunt extends Layout{ constructor() { super( "natuurpunt", + ["nl"], "De natuur in", [new Birdhide(), new InformationBoard(), new NatureReserves(true)], 12, diff --git a/Customizations/Layouts/Statues.ts b/Customizations/Layouts/Statues.ts index 76a46dd..d355fa4 100644 --- a/Customizations/Layouts/Statues.ts +++ b/Customizations/Layouts/Statues.ts @@ -5,6 +5,7 @@ export class Statues extends Layout{ constructor() { super( "statues", "Open Artwork Map", + ["en"], [new Artwork()], 10, 50.8435, diff --git a/Customizations/Layouts/StreetWidth.ts b/Customizations/Layouts/StreetWidth.ts index f93e8a1..4d72b6e 100644 --- a/Customizations/Layouts/StreetWidth.ts +++ b/Customizations/Layouts/StreetWidth.ts @@ -7,6 +7,7 @@ export class StreetWidth extends Layout{ constructor() { super( "width", + ["nl"], "Straatbreedtes in Brugge", [new Widths( 2, diff --git a/Customizations/Layouts/Toilets.ts b/Customizations/Layouts/Toilets.ts index c6c585e..1598c0f 100644 --- a/Customizations/Layouts/Toilets.ts +++ b/Customizations/Layouts/Toilets.ts @@ -4,6 +4,7 @@ import * as Layer from "../Layers/Toilets"; export class Toilets extends Layout{ constructor() { super( "toilets", + ["en"], "Open Toilet Map", [new Layer.Toilets()], 12, diff --git a/Customizations/Layouts/WalkByBrussels.ts b/Customizations/Layouts/WalkByBrussels.ts index ca8fdc4..499b774 100644 --- a/Customizations/Layouts/WalkByBrussels.ts +++ b/Customizations/Layouts/WalkByBrussels.ts @@ -6,6 +6,7 @@ import { Park } from "../Layers/Park"; export class WalkByBrussels extends Layout { constructor() { super("walkbybrussels", + ["en","fr","nl"], "Drinking Water Spots", [new DrinkingWater(), new Park(), new NatureReserves()], 10, diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index ca7a8d3..67b2b28 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -13,6 +13,7 @@ import {InputElement} from "../UI/Input/InputElement"; import {InputElementWrapper} from "../UI/Input/InputElementWrapper"; import {FixedInputElement} from "../UI/Input/FixedInputElement"; import {RadioButton} from "../UI/Input/RadioButton"; +import Translations from "../UI/i18n/Translations"; export class TagRenderingOptions implements TagDependantUIElementConstructor { @@ -21,8 +22,17 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { */ public options: { - priority?: number; question?: string; primer?: string; - freeform?: { key: string; tagsPreprocessor?: (tags: any) => any; template: string; renderTemplate: string; placeholder?: string; extraTags?: TagsFilter }; mappings?: { k: TagsFilter; txt: string; priority?: number, substitute?: boolean }[] + priority?: number; + question?: string | UIElement; + freeform?: { + key: string; + tagsPreprocessor?: (tags: any) => any; + template: string; + renderTemplate: string; + placeholder?: string; + extraTags?: TagsFilter + }; + mappings?: { k: TagsFilter; txt: string | UIElement; priority?: number, substitute?: boolean }[] }; @@ -73,12 +83,6 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { }, - /** - * Optional: - * if defined, this a common piece of tag that is shown in front of every mapping (except freeform) - */ - primer?: string, - /** * In some very rare cases, tags have to be rewritten before displaying * This function can be used for that. @@ -86,6 +90,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { */ tagsPreprocessor?: ((tags: any) => void) }) { + this.options = options; } @@ -134,10 +139,9 @@ class TagRendering extends UIElement implements TagDependantUIElement { private _priority: number; - private _question: string; - private _primer: string; - private _mapping: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[]; - private _renderMapping: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[]; + private _question: UIElement; + private _mapping: { k: TagsFilter, txt: UIElement, priority?: number }[]; + private _renderMapping: { k: TagsFilter, txt: UIElement, priority?: number }[]; private _tagsPreprocessor?: ((tags: any) => any); private _freeform: { @@ -163,8 +167,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { constructor(tags: UIEventSource<any>, changes: Changes, options: { priority?: number - question?: string, - primer?: string, + question?: string | UIElement, freeform?: { key: string, template: string, @@ -173,7 +176,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { extraTags?: TagsFilter, }, tagsPreprocessor?: ((tags: any) => any), - mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[] + mappings?: { k: TagsFilter, txt: string | UIElement, priority?: number, substitute?: boolean }[] }) { super(tags); const self = this; @@ -183,9 +186,10 @@ class TagRendering extends UIElement implements TagDependantUIElement { this._userDetails = changes.login.userDetails; this.ListenTo(this._userDetails); - this._question = options.question; + if (options.question !== undefined) { + this._question = Translations.W(options.question); + } this._priority = options.priority ?? 0; - this._primer = options.primer ?? ""; this._tagsPreprocessor = function (properties) { if (options.tagsPreprocessor === undefined) { return properties; @@ -202,38 +206,28 @@ class TagRendering extends UIElement implements TagDependantUIElement { this._renderMapping = []; this._freeform = options.freeform; - // Prepare the choices for the Radio buttons - const choices: UIElement[] = []; - const usedChoices: string [] = []; for (const choice of options.mappings ?? []) { - if (choice.k === null) { - this._mapping.push(choice); - continue; - } - let choiceSubbed = choice; + let choiceSubbed = { + k: choice.k, + txt: this.ApplyTemplate(choice.txt), + priority: choice.priority + }; + if (choice.substitute) { choiceSubbed = { k: choice.k.substituteValues( options.tagsPreprocessor(this._source.data)), txt: this.ApplyTemplate(choice.txt), - substitute: false, priority: choice.priority } } - const txt = choiceSubbed.txt - // Choices is what is shown in the radio buttons - if (usedChoices.indexOf(txt) < 0) { - - choices.push(new FixedUiElement(txt)); - usedChoices.push(txt); - // This is used to convert the radio button index into tags needed to add - this._mapping.push(choiceSubbed); - } else { - this._renderMapping.push(choiceSubbed); // only used while rendering - } + this._mapping.push({ + k: choiceSubbed.k, + txt: choiceSubbed.txt + }); } @@ -287,7 +281,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { placeholder?: string, extraTags?: TagsFilter, }, - mappings?: { k: TagsFilter, txt: string, priority?: number, substitute?: boolean }[] + mappings?: { k: TagsFilter, txt: string | UIElement, priority?: number, substitute?: boolean }[] }): InputElement<TagsFilter> { @@ -315,7 +309,8 @@ class TagRendering extends UIElement implements TagDependantUIElement { if (elements.length == 0) { - throw "NO TAGRENDERINGS!" + console.warn("WARNING: no tagrendering with following options:", options); + return new FixedInputElement("This should not happen: no tag renderings defined", undefined); } if (elements.length == 1) { return elements[0]; @@ -326,7 +321,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { } - private InputElementForMapping(mapping: { k: TagsFilter, txt: string }) { + private InputElementForMapping(mapping: { k: TagsFilter, txt: string | UIElement }) { return new FixedInputElement(mapping.txt, mapping.k); } @@ -421,10 +416,10 @@ class TagRendering extends UIElement implements TagDependantUIElement { return true; } - private RenderAnwser(): string { + private RenderAnwser(): UIElement { const tags = TagUtils.proprtiesToKV(this._source.data); - let freeform = ""; + let freeform: UIElement = new FixedUiElement(""); let freeformScore = -10; if (this._freeform !== undefined && this._source.data[this._freeform.key] !== undefined) { freeform = this.ApplyTemplate(this._freeform.renderTemplate); @@ -432,30 +427,30 @@ class TagRendering extends UIElement implements TagDependantUIElement { } - let highestScore = -100; - let highestTemplate = undefined; - for (const oneOnOneElement of this._mapping.concat(this._renderMapping)) { - if (oneOnOneElement.k == null || - oneOnOneElement.k.matches(tags)) { - // We have found a matching key -> we use the template, but only if it scores better - let score = oneOnOneElement.priority ?? - (oneOnOneElement.k === null ? -1 : 0); - if (score > highestScore) { - highestScore = score; - highestTemplate = oneOnOneElement.txt - } + let highestScore = -100; + let highestTemplate = undefined; + for (const oneOnOneElement of this._mapping.concat(this._renderMapping)) { + if (oneOnOneElement.k == null || + oneOnOneElement.k.matches(tags)) { + // We have found a matching key -> we use the template, but only if it scores better + let score = oneOnOneElement.priority ?? + (oneOnOneElement.k === null ? -1 : 0); + if (score > highestScore) { + highestScore = score; + highestTemplate = oneOnOneElement.txt } } + } - if (freeformScore > highestScore) { - return freeform; - } + if (freeformScore > highestScore) { + return freeform; + } + + if (highestTemplate !== undefined) { + // we render the found template + return this.ApplyTemplate(highestTemplate); + } - if (highestTemplate !== undefined) { - // we render the found template - return this._primer + this.ApplyTemplate(highestTemplate); - } - } @@ -464,23 +459,24 @@ class TagRendering extends UIElement implements TagDependantUIElement { if (this.IsQuestioning() || this._editMode.data) { // Not yet known or questioning, we have to ask a question + const question = this._question.Render(); return "<div class='question'>" + - "<span class='question-text'>" + this._question + "</span>" + - (this._question !== "" ? "<br/>" : "") + - "<div>"+ this._questionElement.Render() +"</div>"+ + "<span class='question-text'>" + question + "</span>" + + (question !== "" ? "<br/>" : "") + + "<div>" + this._questionElement.Render() + "</div>" + this._skipButton.Render() + this._saveButton.Render() + "</div>" } if (this.IsKnown()) { - const html = this.RenderAnwser(); + const html = this.RenderAnwser().Render(); if (html == "") { return ""; } let editButton = ""; - if(this._userDetails.data.loggedIn){ + if (this._userDetails.data.loggedIn && this._question !== undefined) { editButton = this._editButton.Render(); } @@ -499,12 +495,15 @@ class TagRendering extends UIElement implements TagDependantUIElement { return this._priority; } - private ApplyTemplate(template: string): string { + private ApplyTemplate(template: string | UIElement): UIElement { + if (template instanceof UIElement) { + return template; + } const tags = this._tagsPreprocessor(this._source.data); - return TagUtils.ApplyTemplate(template, tags); + return new FixedUiElement(TagUtils.ApplyTemplate(template, tags)); } - + InnerUpdate(htmlElement: HTMLElement) { super.InnerUpdate(htmlElement); this._questionElement.Update(); // Another manual update for them diff --git a/Logic/OsmConnection.ts b/Logic/OsmConnection.ts index 648d9bb..04d2979 100644 --- a/Logic/OsmConnection.ts +++ b/Logic/OsmConnection.ts @@ -123,6 +123,7 @@ export class OsmConnection { public preferenceSources : any = {} public GetPreference(key: string) : UIEventSource<string>{ + key = "mapcomplete-"+key; if (this.preferenceSources[key] !== undefined) { return this.preferenceSources[key]; } diff --git a/Logic/StrayClickHandler.ts b/Logic/StrayClickHandler.ts index 89acbcc..8509fd4 100644 --- a/Logic/StrayClickHandler.ts +++ b/Logic/StrayClickHandler.ts @@ -10,16 +10,16 @@ import {UIElement} from "../UI/UIElement"; export class StrayClickHandler { private _basemap: Basemap; private _lastMarker; - private _leftMessage: UIEventSource<() => UIElement>; + private _fullScreenMessage: UIEventSource<UIElement>; private _uiToShow: (() => UIElement); constructor( basemap: Basemap, selectElement: UIEventSource<any>, - leftMessage: UIEventSource<() => UIElement>, + fullScreenMessage: UIEventSource<UIElement>, uiToShow: (() => UIElement)) { this._basemap = basemap; - this._leftMessage = leftMessage; + this._fullScreenMessage = fullScreenMessage; this._uiToShow = uiToShow; const self = this; const map = basemap.map; @@ -38,7 +38,7 @@ export class StrayClickHandler { self._lastMarker.bindPopup(popup).openPopup(); self._lastMarker.on("click", () => { - leftMessage.setData(self._uiToShow); + fullScreenMessage.setData(self._uiToShow()); }); uiElement.Update(); uiElement.Activate(); diff --git a/UI/CenterMessageBox.ts b/UI/CenterMessageBox.ts index a5cd8b6..ffb864e 100644 --- a/UI/CenterMessageBox.ts +++ b/UI/CenterMessageBox.ts @@ -34,7 +34,7 @@ export class CenterMessageBox extends UIElement { } - protected InnerRender(): string { + InnerRender(): string { if (this._centermessage.data != "") { return this._centermessage.data; diff --git a/UI/FeatureInfoBox.ts b/UI/FeatureInfoBox.ts index 47b5011..7c47878 100644 --- a/UI/FeatureInfoBox.ts +++ b/UI/FeatureInfoBox.ts @@ -10,7 +10,7 @@ import {TagRenderingOptions} from "../Customizations/TagRendering"; import {OsmLink} from "../Customizations/Questions/OsmLink"; import {WikipediaLink} from "../Customizations/Questions/WikipediaLink"; import {And} from "../Logic/TagsFilter"; -import {TagDependantUIElement} from "../Customizations/UIElementConstructor"; +import {TagDependantUIElement, TagDependantUIElementConstructor} from "../Customizations/UIElementConstructor"; export class FeatureInfoBox extends UIElement { @@ -31,7 +31,7 @@ export class FeatureInfoBox extends UIElement { constructor( tagsES: UIEventSource<any>, title: TagRenderingOptions, - elementsToShow: TagRenderingOptions[], + elementsToShow: TagDependantUIElementConstructor[], changes: Changes, userDetails: UIEventSource<UserDetails> ) { diff --git a/UI/Image/ImageCarouselWithUpload.ts b/UI/Image/ImageCarouselWithUpload.ts index 38ebe20..8ce0225 100644 --- a/UI/Image/ImageCarouselWithUpload.ts +++ b/UI/Image/ImageCarouselWithUpload.ts @@ -34,14 +34,14 @@ class ImageCarouselWithUpload extends TagDependantUIElement { const changes = dependencies.changes; this._imageElement = new ImageCarousel(tags, changes); const userDetails = changes.login.userDetails; - const license = changes.login.GetPreference( "mapcomplete-pictures-license"); + const license = changes.login.GetPreference( "pictures-license"); this._pictureUploader = new OsmImageUploadHandler(tags, userDetails, license, changes, this._imageElement.slideshow).getUI(); } - protected InnerRender(): string { + InnerRender(): string { return this._imageElement.Render() + this._pictureUploader.Render(); } diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 3d0f633..1a628ad 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -5,6 +5,7 @@ import {Imgur} from "../Logic/Imgur"; import {UserDetails} from "../Logic/OsmConnection"; import {DropDown} from "./Input/DropDown"; import {VariableUiElement} from "./Base/VariableUIElement"; +import Translations from "./i18n/Translations"; export class ImageUploadFlow extends UIElement { private _licensePicker: UIElement; @@ -66,7 +67,7 @@ export class ImageUploadFlow extends UIElement { "<div class='imageflow-file-input-wrapper'>" + "<img src='./assets/camera-plus.svg' alt='upload image'/> " + - "<span class='imageflow-add-picture'>Add a picture</span>" + + "<span class='imageflow-add-picture'>"+Translations.general.uploadAPicture.R()+"</span>" + "<div class='break'></div>" + "</div>" + diff --git a/UI/Input/DropDown.ts b/UI/Input/DropDown.ts index fb15f02..dfbcafd 100644 --- a/UI/Input/DropDown.ts +++ b/UI/Input/DropDown.ts @@ -50,7 +50,10 @@ export class DropDown<T> extends InputElement<T> { InnerRender(): string { - + if(this._values.length <=1){ + return ""; + } + let options = ""; for (let i = 0; i < this._values.length; i++) { options += "<option value='" + i + "'>" + this._values[i].shown.InnerRender() + "</option>" @@ -65,8 +68,12 @@ export class DropDown<T> extends InputElement<T> { } protected InnerUpdate(element) { + var e = document.getElementById("dropdown-" + this.id); + if(e === null){ + return; + } const self = this; e.onchange = (() => { // @ts-ignore diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 0d7eee2..44a816b 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -105,7 +105,6 @@ export class TextField<T> extends InputElement<T> { } IsValid(t: T): boolean { - console.log("TXT IS valid?",t,this._toString(t)) if(t === undefined || t === null){ return false; } diff --git a/UI/MessageBoxHandler.ts b/UI/MessageBoxHandler.ts index 42bf403..a06a25a 100644 --- a/UI/MessageBoxHandler.ts +++ b/UI/MessageBoxHandler.ts @@ -6,9 +6,9 @@ import {UIElement} from "./UIElement"; import {VariableUiElement} from "./Base/VariableUIElement"; export class MessageBoxHandler { - private _uielement: UIEventSource<() => UIElement>; + private _uielement: UIEventSource<UIElement>; - constructor(uielement: UIEventSource<() => UIElement>, + constructor(uielement: UIEventSource<UIElement>, onClear: (() => void)) { this._uielement = uielement; this.listenTo(uielement); @@ -22,14 +22,13 @@ export class MessageBoxHandler { } } - new VariableUiElement(new UIEventSource<string>("<h2>Return to the map</h2>"), - () => { - document.getElementById("to-the-map").onclick = function () { - uielement.setData(undefined); - onClear(); - } - } - ).AttachTo("to-the-map"); + new VariableUiElement(new UIEventSource<string>("<h2>Return to the map</h2>")) + .onClick(() => { + console.log("Clicked 'return to the map'") + uielement.setData(undefined); + onClear(); + }) + .AttachTo("to-the-map"); } @@ -55,7 +54,7 @@ export class MessageBoxHandler { location.hash = "#element" wrapper.classList.remove("hidden"); - gen() + gen ?.HideOnEmpty(true) ?.AttachTo("messagesboxmobile") ?.Activate(); diff --git a/UI/PendingChanges.ts b/UI/PendingChanges.ts index e0411a5..cda6214 100644 --- a/UI/PendingChanges.ts +++ b/UI/PendingChanges.ts @@ -21,7 +21,7 @@ export class PendingChanges extends UIElement { }) } - protected InnerRender(): string { + InnerRender(): string { if (this._isSaving.data) { return "<span class='alert'>Saving</span>"; } diff --git a/UI/UIElement.ts b/UI/UIElement.ts index a0b9d49..8e1ff27 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -37,11 +37,10 @@ export abstract class UIElement { Update(): void { let element = document.getElementById(this.id); - if (element === null || element === undefined) { + if (element === undefined || element === null) { // The element is not painted return; } - element.innerHTML = this.InnerRender(); if (this._hideIfEmpty) { if (element.innerHTML === "") { diff --git a/UI/UIEventSource.ts b/UI/UIEventSource.ts index 54902a0..9529468 100644 --- a/UI/UIEventSource.ts +++ b/UI/UIEventSource.ts @@ -66,5 +66,16 @@ export class UIEventSource<T>{ } + + public syncWith(otherSource: UIEventSource<T>){ + this.addCallback((latest) => otherSource.setData(latest)); + const self = this; + otherSource.addCallback((latest) => self.setData(latest)); + if(this.data === undefined){ + this.setData(otherSource.data); + }else{ + otherSource.setData(this.data); + } + } } \ No newline at end of file diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts index bb704eb..9681586 100644 --- a/UI/UserBadge.ts +++ b/UI/UserBadge.ts @@ -5,6 +5,7 @@ import {Basemap} from "../Logic/Basemap"; import L from "leaflet"; import {FixedUiElement} from "./Base/FixedUiElement"; import {VariableUiElement} from "./Base/VariableUIElement"; +import Translations from "./i18n/Translations"; /** * Handles and updates the user badge @@ -15,13 +16,15 @@ export class UserBadge extends UIElement { private _logout: UIElement; private _basemap: Basemap; private _homeButton: UIElement; + private _languagePicker: UIElement; constructor(userDetails: UIEventSource<UserDetails>, pendingChanges: UIElement, + languagePicker: UIElement, basemap: Basemap) { super(userDetails); - + this._languagePicker = languagePicker; this._userDetails = userDetails; this._pendingChanges = pendingChanges; this._basemap = basemap; @@ -61,7 +64,7 @@ export class UserBadge extends UIElement { InnerRender(): string { const user = this._userDetails.data; if (!user.loggedIn) { - return "<div class='activate-osm-authentication'>Login with OpenStreetMap</div>"; + return "<div class='activate-osm-authentication'>" + Translations.general.loginWithOpenStreetMap.R()+ "</div>"; } @@ -114,6 +117,7 @@ export class UserBadge extends UIElement { " <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='small-userbadge-icon' src='./assets/star.svg' alt='star'/> " + user.csCount + "</a></span> " + this._logout.Render() + + this._languagePicker.Render() + this._pendingChanges.Render() + "</p>" + diff --git a/UI/i18n/Locale.ts b/UI/i18n/Locale.ts index 52c45bb..52e0d78 100644 --- a/UI/i18n/Locale.ts +++ b/UI/i18n/Locale.ts @@ -1,18 +1,24 @@ -import { UIEventSource } from "../UIEventSource"; +import {UIEventSource} from "../UIEventSource"; +import {OsmConnection} from "../../Logic/OsmConnection"; -const LANGUAGE_KEY = 'language' - export default class Locale { - public static language: UIEventSource<string> = new UIEventSource(Locale.getInitialLanguage()) - - public static init() { - Locale.language.addCallback(data => { - localStorage.setItem(LANGUAGE_KEY, data) - }) - } + public static language: UIEventSource<string> = Locale.getInitialLanguage() private static getInitialLanguage() { - return localStorage.getItem(LANGUAGE_KEY) + // The key to save in local storage + const LANGUAGE_KEY = 'language' + + const lng = new UIEventSource("en"); + const saved = localStorage.getItem(LANGUAGE_KEY); + lng.setData(saved); + + + lng.addCallback(data => { + console.log("Selected language", data); + localStorage.setItem(LANGUAGE_KEY, data) + }); + + return lng; } } diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 4b53b17..b79746c 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -1,16 +1,30 @@ import { UIElement } from "../UIElement" import Locale from "./Locale" +import {FixedUiElement} from "../Base/FixedUiElement"; -export default class Translation extends UIElement{ - protected InnerRender(): string { - return this.translations[Locale.language.data] - } +export default class Translation extends UIElement { public readonly translations: object - + constructor(translations: object) { super(Locale.language) this.translations = translations } + + public R(): string { + return new Translation(this.translations).Render(); + } + + InnerRender(): string { + const txt = this.translations[Locale.language.data]; + if (txt !== undefined) { + return txt; + } + const en = this.translations["en"]; + console.warn("No translation for language ", Locale.language.data, "for",en); + return en; + } + + } diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index edf0561..8536c01 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -1,18 +1,42 @@ import Translation from "./Translation"; +import {UIElement} from "../UIElement"; +import {FixedUiElement} from "../Base/FixedUiElement"; export default class Translations { - static t = { - cylofix: { - title: new Translation({en: 'Cyclofix bicycle infrastructure', nl: 'Cyclofix fietsinfrastructuur', fr: 'TODO: FRENCH TRANSLATION'}), - description: new Translation({ - en: "On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + - "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.", - nl: "Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + - "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.", - fr: "Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + - "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins." - }) - } + static cylofix = { + title: new Translation({ + en: 'Cyclofix bicycle infrastructure', + nl: 'Cyclofix fietsinfrastructuur', + fr: 'TODO: FRENCH TRANSLATION' + }), + description: new Translation({ + en: "On this map we want to collect data about the whereabouts of bicycle pumps and public racks in Brussels." + + "As a result, cyclists will be able to quickly find the nearest infrastructure for their needs.", + nl: "Op deze kaart willen we gegevens verzamelen over de locatie van fietspompen en openbare stelplaatsen in Brussel." + + "Hierdoor kunnen fietsers snel de dichtstbijzijnde infrastructuur vinden die voldoet aan hun behoeften.", + fr: "Sur cette carte, nous voulons collecter des données sur la localisation des pompes à vélo et des supports publics à Bruxelles." + + "Les cyclistes pourront ainsi trouver rapidement l'infrastructure la plus proche de leurs besoins." + }) + }; + static general = { + loginWithOpenStreetMap: new Translation({ + en: "Click here to login with OpenStreetMap", + nl: "Klik hier op je aan te melden met OpenStreetMap" + }), + uploadAPicture: new Translation({ + en: "Add a picture", + nl: "Voeg een foto toe" + + }) } + + public static W(s: string | UIElement): + UIElement { + if (s instanceof UIElement) { + return s; + } + return new FixedUiElement(s); + } + } diff --git a/index.css b/index.css index d4448b8..b79d1c8 100644 --- a/index.css +++ b/index.css @@ -307,23 +307,26 @@ form { } } - #to-the-map { + position: relative; +} + +#to-the-map h2{ + position: absolute; height: 4em; + padding: 0.5em; margin: 0; - position: absolute; - bottom: 0; - right: 0; - padding-right: 2em; + padding-top: 1em; + text-align: center; width: 100%; - text-align: right; color: white; background-color: #7ebc6f; cursor: pointer; + } diff --git a/index.html b/index.html index bcdeca3..b48ae01 100644 --- a/index.html +++ b/index.html @@ -28,17 +28,13 @@ Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is blocking it. </div> + <div id="language-select"></div> <br/> <div id="searchbox"></div> </div> <br/> <div id="messagesbox-wrapper"> <div id="collapseButton"></div> - <select id="language-select"> - <option>EN</option> - <option>NL</option> - <option>FR</option> - </select> <div id="messagesbox"></div> </div> </div> diff --git a/index.ts b/index.ts index c1fd116..acf78fe 100644 --- a/index.ts +++ b/index.ts @@ -25,7 +25,10 @@ import {All} from "./Customizations/Layouts/All"; import Translations from "./UI/i18n/Translations"; import Translation from "./UI/i18n/Translation"; import Locale from "./UI/i18n/Locale"; -import { Layout } from "./Customizations/Layout"; +import {Layout} from "./Customizations/Layout"; +import {DropDown} from "./UI/Input/DropDown"; +import {FixedInputElement} from "./UI/Input/FixedInputElement"; +import {FixedUiElement} from "./UI/Base/FixedUiElement"; // --------------------- Read the URL parameters ----------------- @@ -90,22 +93,29 @@ if (paramDict.test) { const layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout]; console.log("Using layout: ", layoutToUse.name); -document.title = layoutToUse.title.Render(); +document.title = layoutToUse.title.InnerRender(); Locale.language.addCallback(e => { - document.title = layoutToUse.title.Render(); + document.title = layoutToUse.title.InnerRender(); }) // ----------------- Setup a few event sources ------------- +// const LanguageSelect = document.getElementById('language-select') as HTMLOptionElement +// eLanguageSelect.addEventListener('selectionchange') + + // The message that should be shown at the center of the screen const centerMessage = new UIEventSource<string>(""); // The countdown: if set to e.g. ten, it'll start counting down. When reaching zero, changes will be saved. NB: this is implemented later, not in the eventSource const secondsTillChangesAreSaved = new UIEventSource<number>(0); -const leftMessage = new UIEventSource<() => UIElement>(undefined); +// const leftMessage = new UIEventSource<() => UIElement>(undefined); + +// This message is shown full screen on mobile devices +const fullScreenMessage = new UIEventSource<UIElement>(undefined); const selectedElement = new UIEventSource<any>(undefined); @@ -119,9 +129,18 @@ const locationControl = new UIEventSource<{ lat: number, lon: number, zoom: numb // ----------------- Prepare the important objects ----------------- +const osmConnection = new OsmConnection(dryRun); + + +Locale.language.syncWith(osmConnection.GetPreference("language")); + +window.setLanguage = function (language: string) { + Locale.language.setData(language) +} + + const saveTimeout = 30000; // After this many milliseconds without changes, saves are sent of to OSM const allElements = new ElementStorage(); -const osmConnection = new OsmConnection(dryRun); const changes = new Changes( "Beantwoorden van vragen met #MapComplete voor vragenset #" + layoutToUse.name, osmConnection, allElements); @@ -191,8 +210,13 @@ const layerUpdater = new LayerUpdater(bm, minZoom, flayers); // ------------------ Setup various UI elements ------------ +let languagePicker = new DropDown(" ", layoutToUse.supportedLanguages.map(lang => { + return {value: lang, shown: lang} + } +), Locale.language).AttachTo("language-select"); -new StrayClickHandler(bm, selectedElement, leftMessage, () => { + +new StrayClickHandler(bm, selectedElement, fullScreenMessage, () => { return new SimpleAddUI(bm.Location, bm.LastClickLocation, changes, @@ -204,7 +228,7 @@ new StrayClickHandler(bm, selectedElement, leftMessage, () => { ); /** - * Show the questions and information for the selected element on the leftMessage + * Show the questions and information for the selected element on the fullScreen */ selectedElement.addCallback((data) => { // Which is the applicable set? @@ -213,14 +237,16 @@ selectedElement.addCallback((data) => { const applicable = layer.overpassFilter.matches(TagUtils.proprtiesToKV(data)); if (applicable) { // This layer is the layer that gives the questions - leftMessage.setData(() => - new FeatureInfoBox( - allElements.getElement(data.id), - layer.title, - layer.elementsToShow, - changes, - osmConnection.userDetails - )); + + const featureBox = new FeatureInfoBox( + allElements.getElement(data.id), + layer.title, + layer.elementsToShow, + changes, + osmConnection.userDetails + ); + + fullScreenMessage.setData(featureBox); break; } } @@ -231,7 +257,10 @@ selectedElement.addCallback((data) => { const pendingChanges = new PendingChanges( changes, secondsTillChangesAreSaved,); -new UserBadge(osmConnection.userDetails, pendingChanges, bm) +new UserBadge(osmConnection.userDetails, + pendingChanges, + new FixedUiElement(""), + bm) .AttachTo('userbadge'); new SearchAndGo(bm).AttachTo("searchbox"); @@ -239,7 +268,7 @@ new SearchAndGo(bm).AttachTo("searchbox"); new CollapseButton("messagesbox") .AttachTo("collapseButton"); -var welcomeMessage = () => { +var generateWelcomeMessage = () => { return new VariableUiElement( osmConnection.userDetails.map((userdetails) => { var login = layoutToUse.gettingStartedPlzLogin; @@ -254,11 +283,11 @@ var welcomeMessage = () => { osmConnection.registerActivateOsmAUthenticationClass() }); } -leftMessage.setData(welcomeMessage); -welcomeMessage().AttachTo("messagesbox"); +generateWelcomeMessage().AttachTo("messagesbox"); +fullScreenMessage.setData(generateWelcomeMessage()); -var messageBox = new MessageBoxHandler(leftMessage, () => { +var messageBox = new MessageBoxHandler(fullScreenMessage, () => { selectedElement.setData(undefined) }); @@ -286,13 +315,3 @@ locationControl.ping(); messageBox.update(); -// --- Locale --- - -Locale.init() - -window.setLanguage = function(language:string) { - Locale.language.setData(language) -} - -// const eLanguageSelect = document.getElementById('language-select') as HTMLOptionElement -// eLanguageSelect.addEventListener('selectionchange') diff --git a/test.ts b/test.ts index e4f2d19..b2a2271 100644 --- a/test.ts +++ b/test.ts @@ -1 +1,9 @@ -console.log("Hello world") \ No newline at end of file +import {DropDown} from "./UI/Input/DropDown"; +import Locale from "./UI/i18n/Locale"; + +console.log("Hello world") + +let languagePicker = new DropDown("", ["en", "nl"].map(lang => { + return {value: lang, shown: lang} + } +), Locale.language).AttachTo("maindiv"); \ No newline at end of file From 7e768c1472709a7150dc622bc749bd4b4b44afe8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 00:38:03 +0200 Subject: [PATCH 23/29] More translations! --- Customizations/TagRendering.ts | 7 +++-- UI/ImageUploadFlow.ts | 11 ++++---- UI/Input/DropDown.ts | 14 ++++++---- UI/Input/TextField.ts | 9 ++---- UI/SearchAndGo.ts | 18 +++++++----- UI/i18n/Translations.ts | 50 ++++++++++++++++++++++++++++++---- 6 files changed, 77 insertions(+), 32 deletions(-) diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 67b2b28..6c8eb39 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -463,7 +463,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { return "<div class='question'>" + "<span class='question-text'>" + question + "</span>" + - (question !== "" ? "<br/>" : "") + + (this._question.IsEmpty() ? "" : "<br/>") + "<div>" + this._questionElement.Render() + "</div>" + this._skipButton.Render() + this._saveButton.Render() + @@ -471,10 +471,11 @@ class TagRendering extends UIElement implements TagDependantUIElement { } if (this.IsKnown()) { - const html = this.RenderAnwser().Render(); - if (html == "") { + const answer = this.RenderAnwser() + if (answer.IsEmpty()) { return ""; } + const html = answer.Render(); let editButton = ""; if (this._userDetails.data.loggedIn && this._question !== undefined) { editButton = this._editButton.Render(); diff --git a/UI/ImageUploadFlow.ts b/UI/ImageUploadFlow.ts index 1a628ad..8cf558b 100644 --- a/UI/ImageUploadFlow.ts +++ b/UI/ImageUploadFlow.ts @@ -31,11 +31,12 @@ export class ImageUploadFlow extends UIElement { this._uploadOptions = uploadOptions; this.ListenTo(this._isUploading); - const licensePicker = new DropDown("The picture is published ", + const licensePicker = new DropDown( + Translations.general.picture.licenseIntro, [ - {value: "CC0", shown: "in the public domain"}, - {value: "CC-BY-SA 4.0", shown: "with a CC-BY-SA license"}, - {value: "CC-BY 4.0", shown: "with a CC-BY license"} + {value: "CC0", shown: Translations.general.picture.publicDomain}, + {value: "CC-BY-SA 4.0", shown:Translations.general.picture.ccbysa}, + {value: "CC-BY 4.0", shown:Translations.general.picture.ccby} ], preferedLicense ); @@ -67,7 +68,7 @@ export class ImageUploadFlow extends UIElement { "<div class='imageflow-file-input-wrapper'>" + "<img src='./assets/camera-plus.svg' alt='upload image'/> " + - "<span class='imageflow-add-picture'>"+Translations.general.uploadAPicture.R()+"</span>" + + "<span class='imageflow-add-picture'>"+Translations.general.picture.uploadAPicture.R()+"</span>" + "<div class='break'></div>" + "</div>" + diff --git a/UI/Input/DropDown.ts b/UI/Input/DropDown.ts index dfbcafd..fc7012a 100644 --- a/UI/Input/DropDown.ts +++ b/UI/Input/DropDown.ts @@ -3,27 +3,31 @@ import {UIElement} from "../UIElement"; import {InputElement} from "./InputElement"; import instantiate = WebAssembly.instantiate; import {FixedUiElement} from "../Base/FixedUiElement"; +import Translations from "../i18n/Translations"; export class DropDown<T> extends InputElement<T> { - private readonly _label: string; + private readonly _label: UIElement; private readonly _values: { value: T; shown: UIElement }[]; private readonly _value; - constructor(label: string, + constructor(label: string | UIElement, values: { value: T, shown: string | UIElement }[], value: UIEventSource<T> = undefined) { super(undefined); this._value = value ?? new UIEventSource<T>(undefined); - this._label = label; + this._label = Translations.W(label); this._values = values.map(v => { return { value: v.value, - shown: v.shown instanceof UIElement ? v.shown : new FixedUiElement(v.shown) + shown: Translations.W(v.shown) } } ); + for (const v of this._values) { + this.ListenTo(v.shown._source); + } this.ListenTo(this._value) } @@ -60,7 +64,7 @@ export class DropDown<T> extends InputElement<T> { } return "<form>" + - "<label for='dropdown-" + this.id + "'>" + this._label + "</label>" + + "<label for='dropdown-" + this.id + "'>" + this._label.Render() + "</label>" + "<select name='dropdown-" + this.id + "' id='dropdown-" + this.id + "'>" + options + "</select>" + diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 44a816b..f025b28 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -2,6 +2,7 @@ import {UIElement} from "../UIElement"; import {UIEventSource} from "../UIEventSource"; import {InputElement} from "./InputElement"; import {FixedUiElement} from "../Base/FixedUiElement"; +import Translations from "../i18n/Translations"; export class TextField<T> extends InputElement<T> { @@ -35,12 +36,8 @@ export class TextField<T> extends InputElement<T> { this.mappedValue.addCallback((t) => this.value.setData(options.toString(t))); - options.placeholder = options.placeholder ?? ""; - if (options.placeholder instanceof UIElement) { - this._placeholder = options.placeholder - } else { - this._placeholder = new FixedUiElement(options.placeholder); - } + this._placeholder = Translations.W(options.placeholder ?? ""); + this.ListenTo(this._placeholder._source); this._toString = options.toString ?? ((t) => ("" + t)); diff --git a/UI/SearchAndGo.ts b/UI/SearchAndGo.ts index 679764e..d5fb275 100644 --- a/UI/SearchAndGo.ts +++ b/UI/SearchAndGo.ts @@ -5,13 +5,18 @@ import {FixedUiElement} from "./Base/FixedUiElement"; import {Geocoding} from "../Logic/Geocoding"; import {Basemap} from "../Logic/Basemap"; import {VariableUiElement} from "./Base/VariableUIElement"; +import Translation from "./i18n/Translation"; +import Locale from "./i18n/Locale"; +import Translations from "./i18n/Translations"; export class SearchAndGo extends UIElement { - private _placeholder = new UIEventSource("Search a location...") + private _placeholder = new UIEventSource<Translation>(Translations.general.search.search) private _searchField = new TextField<string>({ - placeholder: new VariableUiElement(this._placeholder), + placeholder: new VariableUiElement( + this._placeholder.map(uiElement => uiElement.InnerRender(), [Locale.language]) + ), fromString: str => str, toString: str => str } @@ -41,12 +46,12 @@ export class SearchAndGo extends UIElement { private RunSearch() { const searchString = this._searchField.GetValue().data; this._searchField.Clear(); - this._placeholder.setData("Searching..."); + this._placeholder.setData(Translations.general.search.searching); const self = this; Geocoding.Search(searchString, this._map, (result) => { if (result.length == 0) { - this._placeholder.setData("Niets gevonden"); + this._placeholder.setData(Translations.general.search.nothing); return; } @@ -56,16 +61,15 @@ export class SearchAndGo extends UIElement { [bb[1], bb[3]] ] self._map.map.fitBounds(bounds); - this._placeholder.setData("Search a location..."); + this._placeholder.setData(Translations.general.search.search); }, () => { - this._placeholder.setData("Something went wrong. Try again."); + this._placeholder.setData(Translations.general.search.error); }); } InnerRender(): string { - // "<img class='search' src='./assets/search.svg' alt='Search'> " + return this._searchField.Render() + this._goButton.Render(); diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index 8536c01..c793f0f 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -24,15 +24,53 @@ export default class Translations { en: "Click here to login with OpenStreetMap", nl: "Klik hier op je aan te melden met OpenStreetMap" }), - uploadAPicture: new Translation({ - en: "Add a picture", - nl: "Voeg een foto toe" - }) + search: { + search: new Translation({ + en: "Search a location", + nl: "Zoek naar een locatie" + }), + searching: new Translation({ + en: "Searching...", + nl: "Aan het zoeken..." + }), + nothing: new Translation({ + en: "Nothing found...", + nl: "Niet gevonden..." + }), + error: new Translation({ + en: "Something went wrong...", + nl: "Niet gelukt..." + }) + + }, + + picture: { + uploadAPicture: new Translation({ + en: "Add a picture", + nl: "Voeg een foto toe" + + }), + licenseIntro: new Translation({ + en: "Your picture is published", + nl: "Je foto wordt gepubliceerd" + }), + publicDomain: new Translation({ + en: "in the public domain", + nl: "in het publiek domein" + }), + ccby: new Translation({ + en: "with a CC-BY license", + nl: "met een CC-BY licentie" + }), + ccbysa: new Translation({ + en: "with a CC-BY-SA license", + nl: "met een CC-BY-SA licentie" + }) + } } - public static W(s: string | UIElement): - UIElement { + public static W(s: string | UIElement): UIElement { if (s instanceof UIElement) { return s; } From fd6f77c98e58085cd8d8642319e3539d521cf9b8 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 01:37:48 +0200 Subject: [PATCH 24/29] Playing around with translatiosn --- Customizations/Layout.ts | 26 ++++++++++--------- Customizations/Layouts/Cyclofix.ts | 12 ++++++--- .../Questions/bike/StationOperator.ts | 4 +-- UI/Base/Combine.ts | 19 ++++++++++++++ UI/UIElement.ts | 7 ++++- index.css | 2 +- index.ts | 20 +++----------- 7 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 UI/Base/Combine.ts diff --git a/Customizations/Layout.ts b/Customizations/Layout.ts index a883bd8..4645c4d 100644 --- a/Customizations/Layout.ts +++ b/Customizations/Layout.ts @@ -1,6 +1,8 @@ import {LayerDefinition} from "./LayerDefinition"; import { UIElement } from "../UI/UIElement"; -import { FixedUiElement } from "../UI/Base/FixedUiElement"; +import {FixedUiElement} from "../UI/Base/FixedUiElement"; +import Translation from "../UI/i18n/Translation"; +import Translations from "../UI/i18n/Translations"; /** * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). @@ -10,14 +12,14 @@ export class Layout { public title: UIElement; public layers: LayerDefinition[]; public welcomeMessage: UIElement; - public gettingStartedPlzLogin: string; - public welcomeBackMessage: string; + public gettingStartedPlzLogin: UIElement; + public welcomeBackMessage: UIElement; + public welcomeTail: UIElement; public startzoom: number; public supportedLanguages: string[]; public startLon: number; public startLat: number; - public welcomeTail: string; public locationContains: string[]; @@ -43,21 +45,21 @@ export class Layout { startLat: number, startLon: number, welcomeMessage: UIElement | string, - gettingStartedPlzLogin: string = "Please login to get started", - welcomeBackMessage: string = "You are logged in. Welcome back!", - welcomeTail: string = "" + gettingStartedPlzLogin: UIElement | string = "Please login to get started", + welcomeBackMessage: UIElement | string = "You are logged in. Welcome back!", + welcomeTail: UIElement | string = "" ) { this.supportedLanguages = supportedLanguages; - this.title = typeof(title) === 'string' ? new FixedUiElement(title) : title; + this.title = typeof (title) === 'string' ? new FixedUiElement(title) : title; this.startLon = startLon; this.startLat = startLat; this.startzoom = startzoom; this.name = name; this.layers = layers; - this.welcomeMessage = typeof(welcomeMessage) === 'string' ? new FixedUiElement(welcomeMessage) : welcomeMessage; - this.gettingStartedPlzLogin = gettingStartedPlzLogin; - this.welcomeBackMessage = welcomeBackMessage; - this.welcomeTail = welcomeTail; + this.welcomeMessage =Translations.W(welcomeMessage) + this.gettingStartedPlzLogin = Translations.W(gettingStartedPlzLogin); + this.welcomeBackMessage = Translations.W(welcomeBackMessage); + this.welcomeTail = Translations.W(welcomeTail); } } diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 86edf04..32a7ad5 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -6,6 +6,7 @@ import {GhostBike} from "../Layers/GhostBike"; import Translations from "../../UI/i18n/Translations"; import {DrinkingWater} from "../Layers/DrinkingWater"; import {BikeShop} from "../Layers/BikeShop" +import Combine from "../../UI/Base/Combine"; export default class Cyclofix extends Layout { @@ -18,10 +19,13 @@ export default class Cyclofix extends Layout { 16, 50.8465573, 4.3516970, - "<h3>" + Translations.t.cyclofix.title.Render() + "</h3>\n" + - "\n" + - `<p>${Translations.t.cyclofix.description.Render()}</p>` - , + new Combine([ + "<h3>", + Translations.t.cyclofix.title, + "</h3><br/><p>", + Translations.t.cyclofix.description, + "</p>" + ]), "", ""); } } diff --git a/Customizations/Questions/bike/StationOperator.ts b/Customizations/Questions/bike/StationOperator.ts index 5d09434..94f1efb 100644 --- a/Customizations/Questions/bike/StationOperator.ts +++ b/Customizations/Questions/bike/StationOperator.ts @@ -18,8 +18,8 @@ export default class BikeStationOperator extends TagRenderingOptions { ], freeform: { key: "operator", - template: to.template, - renderTemplate: to.render, + template: to.template.txt, + renderTemplate: to.render.txt, placeholder: "organisatie" } }); diff --git a/UI/Base/Combine.ts b/UI/Base/Combine.ts new file mode 100644 index 0000000..83f1033 --- /dev/null +++ b/UI/Base/Combine.ts @@ -0,0 +1,19 @@ +import {UIElement} from "../UIElement"; +import Translations from "../i18n/Translations"; + +export default class Combine extends UIElement { + private uiElements: UIElement[]; + + constructor(uiElements: (string | UIElement)[]) { + super(undefined); + this.uiElements = uiElements.map(Translations.W); + } + + InnerRender(): string { + let elements = ""; + for (const element of this.uiElements) { + elements += element.Render(); + } + return elements; + } +} \ No newline at end of file diff --git a/UI/UIElement.ts b/UI/UIElement.ts index 8e1ff27..6d7b7fc 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -13,18 +13,23 @@ export abstract class UIElement { this.id = "ui-element-" + UIElement.nextId; this._source = source; UIElement.nextId++; + if (UIElement.nextId % 100 == 0) { + + console.log(UIElement.nextId) + } this.ListenTo(source); } public ListenTo(source: UIEventSource<any>) { if (source === undefined) { - return; + return this; } const self = this; source.addCallback(() => { self.Update(); }) + return this; } private _onClick: () => void; diff --git a/index.css b/index.css index 1131c59..82a2823 100644 --- a/index.css +++ b/index.css @@ -243,7 +243,7 @@ form { cursor: pointer; position: absolute; margin-left: 2em; - margin-top: 0.5em; + margin-top: 3em; } #messagesbox-wrapper { diff --git a/index.ts b/index.ts index 0cdd19d..cdca924 100644 --- a/index.ts +++ b/index.ts @@ -164,7 +164,6 @@ const bm = new Basemap("leafletDiv", locationControl, new VariableUiElement( // ------------- Setup the layers ------------------------------- -const controls = {}; const addButtons: { name: string, icon: string, @@ -194,8 +193,6 @@ for (const layer of layoutToUse.layers) { const flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); - controls[layer.name] = flayer.isDisplayed; - const addButton = { name: layer.name, icon: layer.icon, @@ -272,17 +269,17 @@ new CollapseButton("messagesbox") var generateWelcomeMessage = () => { return new VariableUiElement( osmConnection.userDetails.map((userdetails) => { - var login = layoutToUse.gettingStartedPlzLogin; + var login = layoutToUse.gettingStartedPlzLogin.Render(); if (userdetails.loggedIn) { - login = layoutToUse.welcomeBackMessage; + login = layoutToUse.welcomeBackMessage.Render(); } return "<div id='welcomeMessage'>" + - layoutToUse.welcomeMessage.Render() + login + layoutToUse.welcomeTail + + layoutToUse.welcomeMessage.Render() + login + layoutToUse.welcomeTail.Render() + "</div>"; }), function () { osmConnection.registerActivateOsmAUthenticationClass() - }); + }).ListenTo(Locale.language); } generateWelcomeMessage().AttachTo("messagesbox"); fullScreenMessage.setData(generateWelcomeMessage()); @@ -314,12 +311,3 @@ new GeoLocationHandler(bm).AttachTo("geolocate-button"); locationControl.ping(); messageBox.update(); - -/* -const eLanguageSelect = document.getElementById('language-select') as HTMLOptionElement -eLanguageSelect.addEventListener('input', e => { - // @ts-ignore - const selectedLanguage = e.target.value as string - Locale.language.setData(selectedLanguage.toLowerCase()) -}) -*/ \ No newline at end of file From 0f2dff8f4133a6b11b633e336e16677d6eb1e555 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 02:55:28 +0200 Subject: [PATCH 25/29] Full, interactive i18n (still some quests to enable it though) --- Customizations/LayerDefinition.ts | 2 +- Customizations/Layout.ts | 64 +++++++++++++++++-- Customizations/Layouts/Cyclofix.ts | 5 +- Customizations/Questions/bike/ParkingType.ts | 61 ++++++++++++------ .../Questions/bike/PumpManometer.ts | 8 +-- Customizations/TagRendering.ts | 18 +++--- UI/Base/Combine.ts | 18 +++++- ...dler.ts => FullScreenMessageBoxHandler.ts} | 14 ++-- UI/SaveButton.ts | 5 +- UI/UIElement.ts | 7 +- UI/i18n/Translation.ts | 2 - UI/i18n/Translations.ts | 38 +++++++++-- index.html | 9 ++- index.ts | 38 +++++------ test.ts | 6 +- 15 files changed, 205 insertions(+), 90 deletions(-) rename UI/{MessageBoxHandler.ts => FullScreenMessageBoxHandler.ts} (85%) diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index f294ac4..138102c 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -50,7 +50,7 @@ export class LayerDefinition { /** * This UIElement is rendered as title element in the popup */ - title: TagRenderingOptions | UIElement; + title: TagRenderingOptions; /** * These are the questions/shown attributes in the popup */ diff --git a/Customizations/Layout.ts b/Customizations/Layout.ts index 4645c4d..eb13be3 100644 --- a/Customizations/Layout.ts +++ b/Customizations/Layout.ts @@ -1,13 +1,18 @@ import {LayerDefinition} from "./LayerDefinition"; -import { UIElement } from "../UI/UIElement"; +import {UIElement} from "../UI/UIElement"; import {FixedUiElement} from "../UI/Base/FixedUiElement"; import Translation from "../UI/i18n/Translation"; import Translations from "../UI/i18n/Translations"; +import Locale from "../UI/i18n/Locale"; +import {VariableUiElement} from "../UI/Base/VariableUIElement"; +import {OsmConnection, UserDetails} from "../Logic/OsmConnection"; +import {UIEventSource} from "../UI/UIEventSource"; /** * A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers). */ export class Layout { + public name: string; public title: UIElement; public layers: LayerDefinition[]; @@ -45,8 +50,8 @@ export class Layout { startLat: number, startLon: number, welcomeMessage: UIElement | string, - gettingStartedPlzLogin: UIElement | string = "Please login to get started", - welcomeBackMessage: UIElement | string = "You are logged in. Welcome back!", + gettingStartedPlzLogin: UIElement | string = Translations.t.general.getStarted, + welcomeBackMessage: UIElement | string = Translations.t.general.welcomeBack, welcomeTail: UIElement | string = "" ) { this.supportedLanguages = supportedLanguages; @@ -56,11 +61,62 @@ export class Layout { this.startzoom = startzoom; this.name = name; this.layers = layers; - this.welcomeMessage =Translations.W(welcomeMessage) + this.welcomeMessage = Translations.W(welcomeMessage) this.gettingStartedPlzLogin = Translations.W(gettingStartedPlzLogin); this.welcomeBackMessage = Translations.W(welcomeBackMessage); this.welcomeTail = Translations.W(welcomeTail); } + +} + +export class WelcomeMessage extends UIElement { + private readonly layout: Layout; + private readonly userDetails: UIEventSource<UserDetails>; + private osmConnection: OsmConnection; + + private readonly description: UIElement; + private readonly plzLogIn: UIElement; + private readonly welcomeBack: UIElement; + private readonly tail: UIElement; + + + constructor(layout: Layout, osmConnection: OsmConnection) { + super(osmConnection.userDetails); + this.ListenTo(Locale.language); + this.osmConnection = osmConnection; + this.layout = layout; + this.userDetails = osmConnection.userDetails; + + this.description = layout.welcomeMessage; + console.log(" >>>>",this.description, "DESCR ") + this.plzLogIn = layout.gettingStartedPlzLogin; + this.welcomeBack = layout.welcomeBackMessage; + this.tail = layout.welcomeTail; + } + + InnerRender(): string { + return "<div id='welcomeMessage'>" + + this.description.Render() + + "<br/>"+ + (this.userDetails.data.loggedIn ? this.welcomeBack : this.plzLogIn).Render() + + "<br/>"+ + this.tail.Render() + + "</div>" + + ; + /* + return new VariableUiElement( + this.userDetails.map((userdetails) => { + }), + function () { + + }).ListenTo(Locale.language);*/ + } + + protected InnerUpdate(htmlElement: HTMLElement) { + this.osmConnection.registerActivateOsmAUthenticationClass() + } + } diff --git a/Customizations/Layouts/Cyclofix.ts b/Customizations/Layouts/Cyclofix.ts index 32a7ad5..87753c0 100644 --- a/Customizations/Layouts/Cyclofix.ts +++ b/Customizations/Layouts/Cyclofix.ts @@ -19,13 +19,14 @@ export default class Cyclofix extends Layout { 16, 50.8465573, 4.3516970, + /* Translations.t.cyclofix.title/*/ new Combine([ "<h3>", Translations.t.cyclofix.title, "</h3><br/><p>", Translations.t.cyclofix.description, "</p>" - ]), - "", ""); + ])//*/ + ); } } diff --git a/Customizations/Questions/bike/ParkingType.ts b/Customizations/Questions/bike/ParkingType.ts index 5b1cbf8..fa8ab50 100644 --- a/Customizations/Questions/bike/ParkingType.ts +++ b/Customizations/Questions/bike/ParkingType.ts @@ -1,38 +1,61 @@ import {TagRenderingOptions} from "../../TagRendering"; import {Tag} from "../../../Logic/TagsFilter"; import Translations from "../../../UI/i18n/Translations"; +import Combine from "../../../UI/Base/Combine"; +class ParkingTypeHelper { + static GenerateMappings() { + const images = { + stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", + wall_loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", + handlebar_holder: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", + shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", + rack: "https://wiki.openstreetmap.org/w/images/thumb/4/41/Triton_Bike_Rack.png/100px-Triton_Bike_Rack.png", + "two-tier": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" + }; + + + const toImg = (url) => `<img src=${url}>` + const mappings = []; + const to = Translations.t.cyclofix.parking.type + + for (const imagesKey in images) { + const mapping = + { + k: new Tag("bicycle_parking", imagesKey), + txt: new Combine([ + to[imagesKey], + to.eg, + toImg(images[imagesKey]) + ]) + }; + + mappings.push(mapping); + + } + + return mappings; + } +} export default class ParkingType extends TagRenderingOptions { constructor() { - const images = { - stands: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dc/Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg/100px-Bike_racks_at_north-west_of_Westfield_-_geograph.org.uk_-_1041057.jpg", - loops: "https://wiki.openstreetmap.org/w/images/thumb/c/c2/Bike-parking-wheelbender.jpg/100px-Bike-parking-wheelbender.jpg", - handlebar: "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Bicycle_parking_handlebar_holder.jpg/100px-Bicycle_parking_handlebar_holder.jpg", - shed: "https://wiki.openstreetmap.org/w/images/thumb/b/b2/Bike-shelter.jpg/100px-Bike-shelter.jpg", - rack: "https://wiki.openstreetmap.org/w/images/thumb/4/41/Triton_Bike_Rack.png/100px-Triton_Bike_Rack.png", - double: "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG/100px-Bicis_a_l%27estaci%C3%B3_de_Leiden.JPG" - } - const toImg = (url) => `<img src=${url}>` + const to = Translations.t.cyclofix.parking.type + + super({ priority: 5, - question: to.question.Render(), + question: to.question, freeform: { key: "bicycle_parking", extraTags: new Tag("fixme", "Freeform bicycle_parking= tag used: possibly a wrong value"), template: to.template.txt, renderTemplate: to.render.txt, - placeholder: Translations.t.cyclofix.freeFormPlaceholder.txt, + placeholder: Translations.t.cyclofix.freeFormPlaceholder, }, - mappings: [ - {k: new Tag("bicycle_parking", "stands"), txt: `${to.stands.Render()}, bijvoorbeeld: ${toImg(images.stands)}`}, - {k: new Tag("bicycle_parking", "wall_loops"), txt: `${to.loops.Render()}, bijvoorbeeld: ${toImg(images.loops)}`}, - {k: new Tag("bicycle_parking", "handlebar_holder"), txt: `${to.handlebar.Render()}, bijvoorbeeld: ${toImg(images.handlebar)}`}, - {k: new Tag("bicycle_parking", "shed"), txt: `${to.shed.Render()}, bijvoorbeeld: ${toImg(images.shed)}`}, - {k: new Tag("bicycle_parking", "rack"), txt: `${to.rack.Render()}, bijvoorbeeld: ${toImg(images.rack)}`}, - {k: new Tag("bicycle_parking", "two-tier"), txt: `${to.double.Render()}, bijvoorbeeld: ${toImg(images.double)}`} - ] + mappings: ParkingTypeHelper.GenerateMappings() + }); } } diff --git a/Customizations/Questions/bike/PumpManometer.ts b/Customizations/Questions/bike/PumpManometer.ts index 83380b1..1ae416e 100644 --- a/Customizations/Questions/bike/PumpManometer.ts +++ b/Customizations/Questions/bike/PumpManometer.ts @@ -7,11 +7,11 @@ export default class PumpManometer extends TagRenderingOptions { constructor() { const to = Translations.t.cyclofix.station.manometer super({ - question: to.question.Render(), + question: to.question, mappings: [ - {k: new Tag("manometer", "yes"), txt: to.yes.Render()}, - {k: new Tag("manometer", "no"), txt: to.no.Render()}, - {k: new Tag("manometer", "broken"), txt: to.broken.Render()} + {k: new Tag("manometer", "yes"), txt: to.yes}, + {k: new Tag("manometer", "no"), txt: to.no}, + {k: new Tag("manometer", "broken"), txt: to.broken} ] }); } diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 6c8eb39..dad40bf 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -14,6 +14,7 @@ import {InputElementWrapper} from "../UI/Input/InputElementWrapper"; import {FixedInputElement} from "../UI/Input/FixedInputElement"; import {RadioButton} from "../UI/Input/RadioButton"; import Translations from "../UI/i18n/Translations"; +import Locale from "../UI/i18n/Locale"; export class TagRenderingOptions implements TagDependantUIElementConstructor { @@ -29,7 +30,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { tagsPreprocessor?: (tags: any) => any; template: string; renderTemplate: string; - placeholder?: string; + placeholder?: string | UIElement; extraTags?: TagsFilter }; mappings?: { k: TagsFilter; txt: string | UIElement; priority?: number, substitute?: boolean }[] @@ -78,7 +79,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor { freeform?: { key: string, template: string, renderTemplate: string - placeholder?: string, + placeholder?: string | UIElement, extraTags?: TagsFilter, }, @@ -148,7 +149,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { key: string, template: string, renderTemplate: string, - placeholder?: string, + placeholder?: string | UIElement, extraTags?: TagsFilter }; @@ -172,13 +173,14 @@ class TagRendering extends UIElement implements TagDependantUIElement { freeform?: { key: string, template: string, renderTemplate: string - placeholder?: string, + placeholder?: string | UIElement, extraTags?: TagsFilter, }, tagsPreprocessor?: ((tags: any) => any), mappings?: { k: TagsFilter, txt: string | UIElement, priority?: number, substitute?: boolean }[] }) { super(tags); + this.ListenTo(Locale.language); const self = this; this.ListenTo(this._questionSkipped); this.ListenTo(this._editMode); @@ -264,13 +266,13 @@ class TagRendering extends UIElement implements TagDependantUIElement { const cancelContents = this._editMode.map((isEditing) => { if (isEditing) { - return "<span class='skip-button'>Cancel</span>"; + return "<span class='skip-button'>"+Translations.t.general.cancel.R()+"</span>"; } else { - return "<span class='skip-button'>Skip this question</span>"; + return "<span class='skip-button'>"+Translations.t.general.skip.R()+"</span>"; } }); // And at last, set up the skip button - this._skipButton = new VariableUiElement(cancelContents).onClick(cancel); + this._skipButton = new VariableUiElement(cancelContents).onClick(cancel) ; } @@ -278,7 +280,7 @@ class TagRendering extends UIElement implements TagDependantUIElement { freeform?: { key: string, template: string, renderTemplate: string - placeholder?: string, + placeholder?: string | UIElement, extraTags?: TagsFilter, }, mappings?: { k: TagsFilter, txt: string | UIElement, priority?: number, substitute?: boolean }[] diff --git a/UI/Base/Combine.ts b/UI/Base/Combine.ts index 83f1033..12b4338 100644 --- a/UI/Base/Combine.ts +++ b/UI/Base/Combine.ts @@ -2,18 +2,30 @@ import {UIElement} from "../UIElement"; import Translations from "../i18n/Translations"; export default class Combine extends UIElement { - private uiElements: UIElement[]; + private uiElements: (string | UIElement)[]; constructor(uiElements: (string | UIElement)[]) { super(undefined); - this.uiElements = uiElements.map(Translations.W); + this.uiElements = uiElements; } InnerRender(): string { let elements = ""; for (const element of this.uiElements) { - elements += element.Render(); + if (element instanceof UIElement) { + elements += element.Render(); + } else { + elements += element; + } } return elements; } + + protected InnerUpdate(htmlElement: HTMLElement) { + for (const element of this.uiElements) { + if (element instanceof UIElement) { + element.Update(); + } + } + } } \ No newline at end of file diff --git a/UI/MessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts similarity index 85% rename from UI/MessageBoxHandler.ts rename to UI/FullScreenMessageBoxHandler.ts index a06a25a..88f6871 100644 --- a/UI/MessageBoxHandler.ts +++ b/UI/FullScreenMessageBoxHandler.ts @@ -1,11 +1,13 @@ -/** - * Keeps 'messagebox' and 'messageboxmobile' in sync, shows a 'close' button on the latter one - */ import {UIEventSource} from "./UIEventSource"; import {UIElement} from "./UIElement"; import {VariableUiElement} from "./Base/VariableUIElement"; +import Translations from "./i18n/Translations"; -export class MessageBoxHandler { +/** + * Handles the full screen popup on mobile + */ +export class FullScreenMessageBoxHandler { + private _uielement: UIEventSource<UIElement>; constructor(uielement: UIEventSource<UIElement>, @@ -22,13 +24,13 @@ export class MessageBoxHandler { } } - new VariableUiElement(new UIEventSource<string>("<h2>Return to the map</h2>")) + Translations.t.general.returnToTheMap .onClick(() => { console.log("Clicked 'return to the map'") uielement.setData(undefined); onClear(); }) - .AttachTo("to-the-map"); + .AttachTo("to-the-map-h2"); } diff --git a/UI/SaveButton.ts b/UI/SaveButton.ts index 23065ff..a2728c4 100644 --- a/UI/SaveButton.ts +++ b/UI/SaveButton.ts @@ -1,5 +1,6 @@ import {UIEventSource} from "./UIEventSource"; import {UIElement} from "./UIElement"; +import Translations from "./i18n/Translations"; export class SaveButton extends UIElement { private _value: UIEventSource<any>; @@ -17,9 +18,9 @@ export class SaveButton extends UIElement { this._value.data === null || this._value.data === "" ) { - return "<span class='save-non-active'>Opslaan</span>" + return "<span class='save-non-active'>"+Translations.t.general.save.Render()+"</span>" } - return "<span class='save'>Save</span>"; + return "<span class='save'>"+Translations.t.general.save.Render()+"</span>"; } } \ No newline at end of file diff --git a/UI/UIElement.ts b/UI/UIElement.ts index 6d7b7fc..a060e27 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -13,10 +13,6 @@ export abstract class UIElement { this.id = "ui-element-" + UIElement.nextId; this._source = source; UIElement.nextId++; - if (UIElement.nextId % 100 == 0) { - - console.log(UIElement.nextId) - } this.ListenTo(source); } @@ -97,8 +93,7 @@ export abstract class UIElement { AttachTo(divId: string) { let element = document.getElementById(divId); if (element === null) { - console.log("SEVERE: could not attach UIElement to ", divId); - return; + throw "SEVERE: could not attach UIElement to " + divId; } element.innerHTML = this.Render(); this.Update(); diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index c2d3a04..911bec4 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -30,6 +30,4 @@ export default class Translation extends UIElement { return new Translation(this.translations).Render(); } - - } diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index f67f504..79e5cbc 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -36,13 +36,15 @@ export default class Translations { nl: 'Van welk type is deze fietsenparking?', fr: 'TODO: fr' }), + eg: new T({en: ", for example", nl: ", bijvoorbeeld"}), stands: new T({en: 'Staple racks', nl: 'Nietjes', fr: 'TODO: fr'}), - loops: new T({en: 'Wheel rack/loops', nl: 'Wielrek/lussen', fr: 'TODO: fr'}), - handlebar: new T({en: 'Handlebar holder', nl: 'Stuurhouder', fr: 'TODO: fr'}), + wall_loops: new T({en: 'Wheel rack/loops', nl: 'Wielrek/lussen', fr: 'TODO: fr'}), + handlebar_holder: new T({en: 'Handlebar holder', nl: 'Stuurhouder', fr: 'TODO: fr'}), shed: new T({en: 'Shed', nl: 'Schuur', fr: 'TODO: fr'}), rack: new T({en: 'Rack', nl: 'Rek', fr: 'TODO: fr'}), - double: new T({en: 'Two-tiered', nl: 'Dubbel (twee verdiepingen)', fr: 'TODO: fr'}), + "two-tier": new T({en: 'Two-tiered', nl: 'Dubbel (twee verdiepingen)', fr: 'TODO: fr'}), }, + operator: { render: new T({ en: 'This bike parking is operated by {operator}', @@ -293,9 +295,15 @@ export default class Translations { ready: new T({en: 'Done!', nl: 'Klaar!', fr: 'TODO: fr'}), }, general: { - loginWithOpenStreetMap: new T({en: "Login with OpenStreetMap", nl: "Aanmelden met OpenStreetMap"}) - - , + loginWithOpenStreetMap: new T({en: "Login with OpenStreetMap", nl: "Aanmelden met OpenStreetMap"}), + getStarted: new T({ + en: "<span class='activate-osm-authentication'>Login with OpenStreetMap</span> or <a href='https://www.openstreetmap.org/user/new' target='_blank'>make a free account to get started</a>", + nl: "<span class='activate-osm-authentication'>Meld je aan met je OpenStreetMap-account</span> of <a href='https://www.openstreetmap.org/user/new' target='_blank'>maak snel en gratis een account om te beginnen/a>", + }), + welcomeBack: new T({ + en: "You are logged in, welcome back!", + nl: "Je bent aangemeld. Welkom terug!" + }), search: { search: new Translation({ en: "Search a location", @@ -314,7 +322,23 @@ export default class Translations { nl: "Niet gelukt..." }) - } + }, + returnToTheMap: new T({ + en: "Return to the map", + nl: "Naar de kaart" + }), + save: new T({ + en: "Save", + nl: "Opslaan" + }), + cancel: new T({ + en: "Cancel", + nl: "Annuleren" + }), + skip: new T({ + en: "Skip this question", + nl: "Vraag overslaan" + }) } } diff --git a/index.html b/index.html index b48ae01..36e8dc1 100644 --- a/index.html +++ b/index.html @@ -17,9 +17,14 @@ <body> <div id="messagesboxmobilewrapper"> <div id="messagesboxmobile-scroll"> - <div id="messagesboxmobile"> </div> + <div id="messagesboxmobile"></div> + </div> + <div id="to-the-map"> + <h2 id="to-the-map-h2"> + Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is + blocking it. + </h2> </div> - <div id="to-the-map">Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is blocking it.</div> </div> <div id="topleft-tools"> diff --git a/index.ts b/index.ts index cdca924..58a8b2a 100644 --- a/index.ts +++ b/index.ts @@ -11,7 +11,7 @@ import {Tag, TagUtils} from "./Logic/TagsFilter"; import {FilteredLayer} from "./Logic/FilteredLayer"; import {LayerUpdater} from "./Logic/LayerUpdater"; import {UIElement} from "./UI/UIElement"; -import {MessageBoxHandler} from "./UI/MessageBoxHandler"; +import {FullScreenMessageBoxHandler} from "./UI/FullScreenMessageBoxHandler"; import {Overpass} from "./Logic/Overpass"; import {FeatureInfoBox} from "./UI/FeatureInfoBox"; import {GeoLocationHandler} from "./Logic/GeoLocationHandler"; @@ -25,7 +25,7 @@ import {All} from "./Customizations/Layouts/All"; import Translations from "./UI/i18n/Translations"; import Translation from "./UI/i18n/Translation"; import Locale from "./UI/i18n/Locale"; -import {Layout} from "./Customizations/Layout"; +import {Layout, WelcomeMessage} from "./Customizations/Layout"; import {DropDown} from "./UI/Input/DropDown"; import {FixedInputElement} from "./UI/Input/FixedInputElement"; import {FixedUiElement} from "./UI/Base/FixedUiElement"; @@ -135,6 +135,7 @@ const osmConnection = new OsmConnection(dryRun); Locale.language.syncWith(osmConnection.GetPreference("language")); +// @ts-ignore window.setLanguage = function (language: string) { Locale.language.setData(language) } @@ -265,29 +266,18 @@ new SearchAndGo(bm).AttachTo("searchbox"); new CollapseButton("messagesbox") .AttachTo("collapseButton"); - -var generateWelcomeMessage = () => { - return new VariableUiElement( - osmConnection.userDetails.map((userdetails) => { - var login = layoutToUse.gettingStartedPlzLogin.Render(); - if (userdetails.loggedIn) { - login = layoutToUse.welcomeBackMessage.Render(); - } - return "<div id='welcomeMessage'>" + - layoutToUse.welcomeMessage.Render() + login + layoutToUse.welcomeTail.Render() + - "</div>"; - }), - function () { - osmConnection.registerActivateOsmAUthenticationClass() - }).ListenTo(Locale.language); -} -generateWelcomeMessage().AttachTo("messagesbox"); -fullScreenMessage.setData(generateWelcomeMessage()); +new WelcomeMessage(layoutToUse, osmConnection).AttachTo("messagesbox"); +fullScreenMessage.setData( + new WelcomeMessage(layoutToUse, osmConnection) +); -var messageBox = new MessageBoxHandler(fullScreenMessage, () => { +new FullScreenMessageBoxHandler(fullScreenMessage, () => { selectedElement.setData(undefined) -}); +}).update(); + +// fullScreenMessage.setData(generateWelcomeMessage()); + new CenterMessageBox( minZoom, @@ -310,4 +300,6 @@ new GeoLocationHandler(bm).AttachTo("geolocate-button"); // --------------- Send a ping to start various action -------- locationControl.ping(); -messageBox.update(); + + +window.setTimeout(() => {Locale.language.setData("nl")}, 5000) \ No newline at end of file diff --git a/test.ts b/test.ts index b2a2271..71276c8 100644 --- a/test.ts +++ b/test.ts @@ -1,9 +1,13 @@ import {DropDown} from "./UI/Input/DropDown"; import Locale from "./UI/i18n/Locale"; +import Combine from "./UI/Base/Combine"; +import Translations from "./UI/i18n/Translations"; console.log("Hello world") let languagePicker = new DropDown("", ["en", "nl"].map(lang => { return {value: lang, shown: lang} } -), Locale.language).AttachTo("maindiv"); \ No newline at end of file +), Locale.language).AttachTo("maindiv"); + +new Combine(["abc",Translations.t.cyclofix.title, Translations.t.cyclofix.title]).AttachTo("extradiv"); \ No newline at end of file From e973e71c7bad0a8800cac0a1cceb9abe47065be9 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 10:22:49 +0200 Subject: [PATCH 26/29] Add social links --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 16c5387..ad25c90 100644 --- a/index.html +++ b/index.html @@ -9,10 +9,10 @@ integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/> <link rel="stylesheet" href="./index.css"/> - <meta property="og:image" content="https://buurtnatuur.be/assets/BuurtnatuurFront.jpg" /> + <meta property="og:image" content="/assets/bike/cyclofix.jpeg" /> <meta property="og:type" content="website" /> - <meta property="og:title" content="Buurtnatuur.be - samen natuur in kaart brengen" /> - <meta property="og:description" content="Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje" /> + <meta property="og:title" content="Cyclofix - a map for and by cyclists" /> + <meta property="og:description" content="With this tool, cyclists can contribute to a map of cycling infrastructure" /> </head> <body> <div id="messagesboxmobilewrapper"> From 6424c75c96cacddadf28d483c884a93542b6611a Mon Sep 17 00:00:00 2001 From: Pieter Fiers <pieter@pfiers.net> Date: Tue, 21 Jul 2020 12:14:56 +0200 Subject: [PATCH 27/29] Added pietervdv's bike shop questions --- Customizations/Layers/BikeShops.ts | 54 +++++++++++++------ Customizations/Questions/bike/ShopDiy.ts | 19 +++++++ Customizations/Questions/bike/ShopName.ts | 18 +++++++ Customizations/Questions/bike/ShopRepair.ts | 2 + .../Questions/bike/ShopSecondHand.ts | 20 +++++++ UI/i18n/Translations.ts | 21 +++++++- 6 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 Customizations/Questions/bike/ShopDiy.ts create mode 100644 Customizations/Questions/bike/ShopName.ts create mode 100644 Customizations/Questions/bike/ShopSecondHand.ts diff --git a/Customizations/Layers/BikeShops.ts b/Customizations/Layers/BikeShops.ts index d829eeb..b0e58b4 100644 --- a/Customizations/Layers/BikeShops.ts +++ b/Customizations/Layers/BikeShops.ts @@ -8,22 +8,37 @@ import ShopRetail from "../Questions/bike/ShopRetail"; import ShopPump from "../Questions/bike/ShopPump"; import ShopRental from "../Questions/bike/ShopRental"; import ShopRepair from "../Questions/bike/ShopRepair"; +import ShopDiy from "../Questions/bike/ShopDiy"; +import ShopName from "../Questions/bike/ShopName"; +import ShopSecondHand from "../Questions/bike/ShopSecondHand"; +import { TagRenderingOptions } from "../TagRendering"; export default class BikeShops extends LayerDefinition { + private readonly sellsBikes = new Tag("service:bicycle:retail", "yes") + private readonly repairsBikes = new Tag("service:bicycle:repair", "yes") + constructor() { - super(); - this.name = Translations.t.cylofix.shop.name.txt; - this.icon = "./assets/bike/shop.svg"; - this.overpassFilter = new Tag("shop", "bicycle"); + super() + this.name = Translations.t.cylofix.shop.name.txt + this.icon = "./assets/bike/repair_shop.svg" + this.overpassFilter = new Tag("shop", "bicycle") this.newElementTags = [ new Tag("shop", "bicycle"), - ]; - this.maxAllowedOverlapPercentage = 10; + ] + this.maxAllowedOverlapPercentage = 10 - this.minzoom = 13; - this.style = this.generateStyleFunction(); - this.title = new FixedText(Translations.t.cylofix.shop.title.txt) + this.minzoom = 13 + this.style = this.generateStyleFunction() + this.title = new TagRenderingOptions({ + mappings: [ + {k: this.sellsBikes, txt: "Bicycle shop"}, + {k: new Tag("service:bicycle:retail", "no"), txt: Translations.t.cylofix.shop.titleRepair.txt}, + {k: new Tag("service:bicycle:retail", ""), txt: Translations.t.cylofix.shop.title.txt}, + ] + }) + + new FixedText(Translations.t.cylofix.shop.title.txt) this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), //new ParkingOperator(), @@ -31,20 +46,29 @@ export default class BikeShops extends LayerDefinition { new ShopRental(), new ShopRepair(), new ShopPump(), - ]; - + new ShopDiy(), + new ShopName(), + new ShopSecondHand() + ] } private generateStyleFunction() { const self = this; - return function (properties: any) { + return function (tags: any) { + let icon = "assets/bike/repair_shop.svg"; + + if (self.sellsBikes.matchesProperties(tags)) { + icon = "assets/bike/shop.svg"; + } + return { color: "#00bb00", icon: L.icon({ iconUrl: self.icon, - iconSize: [50, 50] + iconSize: [50, 50], + iconAnchor: [25, 50] }) - }; - }; + } + } } } diff --git a/Customizations/Questions/bike/ShopDiy.ts b/Customizations/Questions/bike/ShopDiy.ts new file mode 100644 index 0000000..d4e7714 --- /dev/null +++ b/Customizations/Questions/bike/ShopDiy.ts @@ -0,0 +1,19 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopPump extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:diy' + const to = Translations.t.cylofix.shop.diy + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + ] + }); + } +} diff --git a/Customizations/Questions/bike/ShopName.ts b/Customizations/Questions/bike/ShopName.ts new file mode 100644 index 0000000..7f849fc --- /dev/null +++ b/Customizations/Questions/bike/ShopName.ts @@ -0,0 +1,18 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopPump extends TagRenderingOptions { + constructor() { + const to = Translations.t.cylofix.shop.qName + super({ + priority: 5, + question: to.question.Render(), + freeform: { + key: "name", + renderTemplate: to.render.txt, + template: to.template.txt + } + }) + } +} diff --git a/Customizations/Questions/bike/ShopRepair.ts b/Customizations/Questions/bike/ShopRepair.ts index 7ad84d0..504f716 100644 --- a/Customizations/Questions/bike/ShopRepair.ts +++ b/Customizations/Questions/bike/ShopRepair.ts @@ -12,6 +12,8 @@ export default class ShopRepair extends TagRenderingOptions { question: to.question.Render(), mappings: [ {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "only_sold"), txt: to.sold.Render()}, + {k: new Tag(key, "brand"), txt: to.brand.Render()}, {k: new Tag(key, "no"), txt: to.no.Render()}, ] }); diff --git a/Customizations/Questions/bike/ShopSecondHand.ts b/Customizations/Questions/bike/ShopSecondHand.ts new file mode 100644 index 0000000..90b9780 --- /dev/null +++ b/Customizations/Questions/bike/ShopSecondHand.ts @@ -0,0 +1,20 @@ +import {TagRenderingOptions} from "../../TagRendering"; +import {Tag} from "../../../Logic/TagsFilter"; +import Translations from "../../../UI/i18n/Translations"; + + +export default class ShopPump extends TagRenderingOptions { + constructor() { + const key = 'service:bicycle:second_hand' + const to = Translations.t.cylofix.shop.secondHand + super({ + priority: 5, + question: to.question.Render(), + mappings: [ + {k: new Tag(key, "yes"), txt: to.yes.Render()}, + {k: new Tag(key, "no"), txt: to.no.Render()}, + {k: new Tag(key, "only"), txt: to.only.Render()}, + ] + }); + } +} diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index 91b6d9b..34697dc 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -88,7 +88,8 @@ export default class Translations { }, shop: { name: new T({en: 'bike shop', nl: 'fietswinkel', fr: 'TODO: fr'}), - title: new T({en: 'Bike shop', nl: 'Fietswinkel', fr: 'TODO: fr'}), + title: new T({en: 'Bike repair/shop', nl: 'Fietswinkel/herstelling', fr: 'TODO: fr'}), + titleRepair: new T({en: 'Bike shop', nl: 'Fietswinkel', fr: 'TODO: fr'}), retail: { question: new T({en: 'Does this shop sell bikes?', nl: 'Verkoopt deze winkel fietsen?', fr: 'TODO: fr'}), yes: new T({en: 'This shop sells bikes', nl: 'Deze winkel verkoopt fietsen', fr: 'TODO: fr'}), @@ -97,6 +98,8 @@ export default class Translations { repair: { question: new T({en: 'Does this shop repair bikes?', nl: 'Verkoopt deze winkel fietsen?', fr: 'TODO: fr'}), yes: new T({en: 'This shop repairs bikes', nl: 'Deze winkel herstelt fietsen', fr: 'TODO: fr'}), + sold: new T({en: 'This shop only repairs bikes bought here', nl: 'Deze winkel herstelt enkel fietsen die hier werden gekocht', fr: 'TODO: fr'}), + brand: new T({en: 'This shop only repairs bikes of a certain brand', nl: 'Deze winkel herstelt enkel fietsen van een bepaald merk', fr: 'TODO: fr'}), no: new T({en: 'This shop doesn\'t repair bikes', nl: 'Deze winkel herstelt geen fietsen', fr: 'TODO: fr'}), }, rental: { @@ -108,6 +111,22 @@ export default class Translations { question: new T({en: 'Does this shop offer a bike pump for use by anyone?', nl: 'Biedt deze winkel een fietspomp aan voor iedereen?', fr: 'TODO: fr'}), yes: new T({en: 'This shop offers a bike pump for anyone', nl: 'Deze winkel biedt geen fietspomp aan voor eender wie', fr: 'TODO: fr'}), no: new T({en: 'This shop doesn\'t offer a bike pump for anyone', nl: 'Deze winkel biedt een fietspomp aan voor iedereen', fr: 'TODO: fr'}), + }, + qName: { + question: new T({en: 'What is the name of this bicycle shop?', nl: 'Wat is de naam van deze fietswinkel?', fr: 'TODO: fr'}), + render: new T({en: 'This bicycle shop is called {name}', nl: 'Deze fietswinkel heet {name}', fr: 'TODO: fr'}), + template: new T({en: 'This bicycle shop is called: $$$', nl: 'Deze fietswinkel heet: $$$', fr: 'TODO: fr'}) + }, + secondHand: { + question: new T({en: 'Does this shop sell second-hand bikes?', nl: 'Verkoopt deze winkel tweedehands fietsen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop sells second-hand bikes', nl: 'Deze winkel verkoopt tweedehands fietsen', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t sell second-hand bikes', nl: 'Deze winkel verkoopt geen tweedehands fietsen', fr: 'TODO: fr'}), + only: new T({en: 'This shop only sells second-hand bikes', nl: 'Deze winkel verkoopt enkel tweedehands fietsen', fr: 'TODO: fr'}), + }, + diy: { + question: new T({en: 'Are there tools here to repair your own bike?', nl: 'Biedt deze winkel gereedschap aan om je fiets zelf te herstellen?', fr: 'TODO: fr'}), + yes: new T({en: 'This shop offers tools for DIY repair', nl: 'Deze winkel biedt gereedschap aan om je fiets zelf te herstellen', fr: 'TODO: fr'}), + no: new T({en: 'This shop doesn\'t offer tools for DIY repair', nl: 'Deze winkel biedt geen gereedschap aan om je fiets zelf te herstellen', fr: 'TODO: fr'}), } } }, From b8f43abae686ad0a231fac9727f80e3feaeb6f90 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 12:58:15 +0200 Subject: [PATCH 28/29] Add social image --- assets/bike/cyclofix.jpeg | Bin 0 -> 139592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/bike/cyclofix.jpeg diff --git a/assets/bike/cyclofix.jpeg b/assets/bike/cyclofix.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..5a98a408002dd61dc13d6af1f91c18e2f6011df1 GIT binary patch literal 139592 zcmbq)V{|4>+wC3Owv&l%+qQkj#>6wRZQHgpv2EKnCz&(P^M3C--#UNKuI}nuRadF2 zd+pWL`}(u`XBU7hBOxsT00993Nc|0fKO2A`04Nw37&sU>1OzzbKY)gYf`W!dfPwuR z5YXTe;r||JDCj6ie@{Ye3=C{SG6H-8G8zgB8g>?TadAD6|KA4w^aD_!LGi)4z(7a= zpeP_<C?J0Z0fYbm2sqe3L;WwHz#$-^{|*-f2KLW70Qx^A_2&G%8tDY`cv?8$R^I?2 zgtl4bDe0>grxa|h%vZEyPA!we=U|PG9J2V^){^L9vTldvYjhz(ms_kXR@AIw=?2_| zNt~{i<HJuN&A!H#VVC0phiAA;%s0YwGQwTwl$*bYCG3;35zyl)R+z=i`%Flmu6$D- z#5qn{W<{2S>KtKdeNWVy#%Jhc&{}%yE_)7smvr3{E>)=NLxn&br0bY4lkZUnK$I?~ zoT$F6>DT;7=42T>x4E=p3k&J*&1vtFA&ZUM$5+p)99yh5k***?sL}Db4Xp)wTyz_E zqrTp#>$fdmkS)vfVVGgAgt`|Jcv_hZfYX=F&=QVc{j#sWQR_ZED$I2K0bKG`!#}GJ z&4zsccFH3ngmVX_Cj@#YA6TFnWKz>AH8?QVfQtR1+&o&x==z(;V<#wS?Mp@PkcJih znCz{Ipzf0@vhSy}m1Fs}K?Db+ZA<(@`FuZ(SwNA?!O|E}w{9Ay&wC5UvcP2E7HxGH z`Q-YR57z9_bP#cp3@?R2ud<H;Zv%(9A$QN(uPinDG1BAcC8on-`RclgpPNGyNhp11 zpeyJwywIRLdVT%lJH&x~W=N?1Y+=$iiZ#Eu;3?4v96*;bLMuJzI_er8G92rxz(86P z?pT@CoQTNS>*FTCRl05iFWC6Ihd4FG^gGz=dDO5^7G{GEPms+jF@Ln7gRWT)2j0=> zk{CqB$M1Ne(h>u!3HcH%(B1_3RQ5MoI$lFt!6DCejUbcex9**;1Osp3^o(4FFyKC2 zvBglMJ#HOj`TWC9b~DvY$kfb{OAeOVXPj8vYFsTO1bM}JTSx*(a8ajdy4t5uMj2wf z)-^KdSXQsn_ol9WzPnp#U$~%-<2-?QtkMxTQ&nXAC<c(y5yJXLAt)=D!D!dBE;L1Y zoMHUzk88MCu}j>a?&==W&y`CC3e9LTctyXmSFLI~1t75rBhCJ_+|)%0;a4zUP?twJ z=XQ<#{IU$KE?zEmqn=k>-$hl9Os7-Bw*C<%tQw=vCKRR+N1|p~r;s95jYBafi!Kco zOTUu8(@1MFnhivq4BBu015o6uDb;Bode^5JKrv5}Aq5K-13Is>F^cs_LYqf<YQ)=^ zwEu#})kfSRv8d3sCV5mnQ*<Q(3T%tpU1H2f_FtyiLckfDxTnZdsI<%ZMe3mi#-UQ1 zz1D@1)jHpke`F;z%Rmj8{WfN<&-SmJ)n>Jit_ohe?<|sfLYJN<?lw`LPkjSc(Z$64 z0pOopqL6+@HPnNSjF&buk!?yg0;{C-@!qkO7?Ot(r%>)k?KiuoP0%ch@)9q#ta{7I zG2+hX_XC6Ncv^C!M>cSx+~;x}4EqDVjXqOuKxd8VXQ82(tMd(NP{zG6%+ur7B)ss# z-d#gN)h{uyP%0IIl%(B0weWp??K!O4J+xNW;{>9`JZ03yZNol;beHeTeX+b=4x875 zLj{&j=jWm>^=c49xItALs7CV1r)8Zuf0z2DrJtds7ZKl-IPG50UrPB0P`W!nz}{Zx zkdv#j#OdQGpw$!KX^xW+e%|YgRwNu{BIj;@Fm_q;7&0g8rZ|KoZU-L+=1@bBABX5j zl1&ur+-lJeZT5)Zi=v}V8|~RNv(-!lX~t3@E)L~xlexvQ%*j5Bl)PNhr$VwcNrU<t zI6^tJD_Y6V!;(&OI&LjXDyb6z46lYyt6yJr{el;Z!?i#$%$8nF`wA^e2fwVOpm_&B z7}>9mt$W32U;<gY<d&dN!K|sjroSuEgmJAJusq_M_dY=^_k6ZWbaC{w#uBrl_Y}(< za#wkauEJ9AdYn3cij9BgH<#T?kZLJLG7hnE+zkXBsu1KnjZ$%D&biDgtx}1c$G)?+ za9Y*>GJQ%M=l-BOKi>ESSIDgv=NuzuZS*#vV&Rao*=ADRlEeFudd||ccZ~V9hlMGw zu<`qhd{vY$vjWn;mr3ICea~#lLHGE}dqPgJhu*S!5te>H0l#ZyMHcHZenuhlV!2vt zj1Lo5EV%&ZF;!Eir)Rrm^4)qi&-v~f-hi#e>{E~Pg1z7JxNrLx*tatAWYOn%^2(%T zKHWM@i;}p~@C<X5ogCx#P0SCeaJQHpi7&5D7R@a#8G>C!h04@crAP;pB}R>-jU+Q< zLEIsHTY|8Jt4y~$jxiMyMFlH=h=WL(W&25|mYz_!M5Cs&g7Hpc3KX?N*N!1wr;rq? z`k+$8C_254PWz3DF-Bey$BP_CD<Yp!GpTD!<BW)l!L5EjavQc!Fe#e2KKABH&m--c z{qKxL@D>$6ti_+i9xRNLOlBLMpzClVZ{!Twq`T5T-uRNnUO^2^h>zFoR%oB6KIL)= zM|kc`-yY>GW!4aK3x84$W3#3%1z=@%Er^a8U~&5Tzq7_P3aSHBT#{3_${7h2!hf%r z$y?FsAFnLGUV3#cS5;z0P<LII;F*^60a-u6oliN{!oH#}ngr@&Anr788Wyj;Nh=-_ zMs$L$7Th2M?Wz-Y-|mZnU(C$kWO^iVoxUy2Y2P1Wcifzycd;i%;%HE#EA`NJ#Xp>( z#|jKVz3U^DXAm12Xvk|rUpn;(N;2;hB-uXT5#J_Gah;^4qq*Qa6FmbBhe~L%2$t%T zSvkhgWTa*sL$%mTyy{)oHyeT^eWy}!YL8jLc`*Ac&)D4fL_jRlM36gOR*Wd*-l6p@ zLR8t#Ucx9Gq<f|a;PgVE;GnHkTgkK76lq{3m2Z~)gWqo#O3D%+E+><&e8LnU87w;} z4|mW(#$Xs7ac_wBNJ+C$t|mfy9+sNZ6Dr2Pg0_pGCF$yMr>V-x&Ce30ztRE5toxuY zmUD3vJ<|laU~D{!1SUa3(5Vc2Al6BL096r?`Xswbzyf!L^*CDjarE%ucF~1MZ(IM# zC_O=GUT5hUgX(_lezW<#XbAh08?&piHfO0FCQgIZ=KQ!)OG|Gtb|-~VvFN3=M)GYu zXTq+H)QqCz@Gum`sUyb58ac4KtXlwYl@qrj^oGTTM%iff?E@{EGz^Toz%$zXvA<8R zF@ER-B18!nT&*|U?%~ZKFBx{Vr*l~ita`CRdIAA^x^ut)maJUqyy|qgr`0GodW6OV zk7BseUMu`{GL#@Hdy9<u5ApeU_;7QuvkIOOhNR2=ca+-rm%E|aoU^RA%h&*o3R8n{ ziri{Rt=?B8rrIZIeI53$(1KhuOfrj<U-u@7Z}|KQ-irV@bS+n<&0cEkOx&&fP^e-n z`1NQ77T43j9YMXFqOJw^SWceXVc`Ue1?sX~0k^|o=;Y{z(goTPr%W1OQ|ToY$jjY$ zTDUXR`@|NH<V2|J86h(Vv9_dct@-vCvR1L^fobjn1!}x9M?X8dcWrkg6Xv2@-uuf@ zlGM1shF>)(QoU-g*>UasI4yC+jUB~E7<?f$U8rOT;W)6gUCy*kk#wy;jTi1<s<?5f zC-l=KMVg!?D55XNcCxm@#8J7yuqEp`u(Oe2(fx{FS@XtHo~$ev3OJ5KHDmKM74Q(L z1<4Ygm!Jj2x7D|T^wlL+(y|A*JPQmgGkUyZR^n>~=l6CMU>Fo1E`%h-<7`6hWGBk6 zL)8ldw`<D56C#GUiJ#aM%^d1%^t|B<$)zdWws+iXG0IIW6<Hsv*Ba13L`m&L7JJvz z+Kwk^8R944hVRVg-ZK@+#%n2hLR9=;%GN$lXJ?kOo$obw@(#G6BA2fa5&OR_-iD$C z(158<l<3H|A`;%oZT3C%llNG-e$~N1FhjXE`GJ69PDMIkdNgVz5ZE0*p|6^Hz)F2D zaC-mXz;riqh!<9nyuvP}HI}s(^jeaaNl<p|nJ%@m7VCB~b&sQD4y>IgeYEvth)+uH z!-*gE^C1U0EIhM~X;Wv*(npoVqGJca-rW33(_Nc*y%wi54t6_1<Q>nyI}UOs^m3Fa z7RYpTu#fb)GB+e?AhIS6jJQ=qTO`31B7q%h(Q3noYWMnf9C0}g(16y(MUp(*s)8!I zOb!p5w~C8A)+bK&^C^O)Il~M9*g1F<Sd?O7LgFy_p`lXi<MC4YzW`~9Lpa4N*_}Dm z+Z0=@FIRYg*O$>H5+m3G!vhtI+qI>UB#suOc#Tw=lvd=`E^Nj>#h_TbNmpjLI7ik5 zoDFcfUNt{Ph9MM8oj@?u5{I@X#YdHzskMgsHi~0>a4;*@p=ZNb%_QQB{ra(~+y#8t zvYzjKfohuT=ThhtVOaaswf2{DkQ|<~F$g4yx{Rfjo^5OlKD*0<1kTx^>95zW5rdiE zRmV8lon4g`KC<QHr+TX8r@Wdvz8jnn+G(WOKG*+%oP1IR4KbP0y&!2CQ<i|Nq04_E z)pJUpH^=!-`rZ4q6~~iH$TVX}-C>G%(W+f{7a+l(?SPc7-)(bblp|DSBqED+=k2PP zGwBSppy<!Uda`dpzY=$V)n>IbM@+c<S<<O{-p=EQvq584vksq!X-P==tZc!b+Mr_F zjrd5wzY?<2IWSPIg7LxD`m{>5$1|pUV_>H)5T`Sniq<N0%BZcM?uQIG)Tl!x+0b?2 zE)dvy!*qK4-EoglF?l{VbpGBMOqJ(N--2OXif~*(X}7$Srg+jFYRmV8)ckB8Ug~8V zz#$uX0mWKd0btOqNGjmZIGw!w;W7vybrqQj3u)<3??TQof}&|RhC_LxrRX)zWWHPw z)|`=XJHDa%sU{mtQK+Fo4Z*$)mn&Bl^adfVWEUow?t>6j)6DMM#4c?#zsJ*OgK*>n z@1Fxp<={DsWuwB5JZ5`JkO5{E<qEn0QYs2t!jxXENY|VJeX1Xk6+mYx8SAb!BAFIu zWNXC`mp4CCL3|W)D^dtPp0{363rzc|rNRv*ky4SJ1zK!bZ_ugZRomR<nWFOcAOjct z@ZPsV)>d=lguL`|zbAqay|;wq-VxuffVhNyi<2&c6eZl3d{|L0D=AmSq*iPM<;L?~ zQ)bVItebtp15ikUFP64_qGjqelkCuXoU^EG*20=3ID{mn^~gLPyS*f2gY~`VnEsel zfdV!l^6;bkm4Lwp1f=~QBY?GP7tsS1@TX@ww6UQcssxcNDz$DjGN7gJ(L0iAdg*@6 zZ#l&0aqGg*TRfi?T!a&b!_%hjbR%zcX^9B;urfrWNDT=N!R#niEN*_tneU7Rlz(54 zp_-Vwfsf3cxu65v?1SZ8tmb!VU2I#H$<*}PQ9d4VQ>w%n#d{DNc8p?^Y1zZpDB-|E zKlk%R$U*u85C_FnsMPWLGKWNw2T=~Mf^(rsyiBsQramgOuD-vZBt$OuZQ+frJbJJ% z#7t?>)zr}@=@hWh6WNU5Nazx_M~Yg^U5S)ccKC)*sKIB62k(}d3-ZnWTS@iiGHkBb z&{NJW*Bmdb^O10kxhPGiC{YL&C6WeRvqgOnjGYj=Kr?ji@be3ZtA>C`{ZxIltHV}< z0F}A5s~CSH$IIDp&Gq}ud5P5XwA?&`sjfH0-im_Po#L2nXCoT^jqTLqLTt{1NMnKD z)ux`8b!4cax7{&EGxzK4aVi#Pg-Z8WbB09Y54V$<A-uO61)sOp4?@|R4(Auma(;Cz z<hZ8*^^L?f$c|-ExobQQ#hU17_}d<2DIG)dq0%s?@fF+MJ+=aN{fM`^VbWx%%#}Hh zAAIMT0q)RtgN#wZxl-|OL!b)Q*$F~(RqnwzhF#oaAg&?36PdXMg~vwIsUB^pSy6LZ z3e)+#v4b%BS8MV0x>><%M~0Lw5}4@gqUWC){w5mB8pXMWIVb$6&byH+DtQ|Oo}h3W z*E~PCAMGN^9q`y{m)SWoE|r@;+6118m$dgkEO~<$L&}k6VSDFDwU+0%=eFnh=O#8D zjT7@GV;QsuR#j*3x?S0;2<lUQpqFjjDKnUx+Q;<!1Zk1i9?&8<A7YT5)@5{sr!yLM zzgu+kE7o{lcWn^}7^{6v$=Uho&E&B&Hk=gSo%*aF+QS#?<L}*?uJC)B_!+Q%7w(JU z?728@k-C@kQHH6OKbk<NLq{-f$P~hgtXaZ!t<WIKoJkY0aF#n;X_e?^58)y+-9w9- zDG3+9qq9ojxsPs+-I#qcpIi3y=~&cd;=_~mZxu}pHOM%tmG@cTi{mOO{;43<raAiu zu<qLIJEof1WaJ^!yd6Lz(+LB@H0Q=p`DtbE^qpu}PaCB`=Z&n6NNikR52h}DAiZ2G zIJTcJ5Su$@R<6(?p$%-#))OsmMgl^Tfn=X7tQ-duIdv+0U^v}ps)HS!238I2fLR5y zoS2%Rv_~{SUT=@!I4*RpcbK1%1d+5MyzLi3Nul!tFC7QNlc>@2G`(hOrL$}3Kv+v_ z8)8XCd;Rwxx$^4=&VDfs%$Ww&*~=uUwRM8xqz~cJ+Zh74(oPqAsx7TZl?Xud1Q``! zq^Fq05de5l3uW4tr>q@P^WJMwR6KZq6-PG6X!hj-vJaz6xkO6%Pf@E6@rtHvWcx*C zx-eauuN#w!$z^?g7+nnOc4>&v64o<+!XiVpfb)3~n==gGfTa-#1_s;Nv8m#}ld&gK zEOZkAjiz;R2LGKtjM5`rq;jg-=$7^_OHjvJrta4UZ7m*yZTrmBdOg{+KRL;rUtvSZ zL0XhrB~bTn?s_Pn<h&44XmLMd-H<R1?th}nQv!mmv%f$sC=!;k{1ml#0I2EXfS6W4 zKUu-z<y4|#(Yg2Lf~y2sJ)Dq@Zd_ugc`L-w1Z_{?*9r&IDX8oWOhgAdS!4KH6aCWL zcgCe$9N$cwT<T!pC2AHJPsb)(&OECWtNOVQ-)2R96;9YUPayp<P|2ScZ|C28)si^> zFxOb5k*;UOqQh-NseY;A%%j0i7eKt5plqKuU|g9jOGn_BQ?K)#Ye<r^SK*t8e#Ej@ zA!C!;v6GoF;0$8|3RG<aR!=>*XK~_idyyW=%7~U36->=duX`$nS+FU>=15lh=@^3& z-wJ@p^xobc3??*bSQxlz&1mwKQ`D;Z%}#>00Do(JHMyU_@@_s|)<S_Cq!;q6Yw0z= z8wEF$AUk5wc}dt%6b%nS_kJcgPOM->F^-AKtsEka5Z25+Ukq4=8%!=4FMZ#kFT~Y# z2&jux7A8&+4xpoIOBy|5G*qn;vEW*esu|EclQSqMan?EP?(8nQFtk=RTq?4zsA!5O zUwt8xitixA^_PHVU@FvHY@mJ?T{I$Ioi?ex0vD2IvAscnr~Ed(w?6^@ape~yFNd*{ zqqV+mOCU<5h(uDYU}CgB5VYfDG(Wj?KsqZrHl(Tqr|H6GYiDI+Th(e%n5cTX%f5Lu zNK{{<sMi&T;m@av4c0aBX~UNysE(6^!F4_TVdGnyj8J+xAMNLRx1tWiTo1;AUG@yg z>qlJS)Fd&fb^W0!xy*5hR1xfKoZ(B51NP+x{P4Pe+d0+o_kx2Q$Hx}$6dXTRDHW*9 z#xV|1xuCJ|fR~pBhI5}kj%yKvT&LsarHZ}au<*0`b@N=Uu-`54G&OtAWeF>l+)+aQ zDob7;9yxFF`~xVANTEikJO(S1`z7A&T-l}GR--tSfrGxk02k(BP`p;wFX^mvLtPax zum6ic=u8dkvKu9S{SIo{jnMQ#M6iLCM4&~!0|z?h)-8d*&3*6`@8B#{Y#LlB4^+}v zz<|$18@iQpo%#`Q^PsQ;=Fnkw_WNYPFY~4`YA013<m!3VkFxB@Xwx3f0jXs!rh1q> z?XX3VxUt4<yO8;l_K6?S!_Cpg+Bv$H0BiX8)*~o8SFKoD_M@<>3`LHC>1VNtr3r8C z{qhnPy~D8CmiHJ5qoGM@J-7tDZu$o5{!+H*oy6+q3~e=C)ruT99d7jbBVz&ZVsMeZ zQ6nHj5nPl1M>e|K)s~|Dhm&HfeCDFvGLb=0B_P}w?GK<xa{0X;J@FtT?cp=qv6h`8 z@oavY=$rb>X`)+njEy#y4z{_zpJ$9Q!LV5Uvk4NiHtYsAmzd`2O*$ssn_UkTM{8M% zcrC_J@-Sb<3ENmEH^WsQ7CFMy<P&qzlgi}ZEm#_X#XQvJ4TGAecPnGstqc+=EIOsi zl8>8T<tH6as)%8aN58C<rYkC)LnZ}!<=852&p0j?*`#eN9L1F_-))a34jI#rUG1&5 zfFBz}$;=&6f$cVu>_`tDta_U)-x>mBub&0=$3^dXXp8(b3huQZ7Y9zs3+3JR#~Mqj z%aYkRIO`Vy)irkhYw2DUE!^c>>ioLYLqHR?VV`&A()N~*=jqM|t-YR3QI4xpg<o7m zuuZg4*4mFr{WZ<QM+BJP?c=m-05TOzK7`6XBa<s?;T{jRK>Mr-9W%LZ(-3J%NzU*_ zCAJ9zq1L{pC;eISD^mea;kAxmihhBK&B5u*XC>ilXi}c+#jeIy!bF6D{qA(r>`|`a zt2Y@=BgyV-N|ZYKtpNq&48EKTt#+SRhsm0T?ms)q%<fi>uR4FcnKvou+g!I=9}&ct z8+K3=#9jg_<d5C1T?bk|tcL0XSSIGBe#fZO?S9P^y~zf~LppOMf}ASO=4diGXwAu8 z5iph2q&2K<RId|_ZE-*BhJ(ok4sRKnR7({~0n`^#=Y2xzKy9feKh5t=C=V{o%}OhS z=?NC8wQmgB^iHO%x1&4XruEKCkI9{;Ys?aix!=(fIwZ|#I91WfwT=KXDM{i`ZRlev zG3!oNc3XXCSJu?HT_h8b^X9)XlftW~K^Zerl2Y9+;@Z37%|Lhf-}2*icnL2rs8bw< zXw2l}!)2aZrO}zg*_?_1ApTo1J}OWQGt&}@Jfj_!jGIqi{ks=DS0~|ziirGNwOdef z`V*mufeyI~h|TInfgW*MgKqO2ze_9H>Ip~O!PS~jC>@G(>G@<!yc=8n#(6K7t2MF4 z=%hl&qF1|I7tN=$u=qMEf5IRjiq524{kCm;Uiz+{K*2-e;9wOjeYlysFf#WLD`^$s zNdL+sbS}bJm@Lqovk89HDspQx$vn{7bC@DN1nf>C*Cbo!pV9B0Ntt>`r|`nz(`#xW zc&>l&QYm+piv`YuS@V#FMV`uQY2|xeUtm%w9+w&Ic|>^`_B8aAl~R~kIwxd&Ws062 zt!VYeUlhKKVu!k=^gnu^C|G(oHLwg}$4#evi&fNoFp7=pORhXiPr^_-URG!iQ*`#1 zq}{e%^jZ4FNEnv+dN4E<J**skm*Y*jq}6z$r?Bo}1}_<gh=wYUP25SVQQV^o^np?X zewlvk>g;TFZA?A{_>5K?{6rWTd~-%^DsNH5ubzGTv67Dg`Od~g${Z11yF&*)fw^IG zRg&El#wI?v)NlA`ppIBDVAEt8b#D^Xuvl-nh&p9f<>^e5=K9Hsi02pJjxFIud%&7f zd|IeUS(MoQJNmMoa?};$_e|&-%$tMEKDVcjcP^Ey=9;eF{4fK!+H*pAftbw84Bt*{ z2gO36D@RRlx{d2(Fs+7!N(tyNPU8=;v`Ui5lRtn}$B|*iyc41{VT^J5&$qKFi^A7K zM<wupArC22MMK4KTfH?>A%9)0ITr|@^56>%__s~F%rJY+S*fsK!|pfac49|+zbmwq zsxy4M4YjdPrO2ZozvXn3iT$7v7(PqHY?)$v6qc#@OW5+e&H!C`wjFu)CtL%4URKcp zjM}*SMuE6dhk3D2BGP8D^rs4-W;q7ZWwq{WtkV_Nbj5AATiH8S*Rnd4f2>Yi3@D0c z&?-0WJ9cq*bCQ)-y5U!fG;OvqAAZO$CImEL@r~}@DYu2F7Lm=f3-3h6##pW)oJ~HZ z+*tFU`DcDKM2v{x=Z|vZOk(AI7Kyszqom*-{EF?<E{a_H3b^}}V%1NcRRLJlJgCRt zf*iy3R%PuOi`ec3%ZSX%Ifp-FZ7jEBG>*)^V!B*+B%~gt+w9@+M-kn2>mNx9vpn<k z^(3AC0P3A@`3=ahNe$U#8k~yT>f^sQyPfIZ2>fLB8cG?q4#Kdwix+sTW9_}%R$?;U zk5_YQ^9*kH=JrRa5SCw$8pF|GHXjvhwk?z4tQp{!j$#I?%9kj8>DAaGnphp^yHXX- z7bEPRiB59IFMJ#-*n(xlKh8!ld^?#L!$sF2^F_Qz8(h4xHTrZ0(HjJB)~$vV;HA+$ zA09&~L-6Slv*wz{)p_~Lo>b#4VlzZPc-;4XX&8Lb0MB+ps4TbI{qi7#I~ksi$9Kgj z2vww+Q214kJ!{#{JX)HEd%+%Fy!^;xkR#1IzE8tX^p3nBg0c|!et%1U7%XzcRO-2Q zr#%zQusxpaZddS^ITy~jpPylW@-F}=BdXjqik(M*->DUzR1Fl6U4^@)yE?iY6e5`$ z$c=3=cd!mkOlh>ZOXS1091&8h<Yw4czl*oov|J}Xfmp)imW>ZQyx4i!)iUtCQ3w-2 zhfY4B!>eCmHKEjbX8>E)1eaiq`N%&B-*zNB^qH|STW=n>2j3RNt&s0bEPi0w&NaS8 zCJrKCI2Dm!KpB17pl~SSenSr)w|dm|ISoqd;i`~WY?aoF45@$Qc^1moHM#b?IVn@~ z9>_4r&e-&Fd72TkK@WstISD7ZRX_IJXz?!Dpl~ojgRlh%^h}-DlNLg_2^E%V=Tjmv zHLYNse9{;?0iWJuUE8q4Ikq~=q8`E^QL^F@fVa-|jmiUTKnAJssEQx6aj(nPOZvKt zvR^4z72nrP;>TDE*vqAPtg3JzBQu_q!<XIWe(}&M7JRxoT&x5fbC{nWZ-I7EKRC>h zNshlz$l_yEYUHn-WZLKvo2ZK_OwQxc>XjOgqLnb?+G9J(#TCkrFn^?5mLEn4KZss} zPxEa|%fdV0BW=ds9i!2cTePS=kAAl>G>to7akaW1V>)geZKnCe+=%pa5|e)0Ygd&1 z1F&NzBmC{N-)i9V@lh+N!u2|MH8Lo@)I)wUvCV6bjJBF;65~Updf1KM{?x}7oWZod zbTI?->B+;qam*n^2eOrd_lW<UK}H49WH&jXLy-2GZ@AVME^M$%kt{;eD1MCKCy~(A z%uE@7PRbel1W$DWnRrj?@1)FRE2(S6>g=|6ryuDw*h!uoJ%_2a`Z_o&GF=ZLpX?84 zw`-h+-v@Qi_P;mbUUkTSz(A1V1p|qHp%Dc+d*IOZUT1hh1{av=Q%dPV$l^w@NTqeh z7CEFYB*>T`b3ql_DRWoRtmj4By_4MVw=<Xt?Wq(mpE5pppXm!Cej=!ppiXLf%F$Op zOT?6ALCW4j{{bv|gS54XmyLT|Cq@LL>GM6+aIaJx$7&F3rRxOY(mLE}WYRngD%e|f z4IjJp-!pQSvj|SNY`94^eQ=og#WuFJT9J#GqX<gn9Wrmw>61x=oh8LOs4fb3^^{It zi%eiB6}Vd_{t~eElGLvpEZNXhOw$CVAZk0y8L@jfW3+@&>|8vBAP>TcAL%KI*U7u* z7a!WvHsq!8d&$-Mg{g^{^IlH+%k$x6!aU<`XNNm~q*M$)nZ(3^hK~5^O!UEGrbB(f zS@v8Y4D)?q$N`NFoRN$8D$kFuFu1%sTjlveG4y0ztM;5}Hz9yrNVR6O^#<P(Ewc*3 z@@>k505gU87>qjEs&2VFn?lVzvEAB_^H&JUVm<L<>Q-xGplkg=O17~;nQ%uax(;`R zt={%yHtAD*Y7>L}dqEo2UoNs*RWe*dQc>(j%+j_f9zzW|lx7Vev}#Ms-^-POB^ru% zjm0t;q*A|;Nyn{gn#rA4VoN8X9uV8~9Rou*b1;rr8ctsK2(Ze$sY(c5rG({JdpRF% z3MKLO^Vho3U5p*oie4}z*S!cM?fLdlD}J^0EW1V8!|$W~%WunFs=2c=z(;EEnt}Se z*mlg@G4Q|eKu1ZFLiL$(Vdb0g!GN{XY`+Fd1~9Uh-STzxoTR6x-=tq#{Js3eHPG|= zztw=iI=72`aQ}w@t7|hB5|*^T1r|9cImiFnh*Nlm-Nh)U6BeGp&|5dhpu98)yrnH* z<}4ZyRV7IT!Mm%tg<X{092;)j#Q%_?JO01T{*&qi0yDk;(`Wijv7AF%v)1_+zqS>H zNuOPDRZ&37f6R>nk4YyWG_T=}beYX5-7AQMPqjBf=Y*8H5N>A0L|ZJot91GcJVo}m zPDfTi*q4w1IaK1Fm$u;Kukr5+ZxhZ8kpF4uKe42M5bqG>T;$+~ED6@#up?irxZC$1 z$e86L^Yu{{CofwatND9XFZkf&k<LVuyR=t3gRG%vm8}1Qh5iB3Na=0N0vFhLYmBJ{ z^3JT1eb&0=aV6wz%MV8$Cy!rx5FVUaDe5hK8~uLd%LlHW_?qf{f?h53D@)K&oJfTH z?SH2H?<E&=GluSrLgVFR(`7*TJyK+M7@fuV>@nBGs(-{V{|TWfi8bYdh;a}e4w6h1 z?r|mKG17^O!k2Wu)`gY1qj_C#@-L!>#|)ILz<(+~;`79xbeZMTPtL?)II_NnK3De+ z3S`t|86I)YIJ899-{cb>|L?o;KWWIwKC;rkt7?(-iC-M}%l(a$^bR+Ir}H&eMXRfh zAyI@N($bgbf7Pt}dDyk_?+lJ`T3Igtjv?^N>}vxb?3nC-S^YmfS)^a*d>q@&l>9f} z;;)X2;IDuS>VM0){_46w|H{oEp`c;@m2rV0{}p)24B|(TZZUwDG4`QUh|U&QhvV`z z@61qCucpI=i?vZw4`Qle2we3GX`vNrtGfL*=Xv(@Ag*{|+3pO^t*h>A$S-h_C1r3L zW_-lipw*_d=SIqU#JQxYN2X2Hx}#XGu9}iCQ!Sh>Z-JJp7?SafcwcrnR~Yx-&c_2N zYd20q=*U@Jgia|iu<IYxZxkAjzs3V)D>)AJ$3#>XmX+<O7fjq1NAf&@<M53*;@Y~G z5Ii=c7x6Y|8tb<Da#k#}dFxw~oHfD9B3r>ahr&w}2nDS3TFC@U%&O8HTWA^Prs=>m z+nNuzCN?>*l<q|uAGH>1Yw?bX6sPA!r3fHorM6Q_+JG15sxyIYrE7I_`A5sW{RSZ8 zmD|t0mug}8T}B0go&AVDL#Ab=CbW3&;>m9I*m*8#gsJvil?B!1Wn;apdZf6iL3UD- zmmi%p=+v%hzVEO(+|)2N@>Jg>=`<LXx6aw^Zl>Y%=E*)jqoS?6o84bMrOThO`QWaq z1$?|RLOY(p;Y@8EH0jXRfxjST`2k12@KI$lA=8&rK$E{ZnzqW8GXX)OaK?<)M*l07 zNE}kk=DZ~#M45#3g6BK0(IbpgsHg_U;w!#}Q)k<Ij>{PWY(mP`#>2T!5T?0Z$6t}b zt4jH4=|gh$g!Sb116AIDti8F{eIk3q?)6=B9qb;v%Oe7mg_-9Jchrm0mS<~;+dMg2 z`_mc_!nV!JZ^PjmS67$rmohzW1h!semFDPi{3W|TC&EBeptsy+5Jozho2uC=bMVP( zdN-p*_=!O*q;RmN!N`nZrNC0FLyolzrjDQJ_pD!)40dx9gv*o%W{;5pr47oeJz|$9 zXt+II58F0ZT@D4#3{O#$y*mUJQuluV`Cf()Es6SYvw90tgaQ482T6|;G)R)3ESU>g zWL8u42KD!;^%So&%Lc&5nPmR(1BKetWL5}TqvNV@wnyb2hj&JzvH`EkKEl~uexy{Z zP{W1iSP;fSCwk6VqTHUj+X?nh1#v&;lkFdTo#D3=jGCSc^`4pwTPsIY1)e{Oet%s2 zLcS1)=33+^62SzKa_`ZA8g15S+X@M&X<-yOHQ;!%z&Ilex$V@as(dZIfJ$lyu4I2a zjH(ybzL}m`Os{+hBIds!fflq(fBfp*d?{DG&>Qdtw8f+zjMs`SfhOY&|D>Bv<^E2_ zPM#%RLAF`mFHNY8$zeajE??qLBB#^eprT@fvPmqJ^^ts6lqnbJtdbG*3gR)P%Eb^% z7Z`uWNM3{+$q9Jpg)K1I;F68ak3}{4$%MYHQq&b-TV&msH14KCWX&yfJ5uB!^6@L% z7ipy{*v_!*)WGJ7%^>-{+wK~L_xDY`PkdWR^tmAI9&c+-=L-MXt;Y}heg4{j7q9E1 zgPhL|<a4Ef#NyiHDe4X0)b&`+2a5&tCx<&hou^uYkJ_m&$+E}po%rtR-HWN1A?yew zYoFJ1zKrj)v5*JdV8^lI&V*+~L0+K~+sXHgHJ-7RT|^fOqZ?x~C}aioW3+{+I}7sm zsbe`cw+r1n-waA`W96<k4J+R`X9>+8YZ;vMlY86(UNpnrF+WBOW%%qOu5qm0>%S5n zR^*SnO=q?f9!lMh`CDhLe4Fx7-OUF<m+K39DpLB*pA=L+6e+*?zWY+Fm5ruXjj!nH zD!P;FB_BjWqT53$B2|cF%7qL_CH8!u*^^PLR@=AdQHNAJ$?q6f7l{2%FT<+5w2PBV zDb_o!ahS~{qcXKWX1N9<7BWDcIBRwbfj6!7chA?Kh`k|2m9$?jQo|KUAp3Lo;mip` zKt9-o1`-4(K0`_z0fs(AMk1Q;4)XU{j1+5#6Z3mE1|?peELu^`<<uaxOms;8u;KO1 z(ZSCQvjjlggs_zitg6|ZOd??yHYesefe<iZ_?XOoRP~xsMr@J$K86EB%4y4VwOL9B zHBlt3N_sU&((28wPDI`yAg!<3S_fwNNNYi0(}IFhxKgcR(hSew=c63i;YLSk1C1MH zaK5>e1Z6r&(Tu1adJ=dV9Mhh-7er8bT*8L4^0&;TNeAY{MQifLKvpW(d6rlll;YL` zVJ6ua8#s>*p|pJ*CK*=e6mR-5YR7G9Db#r{?jz~IMJpiZ%s`daHor)ov|TD12DyZo z*w0c_1mHb91n%+%bY+*+_|~^eJu2Ae{X!Dd6;leXWPBW~`NmQ5mgF(xBvff2e7H~o zfu$5HEPHW+ksR9zRh9OnoFqNl+Pl^*$MOMW-$;FjKB}I1L(-w8B^j%eH8f`okAY0O zPLj12W%!6LnKdigRw_!nx7WJN@CzE&3|Vm`sS3U-eLI_IgcX@gN;59LI}32cPTH}* zj~jcjsEm?LmjXPLqe?|(Y!mjkKjdN4sb|Z$=vde~G^!rt9>qu@J8;qcCt$P>0084I zHC|Z_oNSMkxFr6sIvOm~xLFIK)N(>s0RVs)xW}aoj63fIAf@Fhn5-#^Bk|nF7JTIW zW38)&VOK<XLc9^mJHywp-f4P9{hp)O%+XTx)5aD6i~|7l2g1qKmFF!Pd;hTq0N^-! zvFT(~1<Q`jC`As&6Z8M_I{trn{XYi(iT}&%|I#|xKeTRcl`ctek%?F=U4PhaK`L&w zoX#PAq|f&gHCQSeL2ecHyuds$>2U*6kG&Wq9iwHl)z~EK=%N;;>59{7k>^WPJ?P-- zFndw=@HD}`Mt&Yg8dWdQ37y@Jlmae311L78n$iR?p}n=O3`k+5PEssr&lIRzw#X+- zuH+d0>d#FNnVR60$dDqhZVyw{v3Ph3b1uYB`enmKFJU(vuW#lUl(|9IlpvEC<_l$L zoMoB<&FM8HzmI|$zH%P5k)ljH7)oT_Y4EIxei%=pK&_I6TB5Bn=trJ5>LorL%XR2R zO)>9K#G{@iQf{4!`X05BNGNlRN~fAC|5VP%aaiH?9PHE7Ww*-B)&Y0*kXpz$A&zU1 zkA6w9_RNd0KIRlke=-JGA?Y1>Nz{ml>_Xu9!B$OQ6TNR&N-L^ZZ?yQ5z3j?Y%jrxW zx*Clr(&06Zz^``^Q%<39TwCNAl>q~jR?Nz(NF(yLC16OCC?h_iNdnV)B%)e6{rHXp zcfv~d9;2!$4N9lW*}+^b6De=pQRllElCd2*ZI{qnJAS#fHknL>l-(yJjjCR_q+h|$ zrkhSHn*0o_Knlq0^85l>tEy%leoWa;7q4h-T;JpeC|&=)NtfwQjF>-TTgVPi8*`x; zCNH^uW;nA7nB4gC_B70o>A+8pSgaN!G+e%NMPse3_m^l8nse|1FLJTPDtfCWgsgL% zQDo9ppUykxF0-P1!ff)zk4PB1QT9|&yApS`cT=QQ1Z`9&6Z}piTP>tbGmhq@zvMcV z#b?O5@fgd741Qj6LSgw}QR&<nz5?Fb&@}Nl{;~O)?gchIvUu-Y@upVNVxH2GvsL5? zD+{f<P{1(a8@|4D?zpUK=L|)~x#+p=;H$M5`u6;*#N~sd>?z&7hC9|UbLzPnmmUi5 z!pA9RZj^_J^&%^_$+W%K=}7<-RaSjFruElDWW>g`<~CQ)9{_K8HTHX#g|CE*i|Waa zh*QjQ>AhvLDVDz%8GQAm!Qa2<Te@jRpT^V8_#*}PhzIJ&O<b+|PEP$A;kS=lDr&kN zQfNfq`Li#(O06<aw#GcGQtj>ywU<>+`QBE|81a^UrcR4YURYm4?tOR}5O$h8wbe#^ zPgWW6VPt*Q!P{<HNypP{8*twv^lS2|HDKAYB9M(G3z{PJaSp_6+|4r1s%Ylxq)-9G zWNeF}gpiHo4aw&eW(gPiaFjC=$4X(-8i8mK1HuSTRrWsTjN66F=-i&Ihg^j)9F@_% zfl{hw*Ct;_2pscfXQm4;gm&EuaD}_|+4i(gp4;hR*^}>0X;|ZVjR)D*5jI(km4?P% zh)AL-iEV>d^}3tzQH;QoSILCb3`7gM4(GTbJGxA-mTt1iqX`FxF=zhBpvUSSUEb^q zAK4zE(h~KfT(n0*U0tG??CYIe*Pib+{heJ$pMvR6*Wx9IpEvRQIU;&O29!jmZCSX{ zvgoW!?UF7Eu$yn19+UeV=?twi9Tu!h<n^-ir(B8-(@O4wZ0<Ib;zdK7E`EXO^JZR1 zoTqL25?DE00Xm}XVOy)SL8ZL<rb-DC?!QyS>Qd|*+l7K~i}g0n4^&h*jk?W6XTEU+ zskQ{9mFmidQJ`=Ojo31U6dp3zNQW$VE8-wEoT?!}2~^qvPz>ELRE4dH`So-m6B6WT z8f;}b$3m6hAxsNi>g3olOVm1$!}0LJtY97EH5suJj4jEU&c+%{c?n@w+xfzYE1?sL zYos|W!Of1C&hiSGc_f_gOUM|m>yx|$W#1yw3j1uhlA89ROKv*8>-Rg3YrUh%3BmW_ zZaK>+#o@6^abz#EjBD(cudvE%Tz#^OHtaV^g>6fVsGWxmar$hG(v7=THw5E`Te(|@ z2gJSSHI!;oOqZtsNQKwWjgI#Rnqu<Gb_+1Mu|CwPQYGuQ*@{cKmcyIAB?Xh~;6Um+ ztFoSpB*+57`h`QERH;w*>S@HZm7`G%Ml^p_{VEx*=k$TGn`QOuD;C4I7yAjQH%}su zD~c0x1jvxCsGpXV2WVU%v28dh((AC{>O^g7XM?jm>Xcm0%cyLc?I;1hMH>;5AcoBV zpveX3EG*<TnO41UWw7(|J6B77QDACZ#?xmk(_sP|Wf8f6Kr!e(929<*4pWJwqPX)I zeJ5TgjN-ne5WKRHGY5@lo#5-AyI%O{3aa*H2gN5z(BQ?yQ{cdo1v;E!YCA>ZICqwK zSkX9{weuQHtCF;7fo9+G^%UdBiYj5$ltpEVeO5q1oDAh6wwp6_R2XrtFzTg7$BX$& zyHUv-TQotjJyUImE6?-l3U`G05~Q|T@HaqFJUKmmm4wqs@4pO4SII@>lwzlR{w~Py zZ^l0n)(ggp8xAGnUw<o9F?djwp8zB!&qjLdU3n_89jDoUOk_k7jDE7x`^B(6={?6+ z3kR?$0iymX0JCO+2DX1F{geI&Aa3^7rrb)#!nCS?0DnoC3;C~;6bKk7IK*EcuD_m9 zAOKL1zu(pXNEB2w5-4;GW@J(pRyIsBb}VvXVhSb^4k1cWC1~;gai0Q#05Me^bK4jl zT<qFu-v0w|h0W9^_1r#H8>dXvCzttPH!AFki5mIQk*YJssuK}s!|iE)ln}JcB>HU# z*CHN@y<0Azry*?<d2zj!8vmTtQn{d;YEs5;KYO$V#fmPs44Bi`O7o3GQuf?~nvl7J zo+<jCV-6*(5jm&UcQa$MK9aCR_U`AwBSy>bx;5FKgW9l?Fz3`b#>1>lpVZ7DYHWi! zqg@HQPPi$TmQ!*tBB>k*L7w(}m#!M)YfpY=97Rq)QpI|*>s5UmHe!A(2_SsPIO$3p zR)*4^hVmv}#>Fw>UvN%Rvz>EukGI>dh!q801$kKwNnCQZ)Gp5oT(;Xen)QgYZE3KE z#FZB&7o|C@8VZ%i5)dV%=?1FkhSE?8pR9&0R=M-=fcF+W5%Rh1N2#+8Oz{#H>6uKE zAzv0W9fp{}QK{o6Q2+Wc)s;CGlPQxQbFbig)pUH19V+rU@X`}=rl74M6^hv~@8Hoz zV_f2qAFBlQQdd1(j3|i^I}d&ExZqLT?XhIZPMDZ6WRf&w<{W>OA2CvOZdAOXal=C) zdGF|qPdrL!=X(vw{sS1vnhx;X4`bYHoZmCxm0N=O9De&^cz&?G>L2=bJyso&_S(q2 zf(L2DVKkNs@wOcj_2e%aZNIJQ_w14xGp=Ynm;n9h7gw%S-$rcDyehU835-_;RlTYk zRO2L0c+x>P2<SrkIoTS7mVhKd2Z_jfM=oJd^?hs&VaCZo3W6M8TKTV%r<YZ@F_T4+ z9s5q#b1-a<6{khlny!Sj{isB`LUWodzL4j!Kc2Kq%>?vLLwG;XP`!z(sfr(?EV?z} zE8zJLpu#(bap4a@?UnP2cCBdj_%q|}=VBS^dfa@d*{Wt(jeS6PM0R1DyiUM%P<hm| z-Rk*%r&0}CbH%*YTvW-?!ld!4di~aj&4~3UiJq_<#QRDNTfEJO^S8N8s0h(Xw!5gf zNWHWLqc(e5<-QV+<q-<XeOV!`hC5=ZkurRCd5(zavnL0Bn!j?1RrVNLw^|yBfCCCr zoUWU*l1M>0<`}Ev*-&{zzqFWiT+ZZps#_q{b+V*TwNy8y0ZaLUZ9Mb_mb@u@f{jU( zJB+(G-3MpskGqxBE<YM9{!%Mv^+2SiYK6`&+S|_G!zk+QQ$HLJ^!NS7q})!3a$sPw zqf%F+7KW1Vsl0+*G<B=Q?q_}%x=$o*0wY||h&h0hI<67d6)_igP>Kzti;y_IPG~f> zfFm6V(#-jGO-PpjW%3pl!e$QguEIupwgq|FEMcgrad)<Flg6@`{tPBJ77*!N2l3;g z^U22Duyj!p?~3e*dZ{sL5}K0@I2?|2=2jyO<oz+9Hsg;bZYP8_(MP$KnNB6iTn^iY zvo?{Bv5NvnkADC`L&M!gM!u>&{b)r`7!||V^IqlT*+<PecqxH7CJV6Qxu{+yQ)ZV@ zct=@(0Jg<U&=b{djFB=Mu}qKN?zV;8icT;gh3QEjraANLoeV;)q&`u57}~~pT30{2 zHY_cpnOitErbcs>1-QgWsc6aT5>a(+a_qXtts*y9?6t>Sa;Q)EMEmLNuqtOThWZ(E zbKIPkwudEmrwq5^x9BnWNr9}ahY$TU>(|@zM@Op)^H{cZV#IlW-;$22sJK)Fyv|c_ z|0UJ*QF*QgcE-hT=^G4qCoD8HoOEziyERu5c6wa;Y+dAbLG4PA2473ZIk7BoA%{9k zYvD1(EJwG=fq%EZI+jwh{q~uIqkncLA}r!0+KSs$hr^JyvXV;vecIIE&)}}M#I(3+ zEtrV)_!xNJSqbpnbfYHdxpre@och%*leOX=5LYEaSt$iB1}EQ?w9C?u)0&zSnO~K8 zDlYv*9$&2tOY^`>5u-~c_k!dq7mUdf;~|JOg^gT(PrSpO85a32Z29HBm-cnVN7qhK zb6Oon;lL23a<JRtj;eME&(S4gR^v$)x5kL?7lGB~y;nIAX4bU8Se30I$Gg{1o%0eR zH}m1lnT|)|yd_4B8HQ*dhcicu(ve7~6CW+<)nmLVues~)Z`eP1!N%~|ad@YSX^S_9 z9eQRHHX=RVQ)EW-A8WJYwG5GNu<UxSzMT~AD?~TQg|*d+^`~NB&4|zNqWus=!|bQ@ zJdMaH#&S>!X<PVtSo#M5`F)f64vEe}oaQalme#PZzh-~&_1pYv-<XC{44ZFD5`Q)_ zS&2ibgskQ=Xvu~K*F|Gt%S90#2W^=ii+9SZBBk`cBG_(#8MxSldDJjU+@OKIwO<21 zP)pBoZaFt;;6@|$*{y^C?km4PTxZZY({TZMb3?iTPE<FD%-zQbc-A<?RZG_FG-jhE z!DLq6A`^s^QNm|eh&)Nos@_Y(&ei0mj!>ahK~oq_g_*HM&b&4pndK-NONjpBY^S;} z5KY}`Z9Tf3V5=#aZmm%5%4N?-!>mtL<YM(5`g^UUU1g-?>Vmu+$`<SI3B$P5qd$PM zPwDGlil3^x+k)3bdOP)fBZdp_))=a+Pa4Vl8G!@6(%W?Epnm}BcDnAd`itvr`y@u7 z)r}31<h*cDKyobixDD$C`w^EQ*3|RHvz_BjZb~F|G*d)H`RQ7H;X5Ho<-}AozySyB zu{K&PH;%v76%1Q&D}?ulV@6oLPCOYbMVY{m^kWAAa){Nw^;9+|I42-2i8@#;^Bvcg zjVvdKOHDXX0?!LgHyX|JR5)Hos+eRY0>0Q>*b?W&Txmn;nDP|8s?3Xw`!vc#Sz=ob zvarbfirO3O+hv<^e|<*X56l8yPb!Pe+K6>->H+YKJaI}DNN(c?UbD8w8QdI?#jU&6 zA*WPjy@YRf=o*m}lE|@m543`a?SjBwskElFcP^%)%p|2^3GR$c%SN(s&vHk7yV=^T zZL-?dpx+I+<m|yDOCaWZu5KW_N4S!R@Q^S54y1Bn&46eb`3Bk1Z<OUR9ceFZaRD$r z=WkCM1<n0*^>i)LwR9=P{Xnj8u7Maj%iBVZtl6b(MjF|=Gi`fxcZd{6p(P;I*kBS@ z1+1^uzY9bjfXEXZzFI`3Ud=BSfBDo?(psOZF%$tmP6Gyo9W-8uJT2T#kTr+ZaIzAW zgZZA}QVfgssS2caQyTbQC4^arVoOI)>ddJ;?>z}7p#FqOj^;7PRLQSLC0#PQW(=Wi z2`{fs&hzL_+Q*{I`SUj2F}Umqri5NLO$+R9SXb=H^i$2<LhfLcYHY2WHR`RWR$7>A z8f~23oiY{S4YZI!l2jX7s39rrDB{lbWb`+TRLL%Bf<EZGP5ibup2Ks8$8G}La?u?6 zQWD)^@D+429j>|-GOUiIA9*;8vHHAM-cdWtwCud{{n_Cfxmf4kM{cK7C*<oG16&c; zV)TQnS`v9x6R$#*;$>y)S;rmOf+eKb>+Q&*=n-m!S<x<t-hlaW<q$%tjsK=KoLmuX z`^*R~^>10lDLr79X>%C`x?3%jLdUS&P%W^5TAUE{GA+WAX=__mB+aUQNs}$m&Dvg8 zIl(S%Z7AmGq7=7#c&Jiyc!0Ej|AA}_1lyKJ3=SlhVW!WF)=Z=1FdXWG!7Y9oD3o(( zLi&An07^;Z0CyZe_#pB*8Y%o~4p;IuHw&A@h9n%3KC1|%5j1I{kz8dKKlDeDm^Lap zkIo-JQVsoH;G_Lhzw&5871}0P9DX_m;y|<_=)nSEz=sz7xF5p<b_}ar-0uPVaqo&v zEOI!ZG5ZCnLtw!-wt$T&X7;cuno^p#DS#q*(h^$=u7sVIaS5T@$+^DP8?Cc@)DOQg z!OlFzoWUO*#e`!K{E22GZV;vl)sGlbDiD+8Z#f%_C47aT{|Bc)Sie$`VkX&l{9?5Z zELOhOJMFGb@U+qVmZ^lv3EHdqMEpvx!ppk$7EE+)sl4(*?uQ=?7k-w=zf&!C7VuMS zQtU2lvESVb<_Qq^%7*Z_5`s}b6(PLijYSkhgvt^!35qF>!D+>Qp`~prB(ouf;PZrw zIf*?qDcoBr@3gZG5&Ia;bfCotV4F2;MAMc+;MvDhcx)mx%G_42S-aL)(cBe&R`mu_ zLBAnls*OlxV#;iDfVnbs4^NP?r%Fa$k_lM*GGf?*HzF&)+x3?^%ZB2vBS=p?*pMT8 zmjuNHOw<<Q!!1fTfubT!_sR){=70Lr+3#k)h|dFO6HRI5e_1uA+kmz+q$0<hIj3*t z)0-YEK4)8N>hk>B`iR=))Ix<Ru;q{Q1=8#XQPoblzG{~BGNgoXkqwrMcFU{g1=lw> z)zfBBMq%fpo<|p(0VkFnd_#1BI!*&Z;Hk4ZJAPUuCgZg!$<a}^p-ezpS4vS&wpfp8 zYZm3mys~CA?JX_23t~UrQ>NI9o!2!b(~dtG?hj7vRF{rIm|fOjI#$krg><;@yj!Om zuj2gUj5yN%-Z+cLUVka0(`u%2N7Gb7mV@3UW87jnEuhP{TY0w&p)J`6nA!4E6ywrG zSs~d0d9sPek|ryuom}U>GU|%3@+x%LHmYddQH24R%_3s1Z;H69gWnh1q8y)s(c@6~ z%48H0OV2jTX7h~`T$X%Maf+CoI4)8|a~DrH94#$I94FA+C24S7l)+S4gvKIOgt(=r zAgo<I8OHSt%{^Hy;?U6+Y@Hba$WXK?INTZtQDdcCM9)u-xq84gE+*bvHpSa4CIbzv zoRh}oxPuyA3WI^Ds=9^KM$ngra&wNJl!0iDk4(&vO~(})b>$9Du3S#Cj}5j>%N8Qn zDcG^5y*@*&n3Oe)=9+y_W21>+pyFieCoOA_!<|YExubOu9W_wKk331hVnnEiUwE=| z;h`3tCrQeBd6O9Z0N0nXrJqo8TsKZ69;&|Dwon^grhuWTxCutv6V@XQK$9t`ZZ|C- z2@Sn6GFUt>y`>v;p4LpL%<%sJ?`u!w4%J}6p|?8_jOPc(EnikE=d<#(F`h$ldCL?) z(sPzoMTCwP&pFhuZMHffzqMxCxdO(Ypvx=!SZng@#Z|e|JEdfNlkEJ*p}AkFSa!45 zWP?Qe%IOeEX5PQ7#EBm?L+s<}vOBty4>^ThD|Cw}alm~r)x^b+sI5rdMDeK{R=w7V z$j%2H@k<qRe}bkWZG>2ho8hN+TvQ3WtVpU##><jZ_6Ewn6EgLUD-B)hxs%mT2(zfU z)GTUAv{ZW0VI(Vf6B?)w_iv8h=+vK=5AiWg1td-GZ6twi8X~^JY(kABSC?swqwL9T z7$@Y96+<=CP*K@v4U#J(vyCER2E#JOLpHNw1L0Q$#}=KUM<^#NK~|B%)-tO)OMyX; zAiZ3p+Db~eDH0-yX6lJ56Az80E&X!MYVa|l>Av-(sjH<cBU;{FX-`zs+M`7?bnNP+ zpN~gvmsuA&k_q!}sU!<iN0Z55F1JY`i0(_U>d8sO>us{hyKfau+Tm}fM-`~~G8Ig> zAjYbMa_eekxoHW5YO$1p*HF$&Me^DsWdhwK0FB8<mm+^Oc@?;x9lGc-VCgNXG|E>& z=jT`zR&IA1$<yYOEpO7t5V*ZXQu~tP#fy=bB;<f>-Fd{t#NQfu_DSnE`14Y7GB|4v zg-sc!U=b^aJv=dMgB+@9Qcj;_vR;{Y9H*AiHXV?AGWxc4_Gm)Xq%7sWZ=3%Bp?(CZ zi?(lu`f!Nw^sH|yprxj~gt4W|azmTu`O7(C{5H+g7<`#J4NB};EW8f4i;3|})Zdi@ z@VjK=E-_nGCYs@mw41udOAXc#?3?DGg~$>k#4tg#kpyC~Ure!M)p?jJisW_9yfi{~ zlc?o`YK#kPib)OSyolKnu1J6{CWz8(i!Cu7kwQZFE(rHsxB2kXahs&zN=Y@NSbAlt z2VHHarp}HKwL?VI>lS9prvCCCfUtb6bc~pBqBcz=i5yKAgQ87}N8(TUmYNZJs<5b} zJC!<VBR(QIz%Y@a6SiPhH0aVZfZ+h+Y1()pw{zxNnBZ5)O0b1qvc|&g%^O<fYcpC8 zSLiB3n55FgE#h1cD(f1f2+aMThitn~o-pLEX+K{4kpp}H`J4j<R9ue164S<Qp$9}o zCpMUqhZ107J`0?|aZnc|0PQ44lknRtG$z!^cV<C?*IDw$9^{Fef2Vvz-waf9mKSyQ z%~4kAs*>btmmXy3wrkOuK$B6yrf7Kts%KijrFUm*9~?Mx0Xj~m38<}prm~1N8oE5J z^DXC^Z4=Hlo^8DI1Gb}e4s18XVx!2pA$L}X<`&mwkRYV~=;DSlsgAxVatoU_C}{O< zvr!y@A{_aL0y>a46mj!u8zT8SL%OogBT?~|Vk@r%WpjfW#k^_98dABkP>F9YGm!N} zH&96lJ}S6KIc;4I!X<^3j-Qtj{^I6a+J?tY&vRS9Zq-}W{Tpu+Csx?cA+(Wb)TqoX z#~e3mr3gb|2RaRwO6oLVuL9N2dEAC1Ez7b-1vN9ApRpNDA|CNNsFB^~(GqMY*!Cg5 zioSN<jdAB^fmRc2%cXH#$K7qqPyYbVuuOxpLA-?~NjuVhw?oT)`u<6lmMy`C9wE&a z>|YmJioK?LN$yR$B<(~-rA@A7UX3mk2?$YX*^M2P#$V~9vfn&IaWl<6I2MlzwQiUe zptu$Sxcsff_mv!I(8hZki5eo2qAW)jsa#5;pXpWjBJ8e@Cb?%URa|@#z`LqeXFna3 zjM=b>CM!}5O_Ey|P05c)X_I-T<Q!pmWh9pzmnKgKtPl`T_P~%}RC9u5dWvnpcjB4b zYex;q)CO)r<+46Yyf(W&_Yh<{itC~|B0=tx2ymt<kv^!*v^QV!2cDkuXH}>tMp5aT zrHh*`aLyROCo&(KWX<z@EK<dD<<dHUy5AFAGRKQ%?_ooj2U`JF?Q7uIzc)@nkyjH- z*j8EUvzGMsY4v-TZd698iwF`!_YQeza(N7P_ZwMJ1=#YOemokx1{Nf@9fwbA7}92% zLy_eQB*Y}<!`wlMkcK;Q*>r&(Od@ba+X!G1Cp_UkPww-Qfev+`g*EfgWeK+BZJ45% zPT8`xiH%EhsiovI6{!}22PERpV(Uf9M#&szx-R)I+Cu#PWP){DE7EO3Z5s&>XF7sI z@4j+O-yJ+f#XYfH#^*Q~mVKU>2aqIu&ha+w7%*1I%;B}#ukE6C_^vO-uI<0_x<`Ur zrpUQ%8M4c{qatNI$nl<H)JAM;2#n1s^?gCHfcbibYbeCVctClyf6KP7*ltD3IcAZu zD2{|W^KHub%Zpk9G2k#w&!L2GQ^a;N4>|y@CnIdQgZ=ZX(+QIkc<u{=P{fv85^Uu( zeGVchTdpfX$^tfyLoQ7!X33i-Dtr*O_-%`eg@(BEoI*U88+N8}O|MnW@zqpPb9I(P zvELqTI^v+~<5isjfe?)U08N#ZJTT<Qb@@J8OO(~|CBvxE>`F9(tiS4l3UN+T^p!EZ zy|N@$nMT5*Q%2j>cCbP%vIK)=n>2EDMf8-3r)(NyO+j`v%#rqIO>OBW%&sR28DrIz z#wkl##w2>C7f9U1cA9#8(c?Pvx}F+Gb|YrY`p8k8C;YNGWg+Nqy{hgM3Rug97urg8 zm-SDb<xYj}wMB)bcu<p5LvT*&?Z8Fsq`0M=`#k0$_9wHgh~h49hM3dj+Q+dHNh$<^ z6h+nn1uKaVMN@Br;`~<;cr^Q*oN%+Qsh()Jc+tkK>D+DKhRK^^V&dZGGfe9r5?n=H z;8;BnYHrG0QI{`yGGz-aA0_|;kRL>f%-0uUz%A9)?=(39Ml~p{kf=HPl3NuSZE|i~ zC?tZrIkIG5m5RF|7Z*Ex7dFm0x5WEm<D?=sR?6$Gmv0ZPg+~d&9O|}6?1;$guZXsa ziZeu+cT~caCm^AoITa%!66~v;WlXPDN@kqfr6oyB7>+2Z@<5PXT|fkpoD%f;4Rc0o zS|=m6tc(2Bwgt`|Hy06;MU4E8zM{W8ao6E5KsI&N6?2Z#9bQeMs<SCT+ptLu){Jj0 zs^p7OORD+liB2-z&7^l^%m`Cp)=*ZdMR%@Ia!U4fu{Rpi(Pc|THjbp2Nke4Y2zJKf zVBs!r@Ll$Ep6kzIQpF{~ECb&<B&e~=IFPDIE+Bj4XTDeu;|^|!zXksQKn~?PLI@J$ zu5EzekdEqVA1vm#YC!Rkx5I4P3m4(C;^OA`!^SzMC7gNYXo%ygxgF6%cjYN*v4b09 zuKdx*e0Xu$2#$~e3W$O-yq<R^3iMyyREr!{T;Gg#&-Y=w)Q`4pn_{MI@Lz!@7rvsF zt+J=KF(Z75CD_s-N*OU1d~gu5XopTKicb=8T}e5nTY)*QwMZbzu~k_u9{6UM>%<t$ zo!+N*8b*2rdf?W=;`ksPX6Yt#)x-}QOASGJu&5{1YaS-mn7ed#OzoFb{Gcyj-5dz! zP+dKVC4-EkB+J<Jmid!iAZk)IA_mJQ;bSa`3B@T0wt)p6v_@GZcz{}|=D8JqSxec( zaNHCKkO`N_+u1NxGj74UXHyYz2Ye>eI!L&&x5pu-9B%OAY>MAy5!vz1d{kW97<=Z5 z;-N%;bZyP?F@MnG!q`0NIBqz*6oJ|6?v0E$k;G2vAad=FoXlKI-wPMwxLN)|ep>Q$ zUKf#BAl+?bd3obCr<!fTTqXHcK4u9vf4Wb1jD}q$6-01i#p8r~nKXvns-@msXBsAj z3E5}I8$G1dekb}@7vQ)_y6Q9DQ@IILvNM|=jp`K_e0dF0OK$Lk(+uinwP798^>aML zOD3t|s}c>_F%376!ts#$40_U3T0;e{a$AUAb;U@Iqs+&8J0|svXB1_FitDn_;j=B! zA$UmSx`kUM9A}nqn!WK^s_xiNHV1IqMIN%qoL%?EG{j?ip0*SvlUmtIlJ3kUgCK(- zAIU5@;zxQ*1ya#6Ot&gEg|9PeG*+QUi|s4gDvD3)w3s_=O@b*4YTbiyBd~|v5pY^@ za3eW}*`^4t>fok0=;AgjfIt{-s^Tul>M10K)Ul=yW}0wT8>*Qd%w_Ct&Hf6QtB$yW zFFHn~SiDO$Bkso&G9uGfJyw|RkL|vtV|KUA@jLtr7vQUlikbfaX4fhn;W4Fs{EJa5 zsj1ufp-qZRLz>I3I=?H6wl*ij{*vPJebpw$Ba5)OoiH!nSGcC!##}HokpWqHQ@^?j zB`TA=RsR4=;`|pEE{~MwlOZ?=4ZKC39!@oEgmOn^uwHCLL=hitS`N9Uq5889MU*I0 zz#Eqo@ZS_mMp`*lv~O~xSfNQG6X<Cb@yBN@Sul5fhZ;@M1(R@qnXHg27R|)!Q?vyt zWQ32Ny7uvIe{RA_NSaHwSt<@#I*%~#H-aPv8E(SWsd&94ZnjjBg)PmQuYmmQn%IdP zj)+vqhU7-fyzsmx51SF(i6vf_9db{!!XPIt2?C>0PKqO}<0F#w6HQtu$+M_pASpEs zy@Dv`L=zoAOps`Z>)pUemu-;yDi>!ROj!!6@)0&7n1YxcmOGfq<aaPvbSyR7B)`5I zsHtR!5wX3oSuW)y&o44Gfb!^yLt`Bsj$)#FVD`nsUg}xjHpNuJUGRRP_i55;+8dB3 zSdQ|$bqOTcPL~Dv@MAF@l`Yj{DyX;ETEL1bQ(yrDt@KhUA%ZipbkR`{hCHZwEv3r{ zrjA7A=O}*}f6~7d!(D>Jxk`BxBBE~E=ZB<AZsIEBmm@Z|mJNdq{HoteBntFi0cKCi zOJ>UgFv}*qB5popQ5R)_BWQ49&z+Luiw#L9>)mDP@<o`3A+oF`$x$41s$-;hkHM8K z$kj6;oaUKxAB5?-201djZHKa?@K+@HpD}9HxwAr(q_i>SFgYsMsIgq-8RI7;X}Pi4 zovC5B9qFd3#W9C4alD3zXJT<<1!RU^of{;lYa>wxxVA06+koqQ(49nhEN-L9X+<z4 z3TK^6a(ZLdIP8Kv2Den$QCwWzabCk+@mvu=cuONganX7NkVcHt7;w2qHO3F-VE04z zkn-nZ#S}zzc&?zaQAq-+@b8*x(PTHcqi3?E%x;{$li+2W=N<Fq95+)pRm2s}`=a7y zJfV*|)@uYd3_9~L;}M#$dDG#Ii5?q7n1*AGJ-Hi*xnjbY#Eeag5e>yiJVo{~MMXgn zvr$5OqYXc~pkM8=7j;}*-{M%Sl1pYJp6@x|BYOdM@crZ)wG`Rb(Z_8gI<?&~LN$!q zEYOZN>WcC^mjm)gnvLO~Rb|3%MvAL+jy-B~7WFDoW#+Ou^XulEx$iw4q|wn}zf*Y3 zW*?a`qnB3bm{(axh;yzYJ~jo!Y1JJ)Vok9fcFq1-@|_v+AbBW9w?Rn?Bk@5QRdoU> zs2Dg-$c#cbZahmT?N`hQ!ZyjXcdmijTlJfwk0_QlO8RXR_S4iRN@kf@Y3Ok`pBeVY ztUQ}kAvDD`!aVM{4dSXM5#k=?>Z%VsdHy2o!piefdZn;d&M1;SSlXwj@o^a9jbk|L zsEAw(0TFYOa+>J2)Z+6t6Jn184kwlK4lI}x5=3%MlToxP&s{NJRm4?X5Z@hf2S0Gs zV-|G8Q%`B>)~(B6*?AWQai>%c6}*{=20<%r3vHqkOOnia#c)R);;7_)LZ7xy8g4?0 z2#ynx1VmTa#(67|2N97>eUNZ2D?GH#HupBqb<TOV65@Op33Gg$zuQP(nG=#UzbLP5 zCnXuYBvlhOM=u{_YicL?%J#>kx2T>*-PX~rwoNW^QyEo8sa$EuVAwRx0=TQF-s`_@ zr=P8Ate3AKl^UwEu#9qSXRDDPH)qiCAhnL^H9&9rt(cOrOK}mhy_oa%25FBM4C3pz zJ<+$rh(}$PUuK+dj8>ff-U7CG(6~7n&S5L;v?{pRCk~%^p7c^d%{Ln)r)vgL#150B z>k)+SVzAy;yviM{ac0H6(;PfA#6?^N@q1yG5herQxZ?bTUo^Kj#+gzmjB(0m{zjeM zH%=xg%)_&5#F+Mw+O7~DY*<K1nu6PaRIc-E;gF?^JWJuF?K);>%rzM*ML=9_w9l0= zoz5tv0D_N8Oj9z=F>7%Q?_tP|FB1`SMX+a(*+faP95FzAa!*+em3JGEdwI=}lw4ki zVGooz39FkUE^<OlB;5#h#a{ODQPy<xK2-_jQTaerl$URY_^XDQ)Yz;Me5{4o;c0T0 z;rRB!%9JviKSveI7F$+k8<AOR{lLOSCN(Q!j1-RS^<9jyq^inhM0L}ag9XbgCoC)F z$g+!A)YlE4T9E2j`GaubTbM_tyJY0lT}fD4c57!>)q>oa@?08b-H9qyfDj~R(#BlF zku^(YwwOTMZj<WCZZm^t1F^rkgwMl>yrUqBJj?FwR=k^=_F{X7vi(siLdp87*>w)a z_L1#mza%+GQkHBb%L#8eWorgyPELst%rM&=T-&$?dt<I`hI`|i<6Eol`zt0`^C!+L zU4)QxL^Bq3%4zymhtlY_8X89z^!-jbr>MsptfL$ny0;N%t1;RtK|-X{YLa|C(X!r$ z;l7cMax{*cCg1SuLpq<*oezcKZiF0f!(9m5fu(fgVKYT(8A<&y(Ruwi(2I%Tu7)N4 zmuS3S4D>^IUK?n4RXixr_?{PNl*;W7q$;1Y(b^ux(eSTB5`RkcMt@5*PF85$JqfX_ zggBv9i4K^F$_lelq^(TyG^w7QN@|qV$%kKD0x>FgI$GsasDZO}u*W=%Wyb=cyhiCA zUq-LPqO2q>C#J#lqSL5JMMP!#a-$bcQ|;)Qdp#3RXQ65JQ`2;aC8u=JU}?QPxjRMa zdF{chsIISb!<`oFkLk9L$hr$l#(#`41xnS#k5|0yJy9tKMCrl9(fWU<J4I=Sp`V8O z1x>FF^g5(p5A?)l?L0lv(jRBT?G)O6hSGq@X>BY}8t}(N@DB|1Sm07=mRa<jJ)ftl zv(PIsDJzo}lDTu!D6`S1Oop1Frx!}l5tftEim9YDu`G`cG@I)m!<{1?wzOuB7LP;d z=g<0cq;PyU(tS5H7K%35@asc{e@rxUhTn!7Niz3M*V0Rw_Tv3P?5XoVAlND?F1-9? z`DA?-J9MEm)NA`Hp&2J$%aZ3-osOSsxMHigLAvn7?T$HyTi}K`);5Tb7Ro+f762|3 zQ9zi-1@PfZ^>yd*`zC4kx^uHqJ<0u(G|%H^qko*99jM;nBvp34E#d5#W(~J0K}dR@ z3K?HyL{Bz<N+N98lV-!>^Eb)+RY;zpmKLU_zWZfsWaQ6-Y1+<5LI{THn~5^=Jiz0K zomA|#@{&|dN&IzsC#V)CZi$w6FG)SSsO+#T<U}@|ZPv9aCz#nYwpQCc9m$`Fh?{Jg z4{teh{BFsRWUOf@Nt%;ooj^aoiJFV(*)!$Hwi4qMDUHfy#n^8oe{6%kSHIAhMr)V< z0CTo%neyhJ{w_=C#BX&V*<eZY5V!aj(ukPTohMXL#JJs<8zxhy`+_d(aU-Vz2X$fI zlgOy3LH(*CZQB}g#)_K7WwEZmkeHvf7wQa5)4o&Iq9LX1f1+2@AP~`r$n2PpSjuHA zMG}h1xU;Px`5DH!wr!4K_rV+y?EX692?WH*?<(UeoP4^GO^!T#JO2R7uKxf5hWf8E z>a^&p5?DXhnIW!|+H3LEQ8G)){DF)9#LKGF+ov19#LJ1DLx&Wkz9J$w*%R{3epL2- zm1wCYadP|ZRevF1^0f~tVK&=8^+@c4nA&bbnwm#yc6zo`ndr^auk(nBj{Vo8lWkB< z=u6bU$JhS=AXu4r^0!-*(3_7EbT*~2-7`(Rr595EMNjftfwnx7`#Ha+CRERY)CQ~| zI_1alfQY91Cd<^ax{O|~^7P7YB$J6|p!S3L&oIM-Wf3CGqjf@ZA}U-v&UI97u>Syx z`3CGRik4lpfn2`Pn?z|56E!4`F<`aTn{dxc%S}k!J_Ypu0EgP?l^gw9?MnXct6N8> zP3#)YLjM3MG_3U|7$_Y}-B>RY3kmH&LY4MN%1_f<`OS0ul+^vG-%w`V*>Xcq>G`ZZ zT^rhB{Hi?Mfa3{h1OQr;T8(Cm!9}omr%{)`!NtPCOxq-$%KOMThN|RLRXEacByt(< zCm#;i{{Wa>{sbl3ZIc}t(PwQ#0gklGKjd`Z<J`#T*MEfai3@GAsz+A7@9_Q<vQQn% z?1}kj^ry4&y43<iD`_BKg;o58&)~1Kn|f+4F8eq3+Fs+9k~;JB=R_RT2-3`=k<zeC zckaiB=JfVhO!IVg{{YAqCTdT@UuHhf`G0P{nD^AbklTjYy!-DJbQdMRFYy!DSbT=2 zJb|*f{xLqSLld-F1jF}4B_~XNT!8p$4`@G}sGI5~MX;++_Xl4Vn<64ALuHtgfC6Tp zc~o2abqDdQ?9bWD$sJo71ZgH}E{UB?j)jN7zLfT`WqgL3duObl>rS+1qY8`pMfq{} zmFMWmmZRFBdD<7a$S<Kif>e-iC}FD&YoFw0qI8-1n@^W1W4iUPqqP8-$)3(g80Xeu zsZKKK7>U_w_Eb31O=-nng8MkQiMoJxadKO`B!@S~e(Vw5T>=G-Da>&<O#q6T&hPLd zeqN$y_Yw0*+HsxaS$`p>{{SCSKfC$c;Yjxv?*9M}Eo<8HXsU}2N=T1=kv}Z{l=gx6 zHZgp>o}qNA{{WDj%DodQ?HESwJc>bIX8zdyQ>1x#Ioms6kqJk5U1i_9L;O|rE`-5! z(W?Id&s<EnVP3DwduZhveY{2)%{sz_Elc?j83(3~C19vF05bmo5i+7|R_oGyB3qB+ zKg+FyjXue;@i;^}WAe#2LLHX>0NNP{%zEavp_L%yNTX#{`?t77&e_{5U+7M6+fe@i z7{1K?nGXvrDyc7Q?eK#m%tOLYl|7A~uA7S><G%@xw7pOD(SIp+{{Tu3cFx$t%FE=a zpq|y0(I1M;7;LKv+fY>vm8Inbh-fHW;YYNV<4`JV)=sjTq7pi`5%NlQvrpNS(q&wi zJf#)Vv14^wv23GrjQ7C(i+#W_i)3)JadtM<R9o3=$vDq(1X+`_(f<H&G(3JfvgsGd z#6t6T4E1aHmQgd)ud)*L4L906w_JW!pJ964vh|;tlQy2Z?+=M#qHQ)QF(Rr-Ga~gD zA_;l3`cO+~ABAZbuNR4BS{I+o3X&r*FjU(bg)!_<QAYl~Nbz)qrwp5D0l9I-=)6xf z<LXe|6Y9}#>2`3^R6Ylg1ibqEY^dBrFfdU~@&zP^hy+A+Dk{jT67?^Hzr;a3xRyi` z^OqmS@jcht@-HCbSwf}~I<98wxqgec62xuQ$kh^DRv8Au<-$3&G1c20lP}ok2Z?2C z{b+alt!jzv&)L*35X0hm5-Kwk*=)d?a7#_TT`lmwloQ&i5o;b`+Ivn%RhOyJROpbB z(@{F5CNl)=l~(f6v33^{%GbJ;$R)Kd=?C3gMB}OYZR#SlwF!weUUFocXW8^A>X9m> zLs$-Tv5LK<E?ReHwS|{0`=33HIQcgz-%-=iD_c;_@Rv5t_QEW2drBTl(@Z2pRwZy5 zbGbGSY~^f_<D(yKbvIr<d6@Xy>@IafI*sR4GpHHV9DVjtI`Om2Mod>)At#s|i6Mo_ z4>g`@E_tZ9l49@FX;XOl$&B7UZy!0cifv~mv#8EBB03T)DV;#cCf^9}#wQSmIgWB1 z<T=SRnCDb8s@`=2I-!c_XIZRA8_q$BZSJPpp>RwWS;RK;s2S7@SL3F6sJM^ET=P-$ z%rl*4v#8#6Tf&nWGp@!rA-pZgOq&w@p$@Ako^CcHjgphGkdel)<TMD|qD#mzUTZwV zC~82N$Z$y&IX9g^&Y<To&S9LheED;?!BsP;In*5UOrrUwR}M^k^D(o`Mb9x87F=BO zQFG13&ovhrHV8!9;j3YFhPml=bqK9Kh~Q^ba_UDK`jsP_8`?*;x7gCmHdc&8kru~i zcB#FOUoqSE8wGPZnbgjBX1J!f;p5A??XXBam0T^=Nq!NekYh4>sBJW&;|;3P?1<7Q z1=|B`L`-Ohj<S&1K5nv^zfsDV1Q6}&226M`QWG{#I`az9qV&Te03?>5Mq>V@XFpQ0 ziKv=f_4e^^8(dq4ac*Pdw-NG1n`W98-RlU?-bHM!EaS+Jr6R=}Bt+rc2+=WRfTGA! z&JG@V6<inri4+qYZLmX)%}x_YjkX9ZZIeY2(p4sI67tBSz;YT!<HV6RzBxk~sO~&a zblfM!YFe&F^w2qpOid(~rZ<OSXxf$an5>7Mb>Wiw7LU}BYl_=k&>TuTC4Mxg*^<~W zjf84?#t*K+Lu#>?8yeQbHM{|KjuT8FOj6-6TSr!~f=$EX%`;@378KEhY?lHH<%3y? zT^TT00nRIn!7!x896KSyn;@{cmMeL~y@fOh!DFPQ!^;3fUL%XcKW_=PO9_^<rrP6i z`J8%|>nZ`MIU+}1G0h^xmY|+x)y0(RXPrC=-9wF%SLKjk2O`@}8}@PQACUZ_2A8gw zvW-3ptDLiZ_rpcP<%rs2sgE+^szW?Ti-#LzR>=w>5aPPLMR!W0`I$eIiyJK8IT8k@ zhHj!qRmR!iRa1rdEwj^To>vvuMZA<~a&idrlmon32!P@*a8X282NmpBaa_YJ7Rzlj zOEg`*(uU@BXQ61=T?q=mwujxo<RrnXj1I^=aef9!B8aXT2ntDavuLU!yb4nCQ{;_B zTUiPnUaT_3_LRySax27M2PVhGnPnhjURfaeyy6pnYpdl5iR$>ftz$VNGYz}2E2Xj> ze6Uz(qSJ{Ool6?)P*^T>Xd$(;WZG@5-s4q3*G-*cA^45mJqYbC`Na#{mghu{im;Qm za$M$l&CKR`)N?wn=OtX<H%#3#bk1p<(*c7#^5LE!2!-k{F54d*B2CDZaWli}Vt&GU zZ;wYscgX^(Lv1i(hc@D;AdYTKY<Z5KU{%%=Bzsz-bX|<kS(xqecDV~AS0W@Dho##o zX8!;k@k=(}6ccWi>(FmHL5kSpfH~1k#Ss(y3ihr&jyt`oD5cplIyTKPbe)bSlb@3` zHG{%_6y9@R3bh7O_;qa`8PHKtXZU+5A9uq`qjGnH*Df#gb5do0gjejo)`GPWu<)|+ z*T=$*L!Z*kL5tyDq0i|?q0i|)p~di*QQ&w>ewbbqY8|NbcA|tbG&Z4gSUsbK?1!UU zibY2$MKt`(8YyUrr=@cADH-Upv(e>ZQ^?0?<(>^3p+cgTxb~QwSjL;0819;$M0!%e zaI}?*f#~a=AE7R}F{A9m6HHFobiDkPp{(38@c!om!|Q|q@aE|;tpRK~`V!Z2&{j)@ z;q|ub;mz9Qj|*?jTJWb(BhGkDs6tB~6Y8~-FT!nB`^@-LtQp6AF4gY0L*cfgfcrEu zgbs>Qrlim}c2Z7~+?~DC7xSvx&^ID?+7@+#O?Y2+HPy5hEW>9_S^AIZ>AC0=Gtg#d zpiITn^E34P-2Fc<Z`0E=^z6L-IV(R-%Fol1v7J0%i%9-@8~LkzI=8a4{*me&{*h`d zUkCLX=k#|`=BLp1nstiG=@v7h!i@?%)b8)N{m=f9!1|gktYG&FF2u%2_O$y>{rG9T zmmz{I$mzC@f-s=@tQkpzAv3Bu)f^`=WG*)D`PmXol^h8KxUa6p_H~yD<-k1u01%gD zjD56QqTN^O7<u}N9y5Cp*Ow+rVm=k~8?&e)WLa_KHt*9e);xFC`YPbvLtKpGPX-rs z!Z8tc?jWjPGm-2fzNK{-N+p9bRTXgV;t9gAZd2JN?gIt-nT%rky~U9r7?1jv7BKj{ z&KV$w>R4G0TXL9n=9&j}Br#Fck(FXtLz9kb2w>;xD0%vd9tHeDb$b)nuv%2qtYmwd z_msX9CbbYo`XZNn_4t_G>@#yF%x)`e+(vcf#r8P?9j9;Mk^1CJZrTZ`ugfmUm~F14 zg`U3zk-G@bsymXdBS(=Q*@<qaaenSJcVXMHG2D@T0isPx#_{f}-d(ay+y)EvGaIpa zT<XYp8DsS%Ed56dBVNRL`Lf%J$Ld&E1;`|QSt8OSCF{)Y-NYl$8HqhFMA4K8NA=BK z@BP60iS|<m>sK0XWp>JyA9glKPS&4k-^hZBvdKAP)#&qj*p{zOEqda>aWl(4Il@9= zE9r>qxz8JOWhcH@)Kt4-l5NX%6yFt|!`VmajDI{r%_sa0GgCR{q0`tmE_9)d8P{BO z$kp<s445)w<Y_L(d8s1vH=oGGitLwU!jT^#N;uLt#ZQ$HRct1dhoi=Q^E!#bl(9%$ zx{Ji&$W|oS$wp#jbYCvT{fUZz5f|zEJ}~O!t%b+&nOXdHKlthGLHuRxlXi4CQY9O- zb|9L1DK?MN*^}~PJsrsL-hIZYtVP{JAjBGI-EgQZQ7WsR^%ceUzxyqH-d)$<DyDVf z-~KIs=kJ4-$ex>lQ83#fR9Y8_M^Gy808=>?Qy6SWgrmFGjc@#g59e@Qt-q1eUze!l zm@v~u<AtZJQ7Y>sdX4;}W*Ca9uAZ^u5~@niwf;95rm=ab6xJ-w)IY{AvmatcTE0xf z&qbZYE2R0ITS~=?uc~#_7kR$#eb)Z8`zh`J0AOA&LF2VY-Hw?feXTyrzmr^Q6P-x! zlxld4&()fe;svs_IIRvVBZ|P{#Z2;vix6|l4m%IC@!S>pu|;t`!#M6PrpL>RZ8`g} zOSX|;_7Cv_;uA3ATSJicq;YykoRmO7Wi*b)Or#|!7eAj&^DND`pB><7qIK0Q6HKyr zTd&HN(YQYcnHQRIvTes?IO}xrdXfcIT$bXwFHJBi;^Hn`e;JjF{@kUHuk0ta3jYAM z-7-zsMV9<$VqQgeHpI<C{EZj#Jd=Gw(y9TN99=?-jkw#PhxK3W(QEFm+?U==elhE1 zd3jgM{jGoJcECkdxJC5LYUmqa$lZ393ylV&+k|pKkzs2UV`K=*+zYhY&rkkp56Nr% zj+*?vO2HFAW(N~drZn?!H>j95A)=}*Jw{24NoDr!e~ieQff$Wncjn~k591fvkFg`( z-I8(^MZ0iPE3HXQK+>xZiG!e<wJ|<-(`gO;Y4%g-{{XQfEASKMsZZXIl0B_{%fFKq zc9CMkOygz5+!me>Pm_lY;lgCo38l^}oL4yFieU;aI~-RZiH<g-iMHW_Un{}c?e=9? ztpvVZ9J|nCRkNLLroL-4wJ7#%xVXOVk_##Ar_oX^=khHy>!zrvw5>+y?VIJAD!|I} z<p<+;9X=4EqcO$3jpX9?nl$O}h`De4W>;_N{BiO9g!Z6M_SsQm$ZA~-mhn+hbo|FT za?9o3!iV`1LEO+uB++B6y3y54IP%vGJKt^5Ywr;3OYagm$fzMvC#3bN>;C{d8X-iu zAzI9CmD6a!)?KBZ>#L|2a1l}`8*#JK?L%@#UjG0or~d#rew$z9bl2sOi44s-k1+Em zS2W{GRLQz@31mf)9N|lHEQ?>`GCd5Zn5?Fe7G3`U@r&%o*njS1x@O#25no`VNp>^W z<_8*2-E6x}Po7GDbl22BYk!me&lgPVU1`y){D{eteXTyzzmn4NJ6Ac_DyB4CfjF*l z70FFDNI;llwg95%OAV|hv0KL(MM+U`+Sx>~UGMcH$=R>23jDe$chmm>;;Z=Fwun+; zWJ{i#bFG#3(Fs-rPc07r0Qovk&bo`m*iy<f;$lOD0OKyQY`X%;qaj|$+PZs^oXfw% z!CkPvyU2>n*i#*f`f50vGkW9mE&l+F$hYjX8}&07Mvg;IY8C$gZF2HU630<R+I*yw zDutq1S$p~PKgkdx<*eu8%5M3rw6Y0l36geRaZR-fA~x6F_r>?C{0q3-IhFqaoq@LY z{{Y70H!<5{L+bnV4G~DxJ8cj1FCcXzH0zBy4qI}g>Nw;wV##tl{{X33M%{4Sb4|xl zYTn9BIW6jD6)o*rm@yeArCnG^h)rr{wzarsTMf2OIcPj;@+JFBMU8&b@-P1Yja+Y0 zups84V2ajL(c5nLGQ!oT<MC2k*7!n_0D{8qQ+~O>n$Ng>AN^(S$a9cOm4B5RBxR@D zck&T-M@dxoOliatODFLyT*s&L6yFS28^ToB*S`3ui-ef?_LSFiLLBO<EO$FT8=^Ew z<Q46pK%ArW8w`TUvk&1uk(mVLEbvgJoJ&_!#AC~9d9r5LRmzPi=7H9|7H(FtDTeW! zv4F&!I%$ZhQ9W|H0Y&&(Msh@dMVpJ9x5*LNkkpMkBv;Wa@&zP@q)uesh3FPJ=ri-s zY%oBQ7oktuv4<l>n@aPKdNqzQK`53qR%5lcTGdOFE~=(nX{(mig(#MydSi5xiC|YV z5OF=NV81N_l5n`vrz4}Dg$1H!B!&UzeHL~^sg~TVZ{Ed|dKaQj&qbY$Q9Hb7?KIWI z84^L7d7$|HGIaE=y-W)zZ@$Y<wIfc*74$niiKiEkh=|(N6vZkVG{*G(i7j<g5YpDQ zW`R&yqK2QdC9bD#8qQ_cq5ht-&(rof29|R(En_<-AL;uX8XE#tq@9)!{L>{(IWTGG zs@2uA9n02D?68_LZ6keizPaiB^IqZXpJ=nXIURS6S^05Cbd;T}llGnbkNWkP<PTS& zF)}|#WX>a;M>yvU<BNnCNs6ht8e^r8?YqO+o9@oyq4O!WoTDzwOQ<cYnjK@PRX<p2 zEyq{t91T}|x%#5-h4#DE(SffKHMaHI*HQVkytg2osBdF8Cthk(86G8TfG_HXxyr3` z;!UKg(A4d>>Jn2grfWf9{7cvEJN-`AIsH!88U0Sz8U0Sz7|)4X*ok~a)_rmDGgh}D ztw(O-WvDIoe^y@sM%9{y$J@HJ;Qi&oB&|!SgC5p8z;SiPw87T8&2Tk-uv~9cYPH6- z9;93iL3@dRb)LU-jp_?!#J#h}wGFCc+LHG_Q)<<Ho6SNk$kh6PF`Zj}SsPPo61A$; znxa+xzPHq|=V@JKs_idz=AzWlBd3X4*;o3Ft#Ld^*1P`zQFV_J@h4fPY29_JW*w%q z)Qm%O#9d&HQR-Wn74BZzqFrl^K^5!0Rj4A=(Q92(F2Qc?F>ft%*KM=cZS&V{^Ve;& z*KM&cZ50yo(>-$2J#x=IZqq$-&l%LS5WiDDpQ&HO)UNL%P}mXr?Y4U5#(L$pOVn2l zL(!VIP|XIsLDy0&t@Wn3ud!bcwZ0dK`sFY@PuCm8)UqXMx`pi%?oIlvKQ+7$aQjNV zkr7>q__e>45qnDAQpta9-@d;>%!-5RF!R+b_E&SMh*&wvF-bFY$6OJ|B&HktuQkIg zeOEV9!DG6y*{%0*wnhu*j(7#z2LW&wanw!j$UMn64oimYEppoje{6E*F-axHWo$*$ zB05M=^!+97xv`e^l$|N{YWK^|*K}<?AAChkz6|b)Z<@TOMCDVW<!|_&-o;YZ?xJ=v z-yUHX9fT`|UODOFm=MzfIwlKPft+)WDq#^~;rqEsWa#<JIP(qG)FZSCvDgupq*~>w z^}}r}&kd}+q1)TBBwY|{Ky0Jia&b7Do92aQs#>D8wkdfjz|#-!Sn+<{q{)RpSt>VQ zqa6=dR}&S)&S{-YQ_iP;X`J(xbIw`Gae!A{IB>T}aWlz8&nXB&6>(R$CEJl@>!dWA zNqRb0PB+xl+RJp*9h6*7;GaI7v3YCs+ucltzqvEI*?wUA3V!2%ZGM#BtrPL9`%8>i z>io8UZQr<GZ5-X6v&iF)%O(a^Bk6-7Gt8#PEfYNE45Zpa`{ySVfH%U~5X-x8w+XON z6g|)pP@7a!P0}!Y;GLT+62vTRalE|yOsk5uk$MrN$P%!6du+?CwMk6aSH|-+aQD2| z(p+w-Ge;NCb04cJV<=SSh$ph*BH|`S#obB_ITGg9K^Sn^9nRsTM(Nx6F;fUIo5F7i zyg<YZLCwA^g@uLN8Pv?M7T04kWXUEWS9*f{c>>_th{a2qy&>rOek?eS>N*!3kzrVI zng_dev#6_!nnD`CQGDH#a9wQ;jWSA+({l+8MRx06yn!D@`EyV_<hi=%d_i2|S;cdT z;s$ZV1kTt|q&c2eR(WZ7j7`FBNGELVn<gS<!z%SXdh~ob(lCGDwaBR5g71!T#oMn1 z{)6<;N+onJyEp2y{IOq@e(PUCBJuwK?Tz*zC-;$ZN7HHco%+k|#f;&_C~tCDkMTzr z5%teBS>}rs<-s*oS9s#Sgre$aT`|Y<5F?Joj+<FWOxt5@(+Lq#dn;(D<7m7=a90qq zwZ_*jzTFLCt~uGK<0qQ3eG&Q?Z<RBso74yUzPXO-)+;5;Ob;0kw60H(!=9M#%OHS< zIbsrIRZJmXdOo0{+@j=F0b6$%HtU<>-^EiDam2wM$T_wyE^UrsVPSh-K214Z<8Mfr z5w{qZ4C1**c70nn`q5Y^Zb)H?>iwOf`elunokA`qAg*y-(h_5_3s#&pXv{)Ib>v74 z)N5L4Ct)Gu)bQ-<Es?5<_E8uF&YEWw#6-=LHqJS=ZJTD;Tu}w+fZtv|BkX5gtgw5V zn`gE!9POKmrM$-CRA_=>+l_o1P7=dhHT~wyTY2?9v@foA)ob}l^pD<a=|V0${^Wkm z*QWC$<b_<bU)p=!ewZ(s)|<qlQcX|czr%dzwi}ALs=3uX7fT5HM4XMiUpzR?kl6#j zbyZPXcXV{Ir$>`paU|G<b9`xWrR_D5EQoD_D=6U0q8FChjrPVbMkbVHuDqyva;`p2 zqe)Gsj%ZLc&^Luf${UY@M!1eG*o&KCM-s&s1z~kMk`s%XJ;vR{HOE{vZd{uovVfnn z{)NXezhTA2T>k(not@9{Sh=cM`78R3$Vt3gmlMuMs;x%cuayG|R*%M;)5aGZN$cuB ziAtHW;J9Om>xG&fg?4_Vh|8xm(IndWA_wd120dRsw~)4O!BrJ|X8FEy&UwCa&RM>4 zOj6k+M@bC%6wgl2O)T`XL{Eoh#M==QblWf2o&JU)i)Z2#Q}RiKyW^j8Zb4UeQ>)@g zc?v_OsbWg6{{Vse?fVaO{{XW;Odrf=+DEmLe%s#5mBwpYOD`1XZ~m3vHqJP^CKITb zZ||FO$g*E!4%p2ZRV7xpPAUrEDvNxC;ydyed=<pNTw!u^Wzz+BcrKA9q9-G~wvVDZ z-~Rv^(`t!pe%+I(<C@Yk4c4sBQ`K%SM^-(MnQ$Q8v|}rZn39;_yyD|(Ta8TlPSMmu zhXEqBiyfgthaJb69wGjPTyxk90eceSu78sHPw7u(Udp}gE7_9Tu~@*)mhU|$Y!0(z z+<*pb-BEH|&Nz-*G%N$Q66ZG?YbF_HdT)T!%Zl5Xq$K%bxiTi9tk<Mh>1Xa3iQ!eD zrkl}`X!1s%dQHirt>;rZiJd~uYq~gCjuPil%;x5G9L~F$)-}d#9&NO<cM{l>7VWXc zxL|~Q$)LutT=LJ36Oh2X%p72l+_YX++IE(bd<{&aXQKz*QylxCs-vS5<c^)?ZMBEo z{{RE`n!z+c#{HmvV|gJyOE0$a{@VM;)~>^4b;jz(q?G)Wa5IqSZ0((~c;OhP9E40c z7_KVfcgE2%F;nJYF%2-$F_zGRRBe@|QNS(5J^ABDk(cIiu~hP*)CL7JR>wt#xEA9x zPf?c}Sx2*nb*D*z>bUAUv0j68%aajNVcl2C3baQXRFK(cmPNr~^vxy76*z(``=`x} zBJ&?@{wlaji{CfdTQ0=7tApKDK2L)C7rc#?qT!apsOFztr^9ou+<0|OO0$n#5{y%V z9|`t9I}a~W&~LkOjPMyY)&uS|c2*KbGfQ2|)q<X~vfkcIR7*#(riW=JuDXrlH`xJm zWzX`zSIxe1z-{xvi1Oh?B*26vi3Tp)HrnyNa|#6FPf28fx=OTG;YDo_Kye-7vl>UO zys+S_!5IM26$>h<g33(2KW7DWsXo=5RP8I;e{%l-1NWQwD*UeirIa7bXCyCcKeqSV zFRH@5*JKTBlwBuYBF9wm9y~|SJi+IfELjDH=`MWBh|yrMxGdneoG^pC35UJ`<N{7_ zwj=;J-s$jC#>>1#@3Cx+uIM?a+ERhXDG!x(XKP6xa^p_}uCye{N?@NVig9NIHji!% zoi_|oZF9?_D=#8unG<Z#NpMFtL`Z~*gh{^mtBSa#idaXTPVAl%;;uBAw_@x|^<n$S zBt~44+GU%aOs|-cWl>m2mNIRno;v&O#;>vSPNU*eq_R#{&!e!eCIERyj5|Erh>Yxy z-KzO!oM{#u5RD6>*5)FaVX!r_X4%Cz`7>e8*o3N0#FALB;(`i_4=GEV_5t}!gi%RV z6lSU<P~wfS7*DsJWp?ulsm*fk_-h%kv5g(x3J5%MbA1VFiit$vH7YCTwTyvfA{r}l z*Czd6;XdPj>Gb~q)zmLDUZ34CnI$_}C+)xXi|UkHJQ0vmqOdC<d2!DxiyJ(lu6Y-8 z)7-?Zd2uS8&rfkNA-jnY-14qxLChw}F{~}HM;ktw6trAv&zZjHe=4ctTjUh(*nFfY zi6YuNM*EFRiaKg@u1BJgUm6PSWg&iLL{yrO`N-^i?6!i1_T^WZw(1Z&$(6!wjNK7* ze(qv4nbR$}t~86yOKd~MEK<chQ^P!c-LfOdsHcd#;J7G<G)D>kN{hPx0F(EWsEtc_ zpOIk73~t$=g?yaG`4*xE+lNm702kCuCcuy}n%7gp$>`0hlR&&2L+Q9n)VA26_<0Hm z)G3a04CLNC?@{DbMCz_}7dnZYh>5tZY@!XP2h_S?CD$F)Ibk@%Ad2&f+-3yRY$X)J zKbxY`;<m<rZ^)ItT$@b2ux5PP?<nN1nX>y4)z>uza6qwMpsWc%+?MPR7Gp2E8hzeX zvdI-*lqWy@C)OWQDr`ye$#hlP{{YHoZKP#~+k5R7)dsznnY8kp5Q~aZCVD91QjR4! z;#7`$NMcojXQqZNXy>K|dRXVC4jowJip1IY{W5o!422R0M3F^vrn^Uf<#`-5ZKpSB zy|ND5xh()?>L!3@zT~+&UP(7B8%`F{5y%mHPk$7Z1e0<m*Ki`mPupyh%i1D^k1R~z zwudk75Jhut$}6zrtF|MV)a*{-zL;`_G0O(<{L{$LS~f#1^gyr-a!%;dn~wl40(bua zv!3!c;!v4fxP3@Z=<xZys)@c&YX?;hiBN3qWH9ZV<X32IL!m<#zDEU6USizE`GOHn zT@?;Q9X@D_h4PG<it#RPtBSZqjWAB*O^6|v3~V@;P?H3d@dvp`6e#h95q9`GWGH=1 z={>PB>I$^zhX;5I2g6et$J59Wnu`KoMn$4FwUFe&akd_K*mF(QICjvPg+~Gh7UiTR zS`;Sn@FG}>rDy&>$o<6jFSPM|wf_K+!eoTYAEEEGUsF=ZWFkazPpAQ}M027!(oE^5 zbdx$sojmF1PZSe4VmFG~W9+9JM$s|0M25l|jxivB+Is_KLLMa%0EC$o&OO>*11?^} zJ%e`@1O|v)df9<qmZ*^;J$zD-Q&)npx3$8MdCX>Hw1(g6%5G7oN0kkw-K|EPZTVo9 za?K0gVr`!;ZGx$fdnsW&qmIhkCt7sH+vf<NPiw6P#<^QL_4xGq?iO|tl2YC}Ij7JZ zi^N9(cW}O^J?H(c)M3jcR%EiFA3*^62#5C?qvoFDeHqU-@0oE4YPBGfO+{4Y5q3pi zmcdmEMl^0-C>9Guxi#*qh!~CMOjSt{6^MP1dl?rX0Tfd~L`PiCC6Zh;$ju_JEaW-W z3^PI@CBbkP5Zfma*3{g0awk=DR3NP+ryKJE2HO)Pz7Q)YQY>I??mb2uTh|;kZJlu) zk-OEPfvIm0B5GM@Aj$!vdwZ&V)W!N<RO!I`>?-5_)*nm1Y`&_^J5@E7YHJ$KgiPa{ zbB=ABXB^u*d=)c}aoY@$G53>eXAx0?UNhbf4ytuyIoYRiQ<6t?n<RNj5pi7HU)<CP zK<(TPdP);wjiT_dgcBjt=~rKN#gGOd$Ldb2uLf)H;sHmtZ3b?S7->Mtj{2!KxJ*U( zXf^)#ZSfH{-7&VDJa{<c<FctD<B-7eZ9hDjPA#EDRBx#BXv?xq*=YX&PB0%Qm^58D z%C`aS_Kcr1pIg52T>we;y+N=<ZYA4X2SCRrw6Hh*)~66xZ3l_Zh@PLPllxB+T2D@3 zyTl~p$#$Gdu6S}NRw#|{VIdn74C&^{f<l292dnboY%MeivIagSw)x#~kj(h(p4q-S zqPe?!3A7>A4TaELM?Y|sl}Qy!Y;=<m=)}vc4*1POPZQE&G!0CU?llTp<%C3Bt89+G zV(>`Ll7ztp5{}<nnCDXw@7(H^a&d9hoMBzfUT3yQ_Mh}Q(ouQFQFQEg8?EV%+m|h~ zY}+=?u~QX39q|V?+Z=NoB3yh+Yav{S*C4r$NDIkTgV|a<j(g`0?H1VVffZ_FMVkVo z$o~K&!%li~>P&esX9Mk-APrPF{S8*v2Wi^c&M<#ts85SFTlvipU+qy>=hh>&whzNX zpBp7q<N{7O)FDjnsWf&|4oKkaMkblmCCe5CLB>C900pRCTVn=Wj>tz>${>@F8sf~K zq9S9zaon-`8XeuUG{<H_H?+D3>9$k*o5;@tw?Mv4(n8a^WzJCiLuE>djrl`G<G*QN zf5UB-Y`H+gO7`p299J`#pqZj1J%yBUG1DSYF2rlF{Ohvr`6no-r6CnFwknDv{C80m z&tdF>fB?KeL%+C+XyGpSTNW-}Y1U_xS}UcG)w%%nUZ8p#I$KyUuTpu4!=)Gkjy5q- z_Gdh;`iod=IdK{4Be0?>`3P8$A!1g+#I1#kUkx&%I}OPUk?mphG!D6p>GgXd$&So% z5-c!%S8tDO?VYhvGm3mS!ZgDq#6ib|c-yv{Ru5<Ex}z&kwmD-D%DWt~rN-Rd9e9%^ zNi{&0@MAX*hI;xC8aDH~VmublW)Zp0yfJiVsPr|6Z6cjFwPZiF)dYy$gIrwE;^S~( zY`LzP+GYykW{oa3%Bj0^U1%1@QYWd)-PfIQ2fj#aeA!bRKwOu<7v5CI`l>XsB`cN? zo>o#Lth-cb^yuTWa{Z$wz~|g|EdJP#fGMM{%3d*ap#K2zrpIYF^2ecWoS;{J{*uyi zn{K0A&Ic&i`l6R^OS9^|e-Gxe#Ho_i_fg?RN4unf<d1e?vw7a8`OO6wMBNp3d4Fg_ zU{xf>*%QHO12~f6BIBXDqKc63$wd^Ev`peT!LiMkiF3`7ONt_K-FIFo+>}WdmgCQS z{{XK|Gk_p3Y!Fg$^i(hMn44hjy79{I$ZH?=nvJ4LxOm+27KBk^i}7W%E)myS1hG=i zS;=#f=Ox5k-#5i2#U;fe#WK#RW3P6yE%Ld<Ol9ws%?UL)H_;VPCZ9}g&gLX|>pLrq zVKk_ii;<?h+R<FN@W8K0j<v)GSf=Gb8{0R(meq%|GsSnMpVon1qnhk@M8x0Wr9YQc zOy7$6m(vzo_>Q<@ors5YalCpvx#S3vZ7^aqizUC>S#HP?nA_ZR>_6@sPnJq=fSUpR z$Gz>(i%vYHjy{<)kuj%#Zi{d3bqXpB_SE3%*AZzb>;^qPqY-+koQ`XV?iudplK>^) z3W~c3lje%#mc?9g5xaPdwd=4m)ZseXTEhezs909fl)X~BcBIF6y_0-R@psRaOXR_Q z6@Gn-bWVM_=OrvvL{?3ACB-iLnx`N_>+!;I2^88)2|%WAJGwmbsyOmsxJvxaExtW2 zTd;(VGl=6DuSd%FTUiXQNcOSZgji*~J4m99PhnO)#D<ElsgawTXB^;}!#HNxqM5@u z;)W1njyySF83(zy$s6-+0AJX*Afz?3KuU_ky`Z-9u3gl_7&~&u+Sp1u;(;f^f?~Hi zu>7nEdZdt~*qp%AsJ^tWhf7&Ho?gWlqeAd>BfMdnmOEt+Yy!?XquW)tTtUz9o9y1( zIoQQ}9@~!`Bu(`LQe(%bV4alnN|PoEaj=ey5}A`|SuMrWL@uHzdz=3E=d&BH!du)M zp;TIzcsC~8z}m4VLN=Ua*EHmpfVC+iR<+$*6~uEsVm&V0nBXK!gbHAiUJ-Uvr;)(k zPbT&(7gsuk<<K2m>A#3SA}NVML^(b4<<0Ts6b;E$&9N2ET~#b^k}AKpbsprWva7DC z^$3x6#3aRV`6`CpmlRhNcg_I0#1%GeGo{(qo7u|9Z(?bh4V==Ne6aMUo;Cd)rgQX; zm`q}hmKiaUKADNyrk%Sox`70$DMAXuxT#@;uZS_Dd^gT{%Q<HFrJS>TRmH-`a>RQ< z`_A*|**_!<RaN;N!_&Dn(si=Z^4c7`mg0*`-R0jEye1KI$s*(kN9Qi4ywz!$WjmaQ zPL9_}Bd9vrQ6tVD_U%5~g7sWrdOd%aoc6GzX1Yf#LoM{qp7JeXo*?3%W|t%<aV@dM z#d${?o+ACL@#VvZ9fmSUe)NaYMn#iq%sFCB#dG$}vNt*$S`V%N0DVLxvG}UxZ4}9+ zKTNjcPzmVFOqx#Oig{~tPa~&ZBa0rt$VKPt5qeGufiB27P)IhrD&mT5({B`02uB)e zkRAz>-IC5nZSY`PCC3nyxGv;51(8O0rg7UiWRnrZB2iZtH$=oA+!1Xv_cr6jMNmW> z4r1bN<djTT#BmKcoRr0q7>@B&l%%BZF&*(Y-NIB6p2LdLnzpVM{L2vcRNoNCV{mqj zl0B@uGSwx7U}9js5FV{zAq{l7O{UrAhdjt<nH=*2opH{%=Ug+a8Pp8qIolTt4=fY3 zME>9b6}uY7U0F2^g34@GG+`5t<zTC9wa}Dsn|+s~^j{#v@0@W1Djw+AOtR{kJkbS9 zhz@DizrQr;F|3P}Y25uXl2GkbsH~+-Fdbi~)%c#5W5p`xplH28)C}#n6yw_m>Ulm~ zFK3r5-xNx5StJmVvXBdpGSVxwEEIU=45TTI5jDkfQGAS!NcVapPm2^)ttg<1P2Dot z*$G`7=D9G%LOrKV$dqJl=B^^?)WYQDgGkMv5=8$16z%^2x$U-b**PvaNGTdRsiR@C z0&EJ@t6htK@X)UuAvZBZ6FyW;(G!<vPm{n(Ma^3vOmq`s-)7W!uUlr@m7OQi0k?R$ z9rDEo6$!FXCd&{V;%zS;FJg)+8m9cwTQ(hBCVpR=6-xw<XBUa2hmet8+*V4ge39*A z-M1{dae}hx<%v;YCQTJfb+!DsHTsb^O+mhT_9gBm@?u^ldfI2LCMD2dTZSc|V%mlk zs9{=;Nt}d%Ba5t9CetK|1JYd;Ey^8Xs)!0JR!py$f@{LbKYxFpf@0ykiJE4uXDr<_ zSlm>ngqYQQR^4vR+SzioY+_AUYEmP8+UkfU>rnw2ak|$!bLPuCW*K!ENJQgS48Aut zRaOH5Dx@wv&q;U0B|=o(7qYqABsjag(7Ba>jL2R@l0H$55s6CUT`02^Ty`;&G9k(} z7tK@}?2oU^(kbVWn7QO8?>Yp&7EqlS3c$AO(H=N+2)yN6erDrY15K;%yvCm7>#m`3 zM0YgktG5`tVyDZ}5_LAjOt5KR#v`!9dnO+x#3kFGcIA`kso!<Z>x!L`6Nv#RqAnLD z5NUZ55aN##Ut|N~Jk@-@AQ}e~=fDtAJE9j33K+aeb}l8{&3l-lru%0nH#BWg6|_YV zR6%b$VW-N%WRGhC*c{qidR~Y^lNuzK8V&MNY;8t)#dVnsqYkN(stN?n)C-ahE+TG1 z(rn@=;Su7Km~J|l+m7LT2_El(bmTX&kpBQ>UiVGbTonm1TXj*SZ6>box}P&=x;)~h zF6vrcroylw6SglWP5E^W&VzFZh76=pb`3|VxphQPV${6Pt3<6YGL{k0WzE}IF(-<3 z6Kyx-k5>7(>5?VB=5$@fY0T*s0(_4TcMz0Gk~ML)6|Q5G$jTtuhRkUPz?NA@=*<uA zoE7R;itvzI3v2Sd^~W?B4L;I`S)JaJ&pk&G#%o46$dKh6W*}RWL2ac@8+V$}Aw4!c z^;|*Szz6mw&ooR#>wlk@cO7T~3q+~0Vj|;Cl&d5ZQB=){okR>ip3ILH_V2Rd@=SHL zT;f=r4n0C#Q!E}zDx|m|h@4eb1yOmX_@byvF5!x7fQY{-2!mjRXt}`D90vtSwcSHt zKwL3J6iz|KbB=tth?_mK3}WpgxR8lIb5fD;tZbOgI1ep*2#APE<M8D)g(=qhZM=YG zA-)|_LWM7N0!2;m98RDRC`lwmBgI@YTxoa1PR%Fo$?j8|*}^jDJsHNr48vCZ@uo<# zU6hp6SLfe?yj!aQ$?8^kMpj!~^2}sADR9`her&55OS{lzxfScnW04I=lF@BkVTgK) zL)Hw!VRaBT3Yw9F7`idUOMatH7kvK!J+o{NXGa866_De7JIWrEo#zCHwb5+A*~*6X zY@w51a``aq8wVswuKO~3kS4%=&DGeWA0XtIO^GGD2#h)1T!k;8W{Q((K3mO6If*iK z$VWSkO<x^}=@`+qM{`EQj}o7#3Y)i%#bs<&HpJg84r7i(jw~>^NQy1CfB~^0ARDTy zn=UR2u0C7g5@UUgRY-_Y6~iRPUT|}n;JeL##``I^xb+cDdWa~lD(~1ik%%r5Il&Z> z2woLVrs_{3i8w-_CaePSAkN5}Vg^WW<d2T8W$z&xMII_5#@O@e(qT45-#HTh0HLxW zC{HS(FJ&U!B$5m6?xVhN#{n^}*uP4BPo~VwPD3lF>G53&5ScE7gV|D0Dkv|?WwV4z zxVcWpH{p*KyJqQ~@Y_Ne-z<rSqaBFcsNF>gwFuP9Ih1kW<S{2DHd`jKInQREg6mOf zAbA*kmpLwNi_aTX$2;OK%BS*M!e&NkC_>9d9j}M>r?$NG!^P>hY;_%{s0P+LAht`% z!tCF>kBX|qY{U>zF$XSDJfSbl)A8GGZ92`EEsH4~MpC(A(95=z+^J-rwr!H8o-OJ7 zWT^ndRnXdPyiySw_fA};&6770bWt%?GB6^ad`<CpT-$7L$EnutuMrm~GpPRO$v_CW zb4{D%xJ!$;hcA^>jS1betG)tkrPSlu5PF#7tf7;;nk#~w_RdO}gnLKb6qJ=$bj{Z{ zJ)h;qK0oxLe|5KKND)b<%Nc1mj3>oMIOf<(jkn2KhO3OnQ9=StPj${1fQgHTg1ch8 zyCLpQFHs~l#9or;8mWCOn%I~|dy{h!I$dpq$#)ao7vE1s{l?J{H`_heHu-HgNk$!9 zCPApSe0us_q4lW@i>yNdOiyPOz97>|V4Dv#&S{*}IVK6F>yD-oFBvMk>Bj#6Vb5!& z`7H%dwA)57=MV42?TCLXUCH&>8t$yKx~9mSl{1>{Z6UOd_tPEMWVsy?7?{Es!fBIk zTsbYng{N*vPfy$eJstMXOh^)`LJTPWc59AuF8kOc5@s~eHr_l-upp<w)iiCCa2n?3 z<fp&xX6|D0A|Ngy(o8P0xyd(W4O>1)E7amdsDrmmQBe?iV2gY7z?Zh`$DaFQ5$@@h zC?e-H&Zg)4NDd-wplgPzDkr!W+1ov_^<ELz9Ztkc=F?&xDyhE&ilRcyfZ)1IRbpu^ zRfVLrUM`W<yTj62p)tKNs8a_{U*hT8{9QSyaJ1f`&(nH~KTc{qT{)=nbk?N7(>jx5 z(;A&{G{&bwVuJqwc@$cVot32ZPf#?rszirHsni2b>M(W9J*Y?>(z=ruO6r{bDXNng z(z>BtqoB8KJvPG@w#9SPY%|krGt=yH-iq+0L(|uVCS4tgH9Je{9;ThsI+44AuCP%W zjY`3zcPNVz+0tsBXb-1YCV2_?Rn{w>pJ$$*Wr0~_rnR;k9c_*~Rcn$dt`{h4C$$~u zlD4y}voy5R8iC1GT6)EpvneiB_Ni`@5~|~7YOPZw)>|qyPi%-4cDp5Ao-JjbThVGK zL7g|N97pu_qEUxSXe~ymvM3Yu)zDUH9XWE$+g&p_Wdw_e;LU}>EV}uU*r}+#fht5> z9-6|iMv?`FBnkNfs()9=P0t}e3buI{keQxBb_IOpKP@w4i8B%uyz+A~=##j43oJMm zTZ|csiw46Ru9hf}Cho8&u6k_#UQT{_TO9IMIF-}U9ECj*y6J<SO2Kg;PFz?Mc&&7` zM1wVPD<)!BN=))HbJOKvSIoqTHx9N!i%_QV@>4U(OwS`Pcc4pAV!nRwLY>Fb?EKSk zCZPElySoNf9Yf?HRV}pt0M@qDu^<%vY9}5>ql`5bPq`)s{FfK;E0!*_LU;XkEpHfX zWVGz7IXn*JS-DsB9}(4FQs4go){S*o`^9MhTUpSN;M5f4ZV&)5eNywOZ&<RPyQ^vR zR8;s_+DC-HsN3gVX&!3-0O+fYp?|6CnwD&SGbTlc8YH-~qouuC*!*lBoX}WbTuQfv zqHB<!(VBV0^mvrEbT+aql1`+gjbvKOO$s}ysM<tSRQ~`($?+l#Tk6-`dFGjCMEH|M zs^thZCsWyaMPO7}w_zIdQN^ku@g{(cuN1V~p+$EV-Uq^7$itD_B%f&92wojoJ1bBr zXN%*?Z-+NeTWLT2({Zr)DS{WlRS^V)lm34&$6FiaYiT3AS~dmPx^_sTR)5D)%0-XT zZ}i;BF<O^Q*|%4hdaq8>D>u~-S?(J-dr=;BCacr8YySW~u(y7vOzDpep=up3rMy79 ziQ(KWf2j($Q0tvlD^1+r>nIZ8_?s$Tgqz}~tBYNCI<UM`)EfT)U(v1q0Eibg$)@~A zeff7<&s5*&&keuuYe_ay{AQ@}4^c~s{{V_oQ$WBuXAe|X#XQih_2z4T60WH=8=fKR zH??M`gfi%~wS%*1=hJDo?^jJ|$uK0+=zH4cuIWh@;;Aen>TNwD59%sEg_MJlJ3K;_ zJ>Lm`{%{H}F@L5GQ_X2>$qvDW%s9HHK9zVG)7lM*^=)`!PFHGw58<}%rqj(k7F=mj zt(;Me^VxA-)~xWV!N#?+lCF4-St4zZh4twROaB1vTf}x<T^6phZ^HN*n${18H&LLm zr}Z|p)eQK9s;yl9py@vlhU(wMTgytVH&dwYtb9DuULdZW(f9r#L9Pugu3NaU)!MQS zo2sDJR}<{0JVt8TmERQ^4J4i_vKG_)O!E4NPk5D8Wm-Nj@oQ1|lLnUGUF*$QRQgux z#?B6>*UeR0*7%R=72XxJ)QhzCcDBM@oAovY9z9gpvnrDQ8#9|XVbsNSs+6X<=$%Z` zD#o2^zdTTCV_NEuP)ww;OXz8(uQdeQyB`dip6<E-0G{HiHKkk~JL0u1YrY!JhvC)0 zNTu+twqK!vain!Uo~NzQ8onl%)EXC3Oz`S{kiC0fH>P|`9ZjmVPNTMWO=#Of2)ZZ4 zX0*<WtD9G)?KGaRPY^U#n6|97qoWBvAZ49%6Qk()@Z;KF_<ZG+%RU^v0?$`ij7m)$ z!%^fOm#$r17eQ&!c2<b4av11Z=+%Lz+D%1YpyP#5X!nLrT~bIHF{<nzHyfsj15<bd zbrx5Hej;AAsBa(EL#wk?>4mxj(N#}aKcS&JK2C$Ag9-lt3hUj(L`1f0lvh-@y4Do1 zWog|#X%kX6sbQ8}sol)45!?R&ZqG$(AJ&?`M`}4*yTu9{Y4Ua5PN8R9+16?s9eq<t za^H)3!mdr8m($^@mqNd$wF>w_sdR1OhJe%L7d{|Cl`@Y*>Agi8S5eYynwXc>mKEL; zTh?nWO()LU#PM>ALuwmJ`4yV(ew4S;_TjF)T+`Op^g343+QPq6Q)(*Bd!h98`rr6J zOL13CpD(M(Z4JoM9&+o<NlK=|Y*uO-aAefRuaK1P8C+YZUh0p|rFE4xRg-b3v{K;c zeHM`&)A~tRiyDHVADa<_esnfSU2KVF#7PG0U|nXqtjvn`Mx72qjD{5r=7Cb?tg&5h zpZcc3hc+x<4KyrSKk-{sS6aZM(==3!{4TVV)?~%j@*?D&e+>pZPwFS5#%-3@PZ#PD z)6xc>eKg19&7)V4w5mAOHDgt^-{}7U`WsHnou{-fj&Lb7uZ7U9H9v{p4Qr>YZBm0o z+dw7IcV3!4A8JIg&^k`$l(N<SlwEJHo2hS1X{ow`Oc=u!H26@uP+3)Zrp30u{GC&! zYjqWTc;L)<nmVdBr~d$i9|kR7gv-cM!aU{<TNM!;br!ki9k-^LNXSOBi2~x%bay3b z)sg903$)oWT_^0#8ym!AXWVp>M8ajecI;q8ynCdUI*KFGWSl!fkikKIcAecOuwfn( zkFhT11PEtkr{S2p{{XCG$V!5tVO~_kk1kRSO+)_x83>Th@MFW7a;fPmY2kmtaU5NZ zs%c?E*R)ME>vP~Y5w}m9G?kJ0{{Z^J+hd$Z6ahzM*hEDH0CS?w<kLi|07?G<r~kwN zP!IqC0|5jE1PTZO2n7TJ0{{U40s|2Q5+N}VB0*7MaRn1HkrW^!GeUBKlA*CMG*V)7 zlflt7f(8{NKtp4c;ZuXM!tf=cqwz#gWOU;H+5iXv0RRR+0gZqfxxja;F>|U&(tSl| z^+KE0MKrcLTofmIaA>Vcb@6<-;+uK2rZrRtA>5eez1?7~Ms-kntJ|Vc_evCLO_ol` z7~q){GG1}uoah0+Ob}#_jz;li55W$!0OOjBfk0n~3I@P$5HC@=1xX8>Ufb2K)Xu6a zu8`LIjx^+>ip^Ha2D6jH+kRT!bKRM$9b8^kwP_e9xwNh0%{V>OyfHh<ebUoWf(H=d zb|*XsG~`WGVj3~Ez?|L1I(bcBuy9Q4AsV;}g|Dj86s?NqQsJ2xuL(3XI<-%9v6o7B zZ%<%1=zEjhB?aB+h7E0$RJvG5#d$VX?qwNFHEAi*wX_91M@G@v(KbhtWFs`kx=y|B zI<el#S)+JsZsJXFS6V?J>Xh2&p{R024-~ixtG8*Pu~$?b8KT!#hiVdIezmU@BaoY@ zz7RpB6=tcTw6V3hcLZd632`;5V^|Faj8mB=NwP7ZIAlVJlMvueMRl;2vD8m+t<s}; zVb;eK$63o;Ov2S=ZldnZGFPQ|4&0-;t%~YQM-8f&jo~$~lI(Bo6bodlYrPnvlC7dM z-!vx>yn_h`;G-r}T-R+AAghjP%!T2g>_%2lx!IIj$KbEX+r>Mn)xDZ8b1LJt5dgbW zqc!CoFN6q?RB=^wgS}pCqrGd&Fye`X)sz&`5jhDOdx~?I>Vi|>QYfMu$O)}!ByrI% zG{~P5iE1ohqfCXz+ok93l^@}$y>%I@yI~eNz&5otKrn&^7%Mm8HCYc0F7*|k)Kc@% zS-sc<TXb#q)jno|D~ECcwG_h?;`Ww|uOky=4mWW^jl02nbQ+af;!~Tws?y>+4=f0m zEr?L27o0C`O?z4gB^;7nucAt3uocM=2)wa)H<^Na87(Gja38vmIk%<jNGP+ZrZ}Cc zg^hDs2c(HL)fnzYnS-$cMGHm>?LDCu5(9>1QO*vzy*?YXW6gbqK_C2F`6fpkV1wU^ zecL&qI_`EU*BDv=_m&X#oHr)PcsBO^iSar5uFcD-f<j{h6hRcCoKqKNRUv$u`zWKE ztay-(Tyjn8h3fB72Kpk1BASOJQ1MQOa#!7sDy`8=t021}c2U45o1<A|6)3V@q7{15 zs^_?lXgVN4t_~;(q%#g_%<BVPj-v2)9?A~BX+&EK&Bv7+kFi{sq9K-z*iO|`ASGXU zrBHZ%1y#^_uB<t(8<D*4Vmh=0;G0K<+Y7d;WH_u`uzK^0&M!ErePZ%e1L&$;YP&@M z3dQRO71a%&u7>e=3svTTse*^rZ_PIyi1J0`3Z4ruf@_-0Zq~YA$wl}|UtJ6~r>A<w zS^ajaFRP{AqO)V@DKXok7KYW?pK1h*Ye7X;iqQPiUv!GLywr_w)fv?i$jA*O5y>^> zv?Y6V28U5G!qQyb#RgLp_g$szQ<%q6t5}k=TBjr|^J?A(oKY~i(8a$bB-VY?MiOa& z;F)?(td<+@a=q4*L#tF}X{(E9RAe}7I=$+w&-G85&??+$0xn>=8@6r;mbVj`(MbYi z)^mvMG;L02R_2=R-n<LJM{E~JqjYV>gBq_W9{0TA!8wz-#TeLBV=%!?5tWgFc`{hR zsO>2nHMXGOHFnV#-KBOT7~Z<f9?&u=u7-eLyjit3K+x1VB8Q53Af4)`fHt7Vg<7F- zg;8|-7iFum#BgG6BJe0{G<y)daiXap1Pi2Ss;e9bM*d0g!8inb70~xT?<Y2R36~vp z9Hdhm1#_xv*vGYlV97G`h8q*(YfFm-Q;ejE>wrP-m{e>|#_>hBaH?n$;80~M9E`wJ z9hFVljx|B2Dl4sUR4xguXsA1Wgq<zY<H5Qa>yMguH84?g3A~ZVVs9-G(E0~Vo~cp( z5K;#=xT;(U(@N{Hf!=oocFy4pb;^||nuy#vg<|z|yVO>1RX+`@B@;u9NUM!dX?5DW zQQv7X*KbkuD(6*q?3Cbhc11E~$=l3vQ5PME^G+vnds&SN1fh7*VBm;pTBzz8Z&Z0q zx!S0rYb$xq8ty9wA{`jT2S&#@mul{6g{|uaBiG{Z*+y>aFlGW{n#0xhOZcG`>a?7Y zYeguqTD3%#R=GtxiD7DVFp-9CQeJ6$j@sZ8rj*?=<cw{2rrIZYTpBi}xC|*uC&k%* zS5$$|BUMBLhgW))IikkA5lY~?`}a=ZwWhkjfs9n|&p<&G)Hx!DicmywPr5}yWHAC> z<A)9<39S?AwYtu6pysICLu7E+#+Uq)Sr10qTM_>N74h9~#Q}1ltT70u)F6p20|Z$Z zd+D@b742YIfEyElJGQ8!e77YnHD@IEM&L`1M5(9-2Rfrn9H<;l;+o0Y*U)CKG-}m2 zM+keR6en_ghqUe672Bs3a}ah{1a*aWtD)Z244rUw1}HW|m_$KJS2Rb(5Yu?=st@aN z8xg=@h3N%XoD0qc=R)(LVKDDrbT2wP*PRQ_h37)^qM>=vyy!*EH5h0fuui1GOu+iN zN6aSc>_=Z>XzPXndFH6@S{qxWQ%joXP!^F?*50kwQmRYmTC22(^ao@nhK#nM<c9@i zsK{w1n-d1)R!OcwkL=$LZ!n{ZFi6GDP3ziotv9aC*0&I%xmvchQF2EeszAeuX;nGx zJJkYYaExB+1YhCg$xNxMUC24(EY%5H6+j%qYp-RsUaruqPhz0kwHH00o0wO6v>DrR z&uJI8(GCKN&kRlU#Yo9vmD(Z^N18OzEN<G>b~c(<Sf;p3d~I&z#xhE=?7ToKS4b{3 zmG-FL;MFczHkjlSohz>rog4z`c5c~3HSbXpMl@12ju-^u^3qNe+^a8S_H+#eH4a(_ zicn7VS62&a`CoK<QIY2Kj&8G$>A^Z1MXmw{VaA~u8#Rip-E|gNEU%o8N|#&SRc5k8 z=KYqu)dmX3EjtgwytkrTl6Xc(Y{)5(TXo)!I0X2lkT{If!>H!C;G80^@S=t77rMY3 zOi`3-4UJCL#W~$(w&Xg-tST6~whP5o(cL@HF}kHRx|ycBxhEMU*4TyOC{<eGI4aht zQMm3-3>fE%a1F&(=8ij%4>Uo*tjf%-t1`Q8?^%_#ZPT|&XW9KwqX=&VOZ8oQ4eGQ{ zco>BrFq?wxuFC9$00Ub{X-{t8jF&SdRlo%^4#ymLrnZkSaX1az(K~5^1=3z{8Z%Dg z;sqJposB9pO}sMNpHH$;=2U3&PNwAO=|`<*l1heA2V~V;Y;G>tD4nP(<QnE(ss*m7 zTEMEb8YY=5h*)NgMp>qqnBoJ#!#or>n&4Vpnsc6N!H8;M3?eJkVB(2$sz|3V5}GQf zZJEuCN11li=-k2{?@MB9<Ta*L&WX1py4O=Q*xfiGqW}|KBL|f1mFF(uRU}mH92`_8 z^*5C2AmJ+mZtUKj6X;8`DXbX*qXD4cn^1=>1H>g@3dImMge?y>>e^|{scd!^mcbdi z^3y{1Zm!^jb6BG+MdlW@{1;Zz8HQ-IJiPBi8Wx-g%qJN|ZX2Qh0Bilz$!Sfx(%xy! zeJ<qM<WK6Rl9yYHYC>+auxP;y>PAWlMQ`=Zr!a_oZ><d~3VqjHrpl|%6&uiTh?bNG zQ>nEH7%}@QvLj6QqE0I`7!VGM@h!}wCEBne7%-{?LUF(vE8Tc;Rdi&Pd8{=70Px&V zY-|`St{AE@sI-hxxH3c$h(_*1MhK3ssjcb`E3g$hu!!oyd3Y57QM)U@L$Wm3u~Q~> zY`t`}p7hRI4G{#l1P|GDFw)vEy90unqMQ?M!U(CMstuAhQoGftvRb*bJ5;uV`A*eV zvZcB5OlV+hL8&F!ql%#VC*!dmWk#Kj)W%Z-nO$u#qPuqi!BMPLx~{kdR*Y2$#41r! zs|CTjsq6!*a3GRZ9QGCGS`dlUaTwKjI2Xy*z+@1ATGq!yTBz=jB4MCANmg*di50!I zJ~He<SS(g2LuGitDaD>ICtJ7o(Kjrph;+@M%pl2QCD_oex%2A}MMq6gz?vE>P?*Uv zxN-=;t03&E#8U98h&8x4W2dos80&VMlW|SECJDEYMaLk3aW&i)EqL}-7RM&G<AKoS zs%VW}ToBe)lNK^vH+j(?CP5C?=e2d#TUl=Qqa^0`Ij+?L7JEB&-HV)k5yf7Yq+RS_ zOofM4o#gLNBWPBkcCKt?&EQ*#bJ#FFldu4TuhovaSl#6fTx&phDGf5m_eO5F%QDXS zT);aRRkb_QZPSx_eP*=mZp=8YwnpOYX;fW!2G*2V$$Wf~ZQN5yKE;T|NDMcdy%HVl zS3m*O%{lI7Cu-Nt7PdS{K$hL!n^HZg@-pi{_^0)b#NZRf1ryvD2qjX5#lRv;e<bxP za+jWhoKD2iQvU!{Ay?f^A;G}yMM?a>C91;mpO7xji`W$WlTDcQuYD>inaF4>R!1~b zVMeCDP-msSsaB6_XEUNX?zER20sBMuR*)JSdWB+Zpq&*zKzS>`ZC<Wv@49<rCLDk@ zSgO!0v|<7v1a6A8QNoHn%1fBK?ieQajKowjiM?!hYCyvr*Yc^RX!4g$PX&0q+zr9I z7hh<U$xh6CQ?R2N>NR!o9%;R7%@`~vZ+h}l0<S$iv6>oDMFU%i!8R5&x$oE%k<Rkh zuw>-sxq;=|P>Q!Nnpa>!y>NH{Vm6@oqYP-mp%+Feu<=%5Xp92fzui*(>=amfA$Cga z71$^nl;VJ@tx%y=MZYbnuXJ}Ba;7L`wVOk*kI_p5q<obZnsy2&X$xB-pJ;WOCq2fc z#8pV+BvDGnn}iV3t~Ip<94Mxjt1i3-d==TOy1UwI@F>dWxJ4BUTfFf=wTC=aI=545 znjlTUZ^vJ<?2{^Ru1baYQ@Keyv7klnp%vS-E5?PY{7oYTBMZAn5R6X&yKB8XFjZB~ z)*BGmsU;0_Rb#2aR;1oVn3pFBCAu22s_R}g2WTM_P<B^W)lluK*wLCjLrb_S*6A6A zBa5!Mb`{y1j`c0kl@pr%s&d?da+gT6#Ay?1Tz=g!?y7C<amJ{O2u!|DQs;o-xE-UC zuJE1%?zWA~JdxNby*}e>OiFL6-;%U)QI-to-t^8xL0-f^5NU0G$yS1zD03@Z-R7fU z#)Q|}saj!Ho#S_l!{|-s>`&_n)_1{OYiaZqqOBGpsjpzf;GZaHyfRF4fW%c^b|p$F zL2#>?t!S!`RaUn;aYg{(=`vMuym+jTEhhy-gN9*B+L4Y0HZUmR>20dCwnttuHh`x| zEFsQ0sh%7|NYw*jS1$QldBs_#cW*8Wstg2IY7~jR%dit}ifX8RQQ{J?Z5FW8c$^j1 z>o))bu16(M`fySG;98C=R*2QP1T`0ACx{#YBLa3S)`vb<1FYk$m`s!yS0Jhy&_KY1 zV}-S@)Gy;yTOLO=@J1^Z(J_(69Xw{N<+Kus5U`MF0FZa5b@mxR8%D7BBYU)V6*N%{ zZY!>Pu8o<eM&|*r4d|Y)9qPEJO37^2Xm4FJS|qp$8@H?R^wpAEONB!!15c_`TWo-^ zxayqRc@Ud)V1PJLGz<dXoN0;<EyIeII_qp36Bt0L6i+g=UO6Hnak>t~*KXdJ@fFvm zg29N#f^I6+{JJ6Pn?hI=x7)1KntOMFqIZn$(0g!CaNNK>h=)*-u|2MLFJfUHh6OY^ z)pRZ~*Y4)kBU;kuQNJY8@+vBDKQ(FU-u|`hb!c`BlwPBLN>)~vu&RyCrCCeHxs=XB zL0-lG05ECmqi_Uo$vDp&qkFQtDjmcwt`Hb8*4nzxu({yYq}4V?WbF7I$Wye;LZ+M- zV6MXKth{Vc1L(p5Ny$g*=#!H2(>1l;Ra-Au?SZ(fpqS(PAmVxWwPpo%bHrTIdqeh3 zd{Z>kZ&tY}+WH9nRR^jxX0TYQQDvl!=(OM)vw9m%R&)u|V{*>Y;_7C11ky4(Mk*M= zs|giTH}0w3OVxQ5<gztFri}QiHK97Y6mK1&nvubmj_lC8Vt+ioX{{q%ioEe&alvMd z_6&Q~qF7)P#P3PCxDC`sY5G3U$cgPbG6ITbS5(zdwt|@`<FrM7I9gvI*}xR;j<wog zg`(76$43|qWOdbG7-L1zari?cg2?8X8FyLj0Axm31#Pj<Vr8GB<BgBA&evmqZm!^@ zh&i%daiiOj;t;#BIi<MoSd2HSBWG|JZBmL1JuRr4*<`6P?Btx_MI7Ny2sN`)3}}R8 zUVU>{-%$PP{H?rEFpIU#UzR(?2!s^_C3Ti!Hk8-ga6wu_%^FxyyV`HYiN(V_!VY1p zi*Tm&3+qxzBZ{`Q+its&u1Y}D+Pmz*0ZeI4%4(73-9R@e{Z^^SG{G^3Vp?1<Oy!`_ zb(2{d=WimZjfOD6v<)q5fFp5Pt-315QoA>z3{Ci|lp4{B21SEH$z-x!kc}Hfs#R&? z08i4EouM_g%LV0T0X>#*=Q+Zar2;RJ)jvaeCl)(+G()X5W3VL$wA3FSKUG2MjRuQb z#-{asqZD5308I+h18C87p7msj^XoP-T9xO%D+f65Sc64*&8x=>l<T14DT*KswLo1> zXp=!*WYg8PG>l>1sT^!z-~d<}>8V@vI=IwioaV;Jo_K1hBepi{vFe^XE*uK!=xkFK z+GOhFiT$3=%C4S>P`21?^1?6nWMCJE43|xGl=e3N09Z`}EY?_Iby^xTdToM2>0q(g z0kqnlJmk*Ft=L!gAsC6&F^k!#oi)BVZ-VBAi^hX`W5uzOUNjAMqmf~#BQR5i-I=0k zfmm%@cm(UrtyLH<s=ClIU15^(*s@tFngnRdtS~w<&>DL+7r7}R4aJf;Vm54EU9nVW zP`n~2ul13huk7KD)eN!qLP~}akmriE*<~Tm9b3z{-wH!bjrk(taP&fZnBvnzinE>Z zF#HtbcubS5280?WavmgpBTm(l6a<~YU2`1Q4TMmls)42@W1}^nibAN(Q0N$-3HmM? za7K1w;hzzpNMCdmTqUg#E|!F=?0rx#mhDh2b?U5NMS-fRYOTYetz&^`*i%_6OJCw` zTU0=Ol?dF`k<ciDrjmv_9jxQ+RGu5mK9I~NM;w97eM{TCHEPD(6$oG|jix9H0Ew<^ z6QQ?}SuB-fdz;9srgKGkvbgUA=9(nba;P|Xxh|q+)K>62LEffpA~>dNDXal(jVA)5 zvD*4?tFy}QDI@~rI9}MTHai8m-63bcs_Dsf-T}$>F9PZu=F!r1x0(jqyiqe-83&YP z5VrzJICpPSSna>j_@|Q2>KC-qyhaMi;Z~Szc8M(Qk}Eqv?7t8RIk`2@bwI(d{{Xb{ z37`wb#)|P8yVffV_7!#R7@W~81hj3UZ<N*!r8l8dnm1ToOx7{rn{zFpJCeP%sjl8U zLosftYZ_g-$V$e8yeQk%`5ID#Ja(xgV@=VmRW!~%gIeS2?@|^$uNt_6QI?1MRg`Xp zpC#nyECGh2dYh~>8KXM}trXpCrPpMjuH~A1$6!Fd7MRJ)Z55tY<o48id`GkF(K0uU zP}e!YdRKNPP8o62U&1X?7|fW{vb##N#Wk$?09#ORJ;eGx#n(sCliC?h+l>L@h6^Ry z?p>n0S}|Rvn#~%W722`T)IOAgnp!nvbq_Sioh{50Eb%_^Ii~{d!ekMe(={ATanEA2 zvE>rU&#^tOeLz<|6eDXwX{I^@nrKE!C)XNdqa~J!>U$=E3&ylG(i~BusoA3s5!7<U z^;hDa>E}<15lat*V?gG+71r%7%Xf{I#C&IRLdi3#51S|D;vE&LW2J?UbakOE4#4ci z19DSN%SNk0vrBJxlbHE70eRFLO)^o*43o&oTYp)dle>y#y3=WE8D+GxaZKLPjo$FB zR)$$(jgA+Y*27Zodw&IFcZ80Qwm{a$EOHDS(UFXwf}5kGl)I`d0GD2SiNv^+9v*0n zP9qG86&2#EL>lG?Ii*a{DSIZ-b&kE-$ZkRHYqCbqFaVRcYHOi&8smyFoQflXPu8H? znmW;vEo16eV4_P(d3mjU8dkg$gEbpm0`si?V4ZWQ1d3D~Q^#E6dVqjv75hOC83r`$ z+Hl&K_B!(((B_@stjdI5?6d^tx%6>r@NU1WhF2TMHEWLbe6htf*R@fJ!tg7sj+ET2 zEO-~XklZ$Sfxm6k_*FXC>D?4?kw-JO=N*kx9<o5!2RgI?!km<Cv&!4P3vwa>Bsu+} zPQsiZjitejYdXRML~X{nXH}Pvn$2djS?IhF2qygrPVpM@Fc_^<gGCdcChQcicVX&B z(^c!N16erCIU-{isa)f%<nrU1>pI0zkkc5Eh0w`?Bhfq1H8}BFu080u%|I3R%39Sc zp|l#i?PQE{XM4RgT_(GN7<iayDWoG)MTU%a>{v8liSe-U+?e(qX1b%;pqP41(^Z<} z<VNvC>~gBI!$4UTjxj^TRSM;uHz{BjL{ku6F6Ktm8YOks_I8hDQ)HZpNEF0Uqu8wX z8+D9swe0G1R*F0W6X|zp!nHgVq4K<t%7D<NYa%yV?9gjtsjVoacZ0g2Gpug1yunbL zL7PG~6vqV{T{TTHsuDr0X|YyVg*SxuLRiKW);;wgXH_Nfo?0KtJ&m-oHnj6DJNYao zY^~hkPm(4}Bw+C*g-ekSRi$Gj*?q0)*KaxBc&`I5q>z_fY4n(J6y%C)2GFeZ7?7;} zWgB*`#Z2u|9z*ctpA*?u>_A5$ZWt?uK<;5!-`O`chaDvuz-e<It9vS(F<n)rF1^vo zee8?U4-~6;6w5UOT<Ud<ZarH*&oyfv4>`X}jNiiyf^@B8B5m8H&1S)?x5gel30yP+ z5c(o^c!U)BBgo=g^gW`fT_S?>Q%fZxn>QiliXrme2L%{AljC!9#3O}Eb{(Nps%ohS zBf$_iBVC9f6L=yP3q2Lyx|e%Ckdu-OW>>L6Ph)$vhPy`to{ZLYMXn?i_UC+RswXl= zmYnh1U7;S6iQ&;YHpL@7CONIA$6{kRX6)Pk$+eR8UGr4krWXfYed-z-UK@(F1t={! znm8!rIGX%b1F4Nu8-c2IRi7V3En!qD)ke1j<TL`VJ0$xpmRqMM%G!4|R%%NDIKJo( zXF6sTVs+Z%hAlqDc2*Bt(I}t>`u8I4DhGo?*;p|9*eW~DfsY<4%=^u$jkQseNgC!B z@<3iDjXl9g>-kAaA|t49H6bz5k|;B7Xv`lYsgAOe=)tdO8Vb5e7;I8Y);tJEy6imX z`zKvAEi|aZ<e2&V+bZHITh!U@Mku6tiMF=N(b@828@=eU_IRC9$aaf{4d>$Jqb)QV zcOhLAR72=4)n|{2<tW_a^p!awa~S0Gc&gDbSJDb7_Lfv8KXIW^J7s8j6<#QSf^xcc zgbv*V?m;@6mO4OH+7nL?qqs9FyNYUx(GjEKb$N}UMJrAZXd~LIKvh$V#p)><*Hx^o zJ-8}v0YMz@0g@L8UVRA$H+@%p*MrGks?y%7LAtpgVOGc`&5kS%-9tqbW!`AJ{g<(3 zsu8=Gq4uF8TR}4#<LwFMvEq253^{gj$q_{vyM<LIimEHQQ4>={!d7Yc>`mpT4&04% zTf>T|P7W2=M)15D=gGSm9DnHjkr{)Uyv=59ZqdmX-UJ4e*rKDHa{mBzV~m=)^M^b6 zaZtuC(gvq{whs2b`~Gpc)>IGU!ZSUn<Wc&Q7`Lk6(ou)9U7;5HS~A<px20qoL4 zJ5<3$!U=E{a00b)Med2foN_uith{`*V6oGZq0>}6bMaTZmUTw)O^jmK9C#q@1S-?D z^sI9ZAcV@CX^M>f@VYtME*h)8k!qnhVW6R*s~=(tyj3(hix8TFRX3sZV0e%caK`T{ z=<N3+ec%tgXr#F8FJM&%dX_@V-9(IiI2}{-PVHZWfJG>2u}dr+BzoywOt4HUYH`&p zQpo32ywx`p=QBOZQb)6jB<59)?gOcA98mzQ_7r1z*Bf_)4daHzTgvQmRZ%#T$xzEe zivy~uv}dLkH0w4!ZWomXTULD0>o9FI+;$Wpn)T{xk||nm;;Bs0beGs<WAC(JT3BnA z+JLQnP_q!}1LeI>H0IX@elv}p2G?_p+TO}FLgDt0$qij%&`P~<wAUoi*4G7cFg2Zx zb|;q_zD;G-kyvD@YquTAu*NZa=$mI_s^>cNj{8SJLc5j}LyD~hc_Ub_vuy?`v$D|9 z6@4R0Y0E}S9~%{tqRgz;C?Vrxm(3dcO5A9w)0w9wQgKZL;xIaI)3Sb*xK%f*h?BuF z)4r573>)~XKK936<P4Jt=-pCwtF3kPMuDQV+Pf<qz9vY578z_*D19T)M-@O6)>j&} zv~zhBXtSpz_JVRieAj4Hu90IKc$PN-y6{2FilUUEc40U{(cMg?wS0#c05=k$AcqIV zi5p1J+U-q@VCfDBR^>fqV~oTvHJ3^DrxrG$7dUFP6bOmVCn_T&nQ)zjT{KskacQa# zBw`Sv3P(E}5p!r3NV)q8o=>#x72B~-X1T54;o$sLn$p9rT)^SPo1pSWS6-4xrIX4u z>NBa>d8YLeJg{PK3L2D20*w_a*7D*+*Nrz4Dc?n9Z{I%!6XSghbq*A+1wsJHs7Xzf z=B;deReRs6cp~aya*a+8I44MEyxLMaj6yCuP8^1sw9um6XS;HhlSbJXNVw4I7JE~A z=p}C=9q?9pEH>c(01ac<=ng_i#ajG(B<)=%Vj9+i(l@9GZ);rQvib^XuZ$4!bN>K! z%RShw)b1rX{1I;BiQl2P;N%T<B2&i%=A6N&(lO&Unx=JThDriBrs1_me+Z#wv$&?% z;ImgDc#o1`Y(Nk*(N#*U^c)dc<`#7ta9LC$O3z~U{S&z<PZSYBKk!e@Zd&go?r2Vb zP+J)xnxgaS^FX$hi3Q&!AOOEt95Yg|G|{d!<bl>{6~rjq%h#If8k*;zw1?z2;H;LD zlOJ&Z04|0AawR#iIBNEZPKHP8k&3;8%`ie^EgBDDHTBI<6m0Ib7}(v}rbHfJbY@l9 zW4uOu6=iKIgJczLSCNR_Pjum}U~V`2B;)}{hKn3k?MN%gVy)8Qb~pn&%Cy-rtBn}p z=z?(vhUFtm<PCKT=xWy!y&5|cUux<a-q@p$B=(O<iWYsA)caI-pXA-B`W&mZKv??W zL>i>T4r{apIH2(B_~70A)RW8_=>WhS8r1T~w1K$keGM>9Ic+6gX{bPq#z<@}q4uEr z0cer!dmSMFx{uF2Ijr>8O-d^@n$2d37^*l^K=CNPK%@Tvl_>X>ai<ee%fxFOaT(|# z7i@QrhmB>Gz?QORIlyfI;;e6ifwDHEl5?YRpaSsTuef8Q9T_ZiP^=byCR!Z!Hu5Ke zJkQZTl6m8x=nX!cF8E`jM;(gN=;XNO3(R)n(^ae;b72TwYu)e&%;!)v{{Y}jkbj8Z zlFdl%;5iDlG2Adub3;R{R*YydB3yA+bM~WYPHEYSy>k0x?KhGV2=36}0Z$U*8$u9k zX^v^L8c6cz2UpRi#5%eHMo0^q?HHATQnp+J$Tyc&3$NpeSGN@ucZFP|f;JB+i>t#b zB8WgnM_40u-UZk$tiJq@aeS}dRNUwm8PHeQp`gXi6)4?xdwK9sZa66@>I%@F0|d$n z2JB70A#n6-xqB1*lV5n_U5dU78mnyV2)bB>?#QM!sx$Qu>axE~E2YWsvNG&)Zq8+1 zwA*@~45nMja=8t>ULS6Tz0^d>)w+YV8>`>BGzSei_|!!?9SuBFR!g$52qC8*YW8<5 zFx(M2VTTfcZo-BLjSY}xwKj&hDy!SE@n|&Stxtx?=~)=1;OYS6_Bo}lYlv_=2-4RL zgdXHA1^uULw_`#$cC_GCG|^_Nh8Q8@lxAR{cW8d8?c7y1Dm7!{$;msKsyZwVITzhO z&4R$)L^#)wQ!#?AF-h`PNhnxf6KdDtLfi;dy2l@~UsU+TFSvqRT!2sl@z&#j%I<;2 zt?yYTb1S4Q(gImC8Bjjzghc1v0JIkirvjv@!kMPT!;VTfnVTVYbVk2r8Z}BFmPS`Q zr8LPbm#ei(;_ZPN42pHk)XaObXu&>-u*s_^I34O@`3c=snyB`o*$Ee<&w$BYPj3>O z)WNyS`5*C7yX?H7M^NrYqXaln1nn^T#GXK8tc|~2SA)*FZj(TBLMFyyv(~2J)->7? ze^y@XgpV~O46xXq>5HFek%i4;S*s9sMchunrE!MrcP`V7C{H4;GqU@)et?yN_9rT7 zY92m$;#NZ(Q9BjDaWw#LQH}LXeK6Xqr>Sw|fg4k~XvanxDpebzSZtHMy>4s*wJE}G z+r{^DcZpNjK?|i)sA97yEPPsz8Q5kN_s^t<-b2TsPvo2kp~XiuP=h|afbBZpwt%dS zX~fLRccF;yS^QPruBH1%#d_sHkcTv{CuVQ8#abNmc)va-tE1-Z>pmmx#AuMUpxhP5 z(Hqckg~hT9%`FwGjgkwbus-h5UFvCF3uOhDXhms`Y8yvV85nz-%6-)pq6q61;v{$3 zJ1V7-$6Ba1N0$MTZ&elgk6={l7~VIP=%kDd#31L$%r^vF>N=^-bVTD+K!%>xJd<O5 z_ak&1RZa%2zWnKX(QoQh&UwbP9EJfEfufRD3?U75chI4YRGd?NRgylbQ*eD#<Lbeo z!(l)<ub@ugaZOL>rSLjeM@HTb8wlp5gTrE;869{u0vyw~OOrgN0FXj!1kvF^M;c<f zMV6XHFI{#)o!VjC5o_j(aQN>v(VBzo*sP3z)<(w*-Ht`fscEj?p#wogEJC7&%{5CL z$qfOgWK~p$v~iVo{{Z=wS{&+bmaEOl`Gr-@b9u`}gZ}{ek-Jw85><J(`{h;Owx?{g zUBCWx=}c2RoXc$|^jFztp1fyqRFXG_<&4BFW=a02ud3RA)VXUxIOl3`O>-GAa5#T! zC<56V>rPe2weGILXe;X0kM#~-ZeR69P(?92g{ZHqT7T3zRKYmsX>zAmIp8fteOA-{ zrOWEJkM#~tmMG<8oGDk@3HheJVDjK8g`l-nwtGS4YH?pxwEqC9a{8^O{Y#Tu1Tug( z%1Xhm_m`h%=AlUeCn46VnBVV}gCFm%*!<K5>XWmrU5ESXuB%4!`YH#4Px*?sAMcgf zf4s13z2%YbRD7*(ITjN%{{YNYxc>lsP{#iNe5>wj+IJa<RlSenEGkdqFEuCT6;jDR zFs|4C05a`=^D0+A^N}uNz}{sm5`R5Wq0Xi>>#B<;vN-V%Aor+jY}AdBhPAGf8hcc( z5XwuOTyEM<-b?ECkM#~w%N!GjF~QBO#NrcgI5%}&Mdw-Xg{tvh5+{ivdB2?=Li00z zaaVe&s7U_+6t1plrMTG|@#To2ZxrKlOig~acA~~IW48-YB}W`^&I2;??oE<-rN4Hc zSS`RJ7|Ai;YALJ^lrUC}7NbmaQdtP{PZV(L)~%XHH2(me;uakoe=*2H(8e{JI@%TI z2j?#~{{Vch!~OER5BJtBf4;k8^Hl{2J37?Y2u)jvtE!&z$$M9il`6X5>J<ZICxxJ@ z$pxd?XmC<BwrPW04eHM9lUfU?(NC=IRJ$XI84F26`#qx%^W2NNnaBM1BGTC?fBsQU z4US!G{{XcWd|B-T;3_RHK=oFi^$t<TWDgO@%f|c5&Ej{I6rA*zw3_9DaV#_&bQJoU z3Sw1~^zoKDQFY{AW18&hHXqeb1ol?Q81B$G(Bx>I`9qynv<ErZnhFl!taCBK{gScE zL5LgE=8iEe!>hXIW0R`vIE+4kXdz~UmP(@~W&9cc0O?=z8=~gP8&7D*L@|Aq-aO8W zN8%5bh~n8BYI(U<m{y6=?0bGALw^uxE^(t;DpQ{CVB8m8@qp}cJJp~0m7nn&^H1b- zHSNH()Fw#WoD?3hP;);m4vrU43=6iNN~LA+QHHpUuBPGQO#QcLIh+sxRGbha0u4v8 zM<Ddt4v9Tu^FWLU$udp3IB^NTir<k|U&Uw7O-gekab#RGcQuls6hef$J*+>XWQ;Xy zg+jCXhv%iOd&{CGhQ5ywtsjK%mW-93@mcd$o?otpNHWRXiUeXaQJ?Zh%}-Q)1ymf( z5-v`F;4JR$1hO~;3!31&xI==wLxKjk0KuI_7ndctyKk`IF2N;u;D4L{-uupb=S=nN z*7Q_&SNHVvba#E#E484SmNjo6ksY3AwFS7@aC7%25iL=?L5v1AQ#si#3U+ijr%db5 zh@|w_1-VP7Z8=McG)W4uDTGqD$IXjZ;v;t`>bh^~H;$<}i-yRuLYE79)$VP4mRZZ5 zz(|_Il4K}Q0rrGejcFRHF|~8;(DrR{BYT*BAkH<MO%feA*}eB-#m2kBDDgf@)d0i5 z%64h!Q!-W4PKL8M$jh5F%Zf+xUDwttKfEVwS^#o%XRbr_DONQvUWmDyDug;ZS4zIB zLR9?bfk<H>N}=sH%`$OF{WHlwNWW|L?JVEG3*%+7sPWy<tXyg5B(iYgMA7yf9l0i> z1N-S%w7+T%e-<XnVc|b=e#F2^pp6NZj*To+{UZY@x1DpgkCMrH?Ycr>FONimtp5*E z27(B?fX|9@-lD<XS888&jtwVdjsTdiFSVMjRq&m{RrW$|E#tf6IVSBXvB(o6qR^NY zr0DbMtk$GPt6r{LsVS1&#k;oQslnMQ1MDy#1nToI$O&4%(<r;_oJ1k2c1}MK&?PCL zE&O%ziLmwTo4>Qj)z9pqSX-!9NS)Tu4P68qmsDAR-Neyzq(~#?I+@z6c10ed>s<<o zN_s!otdoDGYRPJu?S^ddS@TL2(>_Q`;dsd=_U(~k8Wgfga9^Rg8Bi8S>b$4pJYYRR z+9}lRiggh`>A0ieG=gwN%o!#LVZWAz1<=6952DpbBnItSOE<(F77JkZ{yd9)Z(U#R zc&f8jhzqxSzXWYt#%=x?wNdN)<nR9DR8dZ0h`*X{oWy@oht48|uO24>CZOJUi~I-r zw?)J=d~+<{+tqqykNdfq?bQQw{Lbp}+?)P0&`b*&YL<q{Q}i_Bu}N<pK<KpMTF1GE z4oLi)<cipCtM|pm7uO&hRz}t9DOF+ENC&9;8%}RQamt(3Q4ZwRgB9x4FD?o?^>qf8 zL>fZPC-T#y2liREWuLPE@za9EvFSyRD*+*qC08c>G9YvUK-#(WKS;%a<8}=JTmg1o z4jq~!KTDO3ZI<*;hS{0cHcjM+s!St62uqMRD#mBCw5u;es}m-S#d<Vf>5@<MJ+MC0 z|2Rk}jg(NBAPwBp3G8>CkV=GGyHLUhU+ls#fm^t;^}|BV3&DuToaN7nyjb43lziU6 zNB<c~&C40_8=WIxL3O4bOBY-Ka4Um}cER)`P_UmBHwmY!DN54&)2suk@%I?fkniky zzl3xmuCT}Ms_uUwU>%1G3$Ljz;e6F+nXo`c#ACh}nt3Q<1-43@l2AdymG)fiF_6Mq z=K(mVGx<1z(5Kt6v)`%h+FmP8;F$|e!&49@F4HASLevW9h3=|yg=ifAAmLnt;UAwr zeEg&H={Fu>7@{q!2gp>_xbcc6Yo8#D2FXN{a6+<yA<J`UuG(^@op0pP&Wy{-o)rEx zDhVl;NoJHP-NL?8gsJj#Vo@;B1ltxs!<MmHeZ}9k(9qBb-+SXX5R^omAZ;G&L>za> z44btIW;sdZIu&SKdXI>e@+Bq7{SryxT4h4^FOM1wmYi^5`0a&Je(}__InL%+DnB^7 z_uWN|_BQ&Big`2ohd+c@GLrt9nycn6fOhE-%~MNiBO@j!pA)Y{26GoGj2B9MbN9Mp zx$Bwu1Ksg6u6=<3m9@-%mtt2$ovU<G$qY2=R1jnq!g0bbS0bt21m)n$CEaj>opscE z@}3bW(frV<ySV$Z4{JE;4J3D;$t1*E-tql(#1!Vz-2zB$;U`5C9td6Roe^Ff``mq5 z@28|ZNT-_H4xe_H4wdD{s##7B->O863lJ2!o*BqgICY4&A<jDh-5XozfZ|#26q`+^ zP!9%^Od<tKtG(r@9swMhAp@r55N$(7m34YWG#fLL`n7G@4f_s-WPbVI_CURQrMo1^ z((fV;iv4Y>@(4(~Fua;!M<qSN{}?XM9h_iR(uDNoC<%6{4FWE7$>j$2dn(c+au6P1 z_hL=Z=osvmdF9G3ScQvfrjyX;J%Wy7Do(lSkK&V$ULv3-d)fHV1*Puwr+(#9pdPo_ zSb}<hjV`qrP{AzQ5Gc^loQce&{AO0&h}H{ZE03##X(fkuFdFXzhGXJ7@|NsLN~<po z-t|&+!mRU%GQ)3573Muv^{AIwMk~Itzr_>b<Rv_@68_i@mE*cw?D+{N61u5#A9nsh zg3FDpv#{RU;X6lBhr(Sr`agL5X*5kN%Ide4*O5hD&v^T_Ag-x7gNf16(sjPoE2zAR z<u!8iRiB0D4>IkKv7_5{kUcNX-(eNKAL*WfG{Hw5o-{OOgl}Iv=!wMJ-Rio7z_98! ztGt?7KU=2gU7%5wZ!}?!qE34d0kcIx=DCu<I&B5=cWQ0yl0*fW=eG92sFZoskRkv` z-a}vgUi%-UA6+?6T<v|ThUlbFQq8`fZM>(d%$7_sDHWjmzB+YHb&dy);?@&uO7l8Z z2Imir04kMXb}LkK+rqe8wTj>of%K!WMai*6m}n4H%lAZGBixZ!V@!4W=Z`A?5gW+Z z#@3?$Bprv*!0&uWW_8Q{N;EGul$l9l@sU%s$Q=iLXT$f#Ed9lAS?m|mB3Y9+R+-Za z;K2-MRWp=0BySE#{=7L(?dguCV5L)UTXZsb6;dbnXB~Lsr{9on9O2EyTYgowIMnZv z>NQtNGyzG1EE)KO1=Y|8e<R=i2Wdsah6G6i-8x?~^h(E3dug>#6>`3MX`JTGJ94V? zS$ki<A-eJMNn_5-@?-coJjTVFWl?g}VJP`Y*WsYS{3gxnmYd1-hTcT1>0&}Kmc&Lr zpNNTM%xMmZ2Z(XecU&35i#rgHiMxmprzvGieuXtlo25DD?e@2^AQroakA2a9db@>- z;p$*tsoA3H5w2zbn1L#dxn&aJmA6Z_(hOIRwWZ_~S<_%AiI;-=t@0RbXFpP}R3dh? z|7}80H?K)-ktJM8PM0On`+|9wYAVtJc2ojR$(VZouhkB=wl|2R<?vO9(MR-3JKi-} zzuJt#6C_guSN9uZ8j{(M48wJ?w~X*!w9ZV=>#JSzeuCzZ$ch7{&MnBOPMC<=k9;x8 zSv^u9cqD~^xV$*UnLP-=)|Hq_AWc=aH#-$gaz#pQldv(wQ185KFVbVqYd-z35av}{ zM^zsT>$;ETgJG_7$F5=o#{~#iG#yM0y<xyk0ZY&6XB#09HtJY&qaXTB)7Mu&f25mm zKCR7^`dl&Du#PkNdJSi?0YQJy!fQAb5b8CQhkmL58?*gSV7ioNTp<IZ)9PvkHY)fk z%{)LeAf;rm&vy6-QJpaSz3;cC1`$_=k2R8Xon$%Ion*NP%1J<oA36)$UqsOMZSNKQ zantwU=>|$sc)Jvgo_<nh-1OK38tzeVCnfIxE4F&szxU+(8cyTdd7J8)alsmyW9PYv z>tD+Km;T=1JLuozJC7j5*=zX>eV^hCJ!bI?-S8MS9cM}rkrsmSFHxPL$0E}FY{57F z-x41YK3fKQ?n4lnsYFohf2h=yY)|;sB;em@iAAUQFOqKjH<7GjX%>1biem#sz(<hM zYO;BTKZDN)zB<|q*x@!6N8fMtl<7K&oz<xn$%5kBK5w)+-iEx<k^022yS@0#6zG9_ zvwa(aC=T}bq4qY%Gn}S%A3l+FAKvA^QO|H1#TRV6tEc}KvrVqUYk94G`#&-YGWq}f zRn&QB-QRqf>i?_R%D(%!r-ga*Kl9H=M;g!efu7f8Qi5Hr4=Ylch+6%(Dr&GGOg{c9 zh;oGw*fnAy5$dO_!98fF1S%)oTjy033I6!iZ~6|+-LnyrTP|^VmQ=ZHwBgieBMr(@ z78O~)oB(#wM8nB(o6SCN=eT0?zs5m5OB`dyz3{f|9?N_~Xd}?a^v;S*Gx|krqTpn+ z)euU+$k+i#sU#4JY*jDKu+=~5q)$Kn8mNk9{&v%|Xuflx9ICxttV!mk&tCictLIY9 z>vI#!jrO4)-@K43DfGQgoQdk**^lCLC1YIB1K0kuZcVN8j>H?bSJQJU2Gh7Gv_f1g zi$-9ZIvnPWOwAH=0g(?@4&<UmKW<@y3I58PC2HljF5S?P*LlXf7VFXekip9%YF95V z@FeYA=xDEL$Zr(^ae+)Y%s{>#XLq)BgG*Y7!>^3ms-z6pgjn3}_Ji?gJkpoDB|(~V znJGv0HBO!SFLtrQ$Ep<=`X_p5)b3VQv_ocj)Gv9hboDqAKIF3jf%6gQg7AZcq=Wro zxf~A1L#z2&t#(&qDu!{jZr|^qQu?LM=x(qV$9R=>B^0I)A!<0)Q)npcUr=B|LtQdB zYkIA_m2&mkW(87VyL4Zf`i^B0r@oCN*2+6~A7N=-R3VvwVrkQ<u#l|F4$GW>m85qp zchr@Xi8)j8t?9g@&BF1Wr=f?N><K#hD}8RT@`%>?4iGkF!-G*o9c?vHFNfBf-^rRf zON;&Py(HtlvP&>p?CcKSfFIF~=wKTDB($iqt0jQ|Zc6c8n(0+02F77Uo|chH{vu4- z7^;h$)hIuq9pY26PH>l^)>Qv4c%n_(eXrO<CuH#_PAH$w(`#LvY&;Oh?o|6evMDj~ z_ko(&HJj;a_#4F7+$YFB>TO5=g_s)FY>%&SIUnt40R=bafIS$eA8xh_rV8$+*8a8V z0{Tei!YMLniRS!H)(ecXtb9Xax=9AvvZ+kc-yu@nOvFOx@{mIHag%%5sIY#Q8ks3R z`;exbu@~y;qG7=3f_Icj>s|Yiq`E;ujIEzev%fkaz`Jq?zvFpykm8G4rZT#h?$Zq3 z5;oKco}r(Tf!N0(Z@s<6E>a_Up=#-N{*m^Hf_LPGvXW*E81pIaQbR0p!6EM-q;J|& zdJj(MB3M>^k*bmo->Nt?BEBVMFXW`H=^EQ*zv3@BG7?H|iq^VSvSsRC_=(-2Yk}cZ zokY^w!rpBID0EsyY&B+dKa-;8invv_iq=~qKPtKQ27k7|yKMhM2iPyTD?~f@x6AQb zv@CWRSg(ILiRoCcxBm#!k6iX>(NPwgL?4X2<E~ZcJ4gb5;vG?Q(uI82M_x8WM12ZH zMD2X^jGS@xJ}z?<3b>5fZqi2*oo)H-HL!NJ`2U4||8o8t_93oXI@Wo&-RK!sem;Hr z8f~u{cf2Tj_$MW=2)${PKKe+kwe`Z0|L&XL&&rxTLViW-58hzy%7m@<V^hFB(;f6W z@sF;P;69Uyo8Ufq`*Gl3W{o)AfbT&77lp-LC2Pc1L~6uV8C!VgoM&pxYefO5qmI*M zU8Br}i633`)$|{*p-n^l^>qcGe{w{azU!9m{Bg&r-csc{;JQL;z|ywik518z%y^5X zLw%loD(Ny^W800Y`$>}k#%SA(daAf*C~>F>VzVCmyUi!+7eD!sS5Vw@eh5+Dw%r)y za9EsqABUR$@Z$8FeEJsuxpQ=nG}yDoX8_3xQHGsBfCV>f(O|d)kH^*PJnyNHwZ0Ka z^k?*mM(-Fn^S2fmu(OD;p#|j#oZ6wfpwT8VmwHl&ksdsr17$aLs86exHIM<e9`{BI zk6`tt1&agfl|P0;KWR3-!m8TSC0_eiON@6lqGSWU!<}4{0~aP<e9>2RpLBC#5Gq7y z%kn?k`uWU7L8;ENPqET|B%W*?KKH@iFT6gX!yxSXk<7pA!ynndbrncZizt=e@3=3I z#*bXUrY!kf8HsrNw{JH+bScXmrsJ%$pNE&WNA$U;g%bPOMJ^=NqNJ|ulIK*fzuC5r z(tUY@$7w>Wd}VsyrL64_PIq>HW3ve<7BhL@_g;fmTrKvGALKWK;hMd|nD(*VmD?Y( zth8qKZNd@V){w^OmkA}Cjg^ePcMN_Mh<!JH9H<dvGI0~WKukQYI<_9m&+WbKLlJni z4DQ)TR4Q}$f!rvAWC?Z<x~dyBbkg))G)n5^P`O|ykprIAE4g%qZMtyg9Q5xoD?+d7 zq8}O_dkb7+pEe&#M-RWkjs8t}#|G$c$5k*Xjx0GgF}6aG5)`CNelCA0AqcTGNF06^ z<*{L=8PMY3449OcED^Q)p&(~*<#vm{w|n{muUgd(H}V|h_zQn^ti#ee&_^1JxB1wm zB$X_JYA~HOCdtCKLD9lwBXhYPI}@~9Q0?Q2HiI_Y$6OJ||Kr}+FSdWC1*FL4VjKk! z>s{B=q(v;g8adCW`S1zzxk!@^vmfrX#|AGCuI)y9I9ZM$S%_4`TK3>pOOP>JaQnLB z0Y^v`hN7{LY#6>5F&*!&)irdr(~FJ8i_>!CH(ez5SxR|cTi=GsUQs-5U#q9gF%&lc zm2Bc<vH^*!{6?0E9TZ!GM^Er-t9@m)NATws@|o&xeq(dXa%OwqV{d<|UylbzH7z0E z6HYJd2e!r_pC-xBrNX`FwEIq*he*HCH#Y7Z)$XC5)cnBTDTNuHlZqMlzudACb!0k@ z@4px*FbOK{SsNHD40>JYsKrN$tj7Ni%g;8>;If;rE@iBrNth1%(fR$Um5PZR$2hfC zLvq}4zMZ&|Bq`hW>za!D+F|n^cn@g2rb=)`6}-fcRj**9*zd%VKp_GapS3$&RGH59 zVVD<2O(g#^Lg{scyWJ0bqgo0IfF3(kKjYNW43}H^EAh+$?n=fn@OoBem>0h%s=0`? zVLW;rSbk=TX{o`8oxx}KJyx|hP!>S~IK56e25FArWODh=jRqThHeHZUJ0FbttoZ5c z`|IrN(ZhitYOimG<mA{%sz~7Gg@@vITuX9Mt(-zHXR8UK$(rNL8!M8U;)~s(EmU@g zx?)xkRtD(}e>p!Z>6=62Vb;2kYS(-myvlPt)uTcl!Xt~<;=rDxyrhKr7m)D-fY|Yz z_W{T>>VZqD8c5AqMeaZ+iR<q~)eAw`+A^V#)Fp@7D8lqxS_C1&1eh?gmi)$#G9#G4 z>DcJ#cljocp!TdwwLb#!obd5a-`2w(%C>Av^$l0T`{!SQCxOU;;XWqsiQn!;UQZ}^ zj9@fQj~>A`VHm+tKxEyK%l5)}8>9L8tM3}FIyKsI%vO#(E@kuBSajsL_E67|!0M^o z$PR@+$=WfhIC0ay2i2uqqar)8LEBM_^3#(kX$1}HOyhwLs}Mz=qXyNxIR&4>qGv0~ zx4tFrCbM=Jf;yE_15)%k<G<X}5_QN>Wm(M;A85p_2f<Q~t;LGAwnvnshY!*zT!oH< zk|7<IoI`HayJ;~=BUZH+3eg8IcvG&sPT(({yL=&(f-e>WE~+iv8eaQn`+udHAg~!J z%o)|8sgm550US#t%!$w0bujfAy7m?eiLCk?6dk`en;6V=#B=iISPzf9?b7@8$!VlV zG=8EaOm=~qi3F$8WiDZBN3?v+t7FtxX&@iy?>=LD9cWBy+DFN9O>-&pdfqK@wpZ1x zX3#e@QKtT$E%4+F;TDXiyc5zW$36nHy(wuaiMK6d$C2Z>U4HmNJrn^vp)|Xm8c{q# z)7+~>_#9o9J3{;6&pYQ9b0LL54j7Cjxn5b%4HqtFhUHtgh|X4I+lR1h#V4gZA?y2g z$SUXK+j8m%ygT(Nq2#`3!xU_Obk&N-y=QwSZZr@L6W$DfQb_lFiSXApno$JKl2F-L zUwyxR@kSVtFjfufL~CiHbV#V<%Z}USyhbj#geY|8&?cG>qr<haYKQTV7es70HzKuQ zJ@po`_g~3PA(WKh1i3-z{83JDCr|Txgy2WqpWBzJ)jMnE0QRk&DNRw~%$u-;CC<f& zyRO2(N{<%h;R=pbrocmux++NZ+<?0-OI(f97H1oFSeV6&3xNv(MO6ufYDB#qPcPIH z863yS#$C!9Ow+`Hzg_se7thA#RHTWO;=vPt8HT8^)<H6w9*x4AZC17FNdxO0{Gqp) zr!#H+R((ztU8;nGqRH(g92om;dsRHkzNM<Ab59=nT`z6YX1zzyru0X5;>s4=-QM>5 z&7Y18hp>CI>95#v_-2bBFsf5$*EPq8VwB26zr4qLRtyL;P7Et`eLtkRgNf(w@$d7u zK#4B^i9N9`MFkmb%Hm>MD$hqt<8zu-J~zW7m#Rb@oTubyS)CeWiitBljb`e^gfm+T z3swMGSzi(B8Hx5w1`547^Vq{HdU!_n2U*E)5I7WHPDm5<)$anKJdF8!gq|6{ALE~} zTbOXvNqX@hk@(c`7p;>=5y#XW(Z_YEdBHnbOlqs<?OcdE`QExkSt4)cQx<UTn3E~> zMgQ;NOKXtvP}qDF2QYsSrU?_$p+<CvI*Jai%>J?;gxQBkK%I4(e&q+eJ0=E7c!MEr zfP!OQhN&tg`VaNx&vid`KAXa2y(MV3N>^~<$3K9S3B^z`lwZ!K&l9B1y4TYi@X8tA z&I#nC+SYW;m+-h)9^3KkQ6%gn8<c3Ce9Q5@yhFi2rZgjX=%dN^$NZ-Z5$6{Kb}!1r zXedUy<A2|+@Y@#h@U-{Z_j5D)o?;s`0)g1b1znLq)SlOw?!U@;YxiMT6!E>nVX*?b zUf3@d3G9u|aa}oQ4xd{YW4kZ)rnxjatDn!aAGChIz5PNg>CN$TuSr2j7NeJxUkSgq zbeO`cu`kL0M)2EB4&CKa)}i=Y^F|)-a@#%Frpt~U1lJrH){k&bw{)=r?EBlVE+G2V zGEurrhi<cDnko6b1xf*(Y$4(OLZNGCmMT<>PhYeyMpT}Hzulx*m#*mq4meL>r3*r; ze7-*=Gn+#)?TA^NRtC3Q6om_KRwa6^D#snxNJ+idw&aP|j#unJ4Rr5=)@0=7rW<RM zW+r@<XeG75RQeRiB`_~L^1+2LYY!`XG7w>~iPq#sz^|GKAt8m+IlD7Be;k099^Hlt zQW9DCN@T^63rsDGiL;ji{ae%F_?Q7mF9!!q3Z6D)4}QheRj-h?qxqHIDhMeYEA<~2 zB4H;7>9Eqt;q6s$Wo|e}=!Ri`Ak=byB9Tb`?ah^Em&c5lZ`FEOGK#2pV5Mu1S^Y(% z<0qg8GkX(*9tl?`F4lYP(MUVbcmA~d(y4ea*0yMbs`Y#JEnv~UMbv_P&FLxS(s`uz zbhqG#qT9(dzo&riPRqL@GrK&hdKdknGbZZqP7grAQd~i?Y6=k^!-Siey*6Cx72>fX zwC0x>6Uec}&Y@9-{~+P7$tX^c?>Nfbxtgdbw7YiK042{MsmheRl&j?B5Jx`IW^iD{ z+24#j)3tx`F*&S$w3SEv%%b|GvE{<4%#>JiRUsF0CAWq%Dp^U*&o00(a|NGQbW4-& zN4(CNTfBaei?tlsFVmw}7300Dof+w|bj_X!P?}!zoftwm^DYq*huKF70^hS$^mN}O zDo#9(ima+MTE4xTDk8fJxQse&QD`kSl+jjL0(Wb*FCbWdvu&cKfw?+hYz9#r(C;^I zg$c4|l#R4N&sC{I0=4w#7Idl$cH)l0g>Qnq2gF3FbW(P#><9PFbTeu1q?edU9(kUo z?WK{-P%N|9?K3%KUvXgwu4I$eT8g%IF)Io0h&De*iz$25H<oqJnGK33cX`JAPwO{L zcq%r_Sy?h*gc`bfZ@&qMSF|pG;3M$*w3tj+;0Qur++tn6h!d-DWvhYRD?N;V1sJS3 zVLwjX3pr7zL<{ZxQo7^l!%Pl}O!r<Lzb#?RLyQPG)$MNh%+v~glCXWU+$q{GUP(rD z2>AMP`h^g_*q~|=2Ox=TlA_9wZ!5}B8v8S`C#905;|I$khVUq+1~u}9)+-ol-I(3O zav)E|k?NnON=HOcQ~CX}S89ps-du;v-7w(~>9^74#e{oYjVMPRf8#8_rY~a6-P<aq zqF0SB&BQr?s;ZL7@?yd#i>YCYXXdHj$Fev)uz0VTjbn(yWoXr^2(#DpPx8S^p1u{V zvs#a2PDwJ^A34;iH?R{R{o-QCGb7?2(|-NuoS3^9DTaT8NYo$Pf5I8@l?3vF251~B z-JP+&m)k(&R^2@#e&S3exG<s@ccl^=ZmO1UhQ;=+;9w0k6Smnq?+ThbIPbtdyXGa$ zUC#%+(_P5A=h0g>L<CFA_o}A)bg5+Xn9YBQ^mT#@-y6y9+HC(|N&{)cTLsYL{87}G zv>>pE{8>^1N9TtWPX*PxKyEH7+#*jkuiC@G`d=?_JB5Z`@Y=s;qIUWL)fxJMdw52` zg<}V45L;S!%Xu#mqfqufx6k&aJ^r^#RWHoUXZpWt`Cnl7@F>MlaThAZ01aqb>7`Qf zC5@du`%v{giz@O`b)~XvdC^NLG~uh0KR8#P2dU$vTvE@($3IxK?)O*6{T~Mn5k~p< zDey~yAJg}!e6*F6hhR_VCSW7!!ib<R{4{JoqLnO{_Q1rqbHn75f3WYxDf)gyGlI48 zSBmfEfB!oF8%w?OXd9l7Y~>%ayh2vILU5Sp*=|6V_usfTkHH33M}tM`F2WdQyeUa@ zcJ>3gq^Un%t=Omy0CIBJsQeaS`FG-~d>js?MRKto3+G-q*GnRNH3|Qz&J1YRJbxzL z9j@toJOazci%>;N*X(rnRh7*qn35gg7A$7|KMh#%tHwjLn&rD>Q9fH$R<9ew?;%$w zrg}6(k0a{Rn_<M%eKGv+qi3+Lx1S8aOro&>h>!?&^p18#Ix`6+Tx$!7T7?|vcL!7F zO!6}7sX)t_`Vp&p+{v%6DxRi^CRD_pi#b9Gq~*h0;+-3CkWqLnVxlZ*<iqyI2Kc<} zxPpqfWHm7@JRDChx<^0neO~mhu@kcQmJ((Ad)Ws$i`^AIj_IAcsrRbEZOO-}`STHt z2m@1W1;K^P1*+2U<ign>N*G@fd(^qCe7i$U7(D!+$JY8z2BcT#=x9iPZlK(!h84q@ zU24$HoVL~W;7T*U1R(bTDOGDQ3uNEg_8;!C`Y7&VDkFwd_a0|Gfy!)Ek_5iOPjf*| z>2w}oWo?52iaVPL@yw+D+k1A}k7g^T!8&V1!aEsS(YK(0I`x}_XVALtwWgbtAX|fs znx>oDo;L4K?h`yquB!Tj9!#MjE2oYve!-1AI^B<sH>ufpNHY!1n_K$`v$Lr1=-_SL z!Q@04hyc`GbAK88eCp+FO`~QV@_T&047k!ESrXTPUPUC4Zi}(g5-w3ZgZ>hoO0#8) zWYL1AWLp$1(>-HB2=DTgH-Lmb*~1YI!oS5n)wQzhhzhQB!4;gJK<zPdMNHEhFypn& z!>V{uO4907&s`SAH#J!vr(CyU)Qe%tOeXK#>7YchhKX-@nvQG2rT0>Y$9r=cJYr|+ ze?g4XdJGZpau_;FQolIH-^7gCP^qLhe1{H&-UQ=v4rBFR9aJTWrKRtMx25-UbNvpU zh+U=9&2qbPGI2aIu0}e8r|VbFb@FF~&E8m4P5y(FU{~7D#oS?U<fO8-TGjV7qxL8f zSe>T9dCW~;W^lOvHG^(qndB4PWU%1q+nL>&+$=|D&0)ro)N^6ppH^(QF!9WwOyS|$ z{U`_m;Go*c2gOE-3alzFh|<ob7N{Kk0y3rgCiqX9-sT8F=-_o`?&AEsc)!uLhrUzn zyLpjI;}rS=3a(dM2q26S%#*f$ZyqAgP~#@?q0_X)@aS*y8wfu{=%_}V8Ack^1Z*8I z68yf#!pUgff2IZ?c`i|NB%1oA$F(Ihl)zlytP`_vS!E=yKPqH!Foi78Kt?`J?0SSL zEgVrwPh}?Qa~^gEhYD!K-BzB7dVXvDTwur?K1c0nYhR;v$J_1jzG2DC+A!T778<!y z4#*VpD~v%d1*9cJ6}!B*WFfIB6+{5LRGpc4cKnL754KnAuZm7FO0@}XBwXt%>r)o; z^_vlNaq$v~J5<DImQ1ytJ*NMPQrmSRyLsC7^gCXKtQlP8uS|`s=ql*Um>x}Ac3-p4 zF}9@bOELw52Bsrm-;(cz-+hz1hl$VY2)x(RhZNgumZ1n{itTFf1AdY?d-M#{o3^6! zi$2lXlkm$+<wfGh+l9(bMlKXP;r2w5=WQZ46TxLG&lOZZ462-YvB;Ax`(Txb!5)va zs<V{!)Wp;B?&z;<JY_vTn~QMtd9_00v8%Z1+eA2!Ho#D?<i)rM+iGhCba|>A=sq^X z*khdAfWm3*$6(&Kv605^Q9KwaYNS~_o3(pya14e=FS#lk77dNRv53YwHB3;FsQ>N( zykW)FGATfL9mf^zps6s}zG{y!smjIbH?Q#c15VxLWCS>Y`t0IQpi@jSf}zxQ_%O*t z*d%XUw_suEMyu$h%<T7S0ecZ}6?tY{Hb6m<@V8ZUV|$z(l7NZ(M*A6Jnd+9&Z*W7{ z7KgvI=z=DH{sO3twJaxLAu_dA<!VePxr8x#V|^1AsLK7;MMo&K3O6V2>(dTD0EZeI zu=2*{;h@I${Y}}ER?c0`J!II1W0cl<FYgH5vN(12jB(tM6cabR0xu5sSVaeY2H3rs z$6xs6BEntwHm8#XKmO3Bbq7bD@?;o3)Zt;&<yckI$O`gf(Hh|y5z=L;l2AL-Yh~GI z!lBZ}tgYdQ`4y#}8UAGpN{U+&tW~pEh-YhyS5XFus`imA&E|JCak=FaKUeh>5CP+# z^5TzZ#+HM~(aZuJGhB5GER~j)KBY1g#$e#>I7uM4MtxuRsOtluX*XSQ#JNFp61`Ai zzTETZ8tt?yfqlR|P&LL;B+%1MK6xp$X3;sgHuhqx<dastaNTAVUllddMthCYqTZV$ zqpJsw^-bPfWmFlK@6xDlO%qH}L8C7nF&mMFPhXf;*e9K&EJ+>-w0-!vyUax<itE18 z7KonL<JE&1q#A1kaT)O<53_XkgFsN1a>o+L!qE8*YTF+PE`ii4!n<MZYwe{<i#;ox z8bvd2nq$5ul`YfMtsG2OO}43@;BBioy!VfC%vX{tBG#^8s^1XM$;k4Y3htTW8U3x+ zdT5Ko$>m)|H-hG3GBqc@Aoo=<6F@N^{kpqc3NUuBF}+KN*|t}-N1|h~1t&(8>8NFR zs($I+{ye~+3;5`GI?`$E*UnCAFGVa>^g=B6)wGK~<H^s1gS+bKUP}DSXm+1jdLIt7 zn#+*F2){s`qbk8tW@8Rsr`h=d!#3@Erq_M%5PnAi24y#Ja6GzIv3(>v2(DR#$3kpS zsXm=Yav;X95DO0;(Adja5G=X*EkTF7Z6WbZH(-v?m0S>}PVWd$x>BAcoLi>my3{g_ zrzG^MvaObwK?|E;oLmlT6zqtAJd{}r6#yGVe3{5}HB&YeNU)M%sUOz?NNIU+E&XlW z63xbij**>>!Gxp^Z`{z3F?k((NeZ5hBcfV!iCzFV)x5xv8h`~f#;`%9o;gS+J}(H0 zAMZKF!OD;PJWob@*<PwLDrs1yYG2x@f^n}t7<1^F%R4UxsCenPGu<F}qXPv}{ZO*; z;YC+{={`KKf_v8oGuCYR5Iqc`GdWm4*fc0dOT3PA(t1s>qbKt+Nf_kn^!A|eSu5`d zZB2&OHYz(hUZvU8ysX`6J}d3ApkU?e^=NWt#@09Y+QAGo*981d;^HR#y~Y<il_;#& zv1lJvg%<cuL2o~k<d*BxzZq?K7>S%9#akXQ_9*~SX#mxn(JVR=$K+|`D_8&vVo(F> zU(r(}1cEpgeM5p5Dq?uC+9M=j-CtnlIx}>ab@?hwFRc1&kLm#?I-T&i$RCYFFK4Ub z!Y18qreCx0r>2k5?#=&N1aV^bR`}T#dc8>or+<C7gCY`65sNvYUwPWPO^m9{*nBeK zg_1vB@)n<{Rt^o9^j*BY*+v;Mn+pK!Lik6eVr=!~DTOHkk1;yzq~;~5pzk%b*2WqC zIkhp(H)Ut*NOVJdv{ym?u<7Ei%9ka1-ut-LhIXJrt0Ka01O!)j=PbJSZFN9~-uLq@ zD7y^r_aT9_2z{}+>%an3XwHmN$RcLM-5&Zw2vETlw4x89nIG}7$EmUzGgoyP%qNI` z*5wfWG6iCDSG`V_gpR7O-7}26OS?+p{A}s?2JWc6a)Ytv<U<%8K0c&#${V1YeLRZ5 zHPGk&yYOk8f;bSPx-AqSZJ>;z2$RQNHe+NP^$h~oBOap-W8mZU43IQkuJF+?k);`o zwsR>2lW=myA%q|mKUM@J%+ah%>jZGMYxRW63}B-jLRZTwI&FWlzKWfVNLO!XH<F<_ zq$u$sMC@r{uhQID386f~beI+`7tws9oF8@SuoUlZXQ(<RHs_V*yp;WvCCV^C`;Ns3 z1-bcAgEyt<6cbHg2mZ*-*)1&o=GrjiVtfsRTfSlc)>%xh+b-;jqKgfQYLSE4dY)Uy z?3(MuBzaY<&~`1iF$mcPC>sh0b$y%owOqWcI7w*L@iE`IWRBbI6;&*fGpen6LmN&$ zY0f4;#1@tM4SUoJ_aYk~`b^6(Qo@-;gKbcv55S9v(3hMrqi-+2nk8iO8Qfw(Pq#71 z>C>Y8r#w5v8z}jKPSA3&wcER3>|G1fQ3@N&g6Tj6-wMq^BeItYJS)`8yC9-)9Wi&? zhIn&wAx6*tQ9F;wL612EA27kI?&(sERjXt-lcLcZ>h{&K1bZ^z?!7+ewf#i(1aVrO zLBxIh1rP29X&^53{27&C=4@<@kPR%oK~K~==?yFA^{fFLN(R%77%vzYiA6P6Bl2<w z$8NwM@|RY-!oay=x_a0ra%k*2V0{h?2L;`9D{f17Ufdpe>WfxUW2Tio5skPp*tz@= z>z<qRORB3MYSbxS?y4e!ZC4;$&WxOG3+4u*3hym|k_%wWkD^pC>$R_lZb<t%|FHF1 zJv_Z|YU6t*L&8BVP+XZyz#B3o=)~>XeiLqfo2<tyX=!&zc&=br`2A3Qb-0X&{z$+0 zFx$JX_X>Q9@CLcv%=n9Y)xpXbm#2))71NEa=ft6=aHirytwHX?r{J3sWsi2x3~jUp z&mf@`$RLCLJBf(nKAgM@?R%_3%QES2nsek1dKRt5kGrMRB<-1yYuP}-KL>brQ_>h5 zt=Qho{~#Gl&8df^I8+Xq-;l86PX8P@MnAOjrL)7chvLY;plm*sl1q!$g!`b&hDYbu z!VzXM03+E3<7$lJmzwSZ!)4T}n0kOMH=Ic(X2&_IxnES_*K$0BpVZoCU`SO=2~ko| zQhWaBC(^Y5R-3KJ0fi4Dx_~7oJ>F?W{cKQ<_WAU}Q=P$GBPs%HM4n*_WTZiVQgFk{ z`FW0_#F6s~f{SJ@Pk`I*v^#}PjAKOA9t}E}u+ArV6;IZ(wgOzSoptW7yi`Pw>TAJR zcpWZMF;&CNUfr13F;*8XXxkT!Cn9XAK7R1hQN~43JmNH%3EZq5pPh1u+(PV+rfwLt zLTZETtAWi)T8=T;5hgi~7Mgn5CWvO;A{p}KjnJCyvP|$^<>lC`#7X-P;SA3-o7!5D zPGyoM3n*pew5NjkT12iT{Sqnl-@4bJWCIm1arJWMDYy`bKAljZDF;HK^Y+Veq_x~T zp4!QGE9`^DK-%P%Qa8JVB)2!MwDk=+ySThs4?T1sz85)36nvx`msAzq3~n!$)m!=H z5S~mPEaGFqicxP#*_7K~Ny8MLN{K>}$gDP^5^wdX%EO%s6LTEdNw}%3RH=!3zp;l} zm6v=6B56NLC=adTNC3fh!nj2{04`$@N+w-E5LlFvf@z2`%sMHz4ouV@?}rZJc`TqK zD)w}Zop$<Bd4epjDZ4MZx{IeG4zVfM@qXI<_*s%50wWCV%N8wxoU}2$(&+elOe!w) zaMA0kK|DPCM<(sd_nwFLgpoTrjoWHCPstNfZ`pBdgt)2CY~PbjIhg18Rxk8;bz)T? z;dE_AYfF|P%c)YOauGEs{<vd8%sEB_f?gb!n|pFF;eZl{+4t7aSYKJGp!mYn^H+W~ zr??9UpaPxVx=74zBd-~0SJ*W)p7LQ&ecD2QC1M^DiQ;cK|AA`Uc5gb5{iWGU`UfWB z5U~jnZOkvLM$O-E6-T;gO<Uh6yuZa?{cK`%)Nrc_{Q056NRstO&Vdf&k~7oqA3GNd zugNT>Rpv4lwO=6YK8fRbd^^`+@z<5Fwjx_Gs4!=80608AxO~)GuvwHme;UqmpM?{N zn(HtcwIsJnx{HppzTz+kXvja>XJwyZL@M%v<r?5!QH7R^T@8spiPTl&%!d*{<Drl& zInZEJN;MY&5|rK%8dcEJ%rGW1b1XlSvPPW8#b@kPgZ&I0@n#(Ta;h}0!~;ZfQuW$r zFIuIHjm@5p>Fit1xBaC?p!g4xrXG#uS1@bQ0l7_i4;itd$ccVfu*ge-F`gsR_}|9T z7*l_65H=$&PD2tY0e#A6hCrgMqzNQar41DN1r{S;Zkd~~@W~e{L$D+SD$eHd5{##t zGSt6<?OJA9zaUrj$M9af4_C-L^z4q-TMJjFAWsd?ykfJs*#g{ZliB$thZ&WdXw&mv zoT(*XPaqBveutwg9TJH~9~=$~Zo!PHdeZ2kGW0lRkB7d(@EbE2r!no!l|XOpJ&N1i z%UWq4^H0mLyJGOoZ=W`ZMO9Wf_9Wl_s+(sYE#Re*LPATxIT*ghkyPfdYum;7B>vSn zvxa{qj7zR-V8Tp?^9$z!kn6oSv;qA~7%eOUT3sLt7mp#Kouh*i&on0$wfCY%@l?;^ z?Tkof3cx2~SUeLOE#t{ke$xIjanPMpxUxMijWtw(!s6vuO;*dIh2uG^X-0)&v!Y)3 zlqNA(oQ_$rodDq5e^SduS%=)I7Uv`-ob11m1q)ibX$?}^?c<XbIgh!BCLUM6E$zH( zJGcb3HyFdNj5GO5iq`J9FZSYFmSIFI$RpP03Pq=sZ8P$HL2GSj+c(MCh8g#4#>lV< zuT-13v0B^CNUcUsq1_3m!Y8jv8IGS&lRHSfJ~m2xQ~$WY7)@j*x4N6yV&$Q*rdgn8 zR$Ge5=ahYpFtD?Ak6kH|ywcAEse*+uM@`2}dh!{yCe1wPfX~uy4HA-$_XtPgZcytv zMasPX8ODjD9Y9>bk?y@F2%aQJ9xJ<95(FlYm(Z-t>^&^O#I7qTjYhH}#z)EOCVBN+ zy8^Lcs6pU__Z8ko1*}+bt_p98h*#b$9ELwa$ElmGmq&~qU1j^0sSvqJPPef}w3$sQ zJhUhmhd88<zQAUsRU+`)02?#@x?0JuZHx`wt3p@HY)X!xYUR`NAp(L6o=I_Dqz3kz z#ILejYs2)8AZ~X9FXJ9v6dzQ?mY*ao+c=cL0Y10cXa#&FV^MlO@f8z<J+>V{EV~YR z)g|a9YNU=0)#fAYA5>#8w&CfM91h}0m7&DQd<49=Xfbawo90UU2T6JG$YrgKo)cqJ zez9FxE007Xg)1o^38YP}!w)4OoTq=ukwRGP^-+`g_8Xe{6qDF2x;B-92wZT&Q;o5) zn6RQP1gQ(&XTj~?ExM&q8CMEK-|<Xy;Qgs5)~X~w*&nMc%D74If{}uXX0Uj0xZSV_ z_Hz8_tPT^Lv)_hb9PR~V7gm5=btyl|xWdT6k|!q$Tn77F9|~EY3ctip^g`p@gyU?= z`>x-*Ab=!eXI>Fg;xROb@fI5xX7X4sR>$g~<KePzLd=bHXxVMXcUrscmb+;IS`x>a z=yb1gLI6KJ+EUXqk;*Bm<D72zC6bU@i6e*ffsN+x!No8L_0mF8ssNv0JP-NjWO)ww zl75wj@(Q5%hwRODSU$><RX7q?(QA^iN}J{-VMCt%4Apk!O^^~x;~JS=#jBtXhXhEX zkGdaL$Ljr4g}ik`b<_RrTcFK{*od#!q$*P7A#W1-i740?acaAGPp<*lUeT18K}?Ke z5DD%Xh?mT3!EUY<V<X3dIQ*ED-n<P^b!D6c;L4R|G@kr6@)8{^_X71LyxXN0S{!D! zq-pVbBWf@b0fSY<9Y8WqMRxvEJXMG^0ldSyRv(K*>$xmhaTt0<^H@_ZyzYG(b9|$> z?Yg3Tf0vNEp!vaa%p&5MfuiLPN!<{M#qI(UmD-55tX?mi1IRNjLT@gX;5j`pDXJ-l z^BH3%<H0Rw!YrGQ5)fVdy08lPe&Gab+&=Dy>xBRuDGv5|)5DpQJ$G|n1Jn4fRIG*V zLMvy2PH>pR7l7J~cAE&@49QM&))L&E4cm=h0lD>FxS1-t>T{wmNZ#%ww!}u@QfxHQ zDs5puN$9N|ZtNVi>1NNY<ai0=3`>;<Amb{UWv^~xTcLAo(cqVypd2|h*iRi4#0wkq zXAiHWkM*yTbG|X~*IX&1@kZM;pq7QHd>okM1#IP6<0G%?xz>;<%qQX3Y4*uy&C8bA zg&Ckf4`T;|%fsA<1T)Swg}99wn8>V%WpJyPQlhK}fGA6-^{^ktu>GA{Lx9E{Nwo;0 z?Fkg6aSKZQda1%6-Eh)dLKevkTQW&FDV{L`o2e`J#o-JIvgl?i7nwso$kC=o9L4M0 zpoXffE{&$ytfAWp>u39R{6MgGBLkv6H38$<!$<j?mcmqNT0WARYeCSXPtbGI3jKaz zpwBvVuB7QQUy4wo`s{-?c)nF}$+iwk{%A#A5=HTLec+xC;85F#7?g<#$m6LALT#4_ zz=b8fajx=ZJ_yiXa^N0pC3|ePdUkLP1M2PE3OL*<lE8M_opXHA_|nLg^MJBtn)Mx~ z9iKu=m??%Qajuz*y}QE5>+1-!oH0~zpG-;cmN-#l`!Tw^^pOQ20s)N+WPxrM>6Xvg z$wFH(8#-Yg(!Cc0aYdI8(4}nWr0MbW9E=po&HMuV+A7-9?>WJ+k^+6@_Vr*wKVf|u zFM3~fG5<E&JV>-Mq#A3|zD8(rEd4Vi3JxQ(_p4xWZOP_B3_`idY;`UtKuM^=oHN)} zpOgh{sK7R|uKwo|0L6w|5W<KXMm}YuYKuH%J3{Tg`F;*(kP3ws6zlcrYwO|m*w3c= z(g3a|{5p+n5hm!qiz-A3hhPhE;sjm_Lw9x+;WX*ZA|;59E6eJfT~)D!`ElNOlDq|x zNGH4o$JmJ4GKg7sf$K7#hg08EI(f!Z)mTkeFIDk|<=LeS2w=3gukrd17r1f^{cO<x zwbY<rxnS#+_V1Bulal!&$c~R*TOk~-QH{U@Kjm$mJ|wDj$N66A<UcH0{|Xr~ds%(O z%z3iFqbZ%)&F`Wbb=r)oy3Zy*Z{}jVm$gfkx@%u&>Nt<F+s{c#V(2Z%tkHNKB0ayl zmQOrBr0fCq`dMazwL*%<u$2joD~xIfs2tB}2!v$pftLB@sIl$DY*2ay+Q*VVB}$WO zu&=iC2qk7GyOkW4G2-R({>-3-ZOCKSjZr|-ZB0Iio_aOJDeEWIYdlS>#irO~X-0{^ zE*!qK2}e<~AK}~fwrHV}Q`l%gyz+Wk2j@Si7K5r{i0VNlE1Io}Bx7@~RH*<QvNVEM zBt2G8N;~fW=uQ356CCqZ{MgfB&^Gv8$+xn?<6IAYYSM-)FiQD^a0HBc1WF(5m!W+v zn{W0iU%I8=G6Yp1;02}QP>^0wt(jgUcWMBGYC`3uh4k%f&aYu6aHf@giBA!=te}t$ z8G1)lzrD<fpH*6<tnDdSVEYt;3m2|NU59G8-ZxTo$}_BO_p~}7_16KN$imtS+8;Bz zObjVKRm{fjiOut(rT)o~2HB&1uIB2v_=qa|3?J%8kwRg%+NP=xTqf+K!7|O|mVtLc z8kee#WVui>d^<<EFxl@;{>y4R(|fsgupYzQgT7kyQZbqe#xl5T`~2kfSH#dzR|Zrl zV#Ddxd_$*78q;E;s(L|X1Z_V8qZJ6oK@$2%9y(tek4|Cvm?bGRUDX3P^X60L9vMhs zTS3|2RNbQ=ayn1YXeRr1CLRQQC53pjhng@10MV}gU_@r_1t_tY(NqYA0IK)kp3U|j zAk4S0H5CTWcJInY@PZ8nbb>lrR)n)M&6?)gxZtf^j$bJ&^bhGcN`=y3#$KJ8s~4Mu zMsERi1k4F&T(45b3m&5WT9E)SnjQyZE7-WR+4a8Jg}v<N{37ZVIb>I*?&gK3+W!H8 zTj;Us(p}v&=2t2dFuWc-6gTt0`i_gY)6ve!X&}BkTZerMh=4x^%MJAPQ(^)|;IvFT znZjQd^0^A51_+DGv(wCYyNyA*sP>wgNmkYL2-s;gak#&!3Pcx#daT(@Ao69e)!I0~ zTyGQK^}{jflGL8GDa?X6kGe$Q4aX&MGQY&!!5TH*2}R|600USpaBX^*3*z;$`0eHC z3`%YmL77|<>fivVV^TuJx{~;vBid$C=dG~|uo#eP%RFaaajunEi$vm(0_%{G8xZLp ztfdJCNpNew5d5@sc5|?}lHX-#+fL*9ZTyP@Mka-I4AQ1<Vx!ahkGn~XF}C~Bw%j}$ z8fGPzq%w#Z6?%ao!C=<tqMdq25pKd+5zb&)O<tcdYBIZa0lhk^sy@HrTd2lvhw~6i znqNr|j54Wu&Ap|P#yh%|=DXkTY;}Oq3{+l70|X6Kw_qba0d=}?Ns;H5xxOWWmc90( z9Y~G+Bdr*vo*y=3FZ1?-^IRk{SlUHbO{EoE%&@;;i}K5c09HTicY%l8Yo7=77drzf zvT$p&zE>Hdy@e5SjNxo8or&1qsm6(S*d?_|`ybVb>JgJc$CZuWoUu0$YxdKpN+m)q z93o-<&M`qUu867Y%2_^fszz+<o%scl+cv6ddNz?4KPgn!)+*=IA+44^Ll&{$IY9@& z0sUH}6-vHls&@1MrO8JThZ@$D{vdB%9`s4Ed{9!JO|#xgrU<UM&1$=j5}Oq6lKZp_ zbT|x3saW*=6uf+iV~8F;SBEXsyrn7fBT|D~h0F*YyXwTs4qFtoQ;RH)O&2|mq@Y|y zvOYkgU8fTgnO!@ATS2n1H=W#pfJRMf<7y}_V*nd)lN!2Q)ITUuJ?nwiLHb4<@*ep& z)duVpg;6CL422bT9EYb5W$`ivL>Oq#k$7*}_!kMv@bqlv!7uRh*$bv*6cJK=8MXl6 zAmbeLqAd^ECCy68$+aq$MH5a-5=HP`l8e^*2O}2}&9}VG=Ak|%Z9NAZVMb7g+ZKT5 zu{%Bi#AzOzM8hRjVE}JidMm#E<P!<0chbhHHPB21i~-9>&qQ(*t;%ks<6@&vU(L5@ z;)!|HCf7o&nQODgQwT(f6s<HX`F+W$M1ihLxDGZV#l;nevsOec!E%g`jOSKRx=FAS z7~9Ah6y|6SuX}>W3qHY6=3}qK(5X&-5>iwm6}ycHeL)JF<`nvd_au53Cns0&9^h;i zQ-iCad(P#(lkx{|^`0dV<$Hj#X}9A)NMAKNrc>enATif$l6H-9-GM7Eqo2v_{uFk7 zS?Xf1*%bQAly5*V2_6124W-y?Hg2X6Oi2V20b`o?O-B+JajCFIeyP^^4`a}CX_Iy! zNT)`2S^ZB^SBPQ79Sdn!!3iW=p6-cpQN5Qv;V{c|m&A5@etm@Nj-~BV<r&lIPg0i2 zm!^b!7Vs0Djk6FzXE|SMXSwkILwv2-`tYVSJi>3wPh|EsYnB|9q!cTXvT>6Uu*CXA z0Ckdwd{rEkF(ESHM*Z%McGvF_D@PX|9NMU+08QyY9fPMj0<t-l8tA`bONE;|ixKpH z&wrCcP^oZh!VSk$zb*VG!KK8sespG_QBV4L$LVK@$gu^)k8{DJ*@BTBA32?tn+q?# zk?`(AAa3KczG>eH?WU<_-U=7a%l&)ohcC~YrdoL`rXU1q=dDQhS>@rTskj#(EGv~r zH;~xE5z6Msd-!zHcMPz>uT{gzKE@%5je+o}xdseRxT_QYRxK`o?;Q+N?Bw40iA^gp zu}=`t%{JYuU0~_`26SX^0zrWaQ}-VTH;7xwp2ky{>Q#)++LA1(K72KgqwK6el%<NV zwIpR8D|26BZ(Mio!>#UI@vXM~$29!b|6LAqxSakxcdaiuckTO^#M9<>ZZ(TD<^EJ0 z);}UBmrUb%;B{b%c2DuX2NZ{U_F4y)Y6vm2=*7akEHIS#cCAsI^!|AiSU+;gNyXiK zYA3a5RRb+sW_})o;z;ECRU9^KxV2?UEwbE#(NhudR-W2DySF+*`k!YpHCZIV>X^wn zbJw<r#Jv&eO(F6~ce>MA%G41<#T^oX?D<*$`S79QY3^G0@&5qjKpMZR3pAeU#>+3d zu(HeUrk?_uo}#CNN$#(!N$#v{vY+HD>XHxTTDD2MkN0~-Ze#l9wWc<agYRTP-CPQM zc5q{Rm>OpP0EXj+0Ov%@RCK>3TN@s3(e3SDI|Dr;pq1hEb8~ZU7k%ry0n87j47y~L zirfbw#HO}eA)z#@9eDSB1Bk6#ii`<x#L(kagR|N;D6DpOK3im>p@HNwF-qQ$`!E@m zQ*2;AcF$4qD$Ql-37FSUj50Q%riPbX9oat*{{U_xZ2IR45Jo$(R)-mdA$8a1fl<ZR z3-4igA9p&Yv|2?;pund&#(|=w@=T4kin->1Q0BUi32pk)oOe|^C)xmWO%4SD4MEcr z3uKa6YjP;X=Ne{tE~~(8oqzluuUhZgu2fRAGDzi$m*S9JaIQy}_aM^X3CLH+hQ%ee z23lnmWF_D|!+`N5p3yTRiGr?s&DOV>DmmbplEm>=y@vBh;8C!PWIhxHOc%#lN>;ag zMmo;}W+B?~-?-x6xm+vFLj{?SY>l*Dg^q2<j4^Be@BU)C+g)dnl0z>Zt(Dl(xN%Oq zWa^wpYBE7U^o);^%SymQY98T@*VC3;RPz{waMTEe4Nns6kMiNgM`D)N*3R=74Woz# zOc)&CDAMml9M?!*w*lZm#(J^Zp^msn!&;8R4XQIOtE#_el6JO6IgT;K+BaMQ!nrDs z6d7a<u0t|?h%iZY$1B2>QcEMl7{-@9&NK}J5=!YvY+>y-M~k=cGRAQ00N5&?+ey`R zrQy}#kW953BQLjAmjDMH$<nenFb9_FaOA9!n?0h@HKe>n1Pl&)gl)Xzi2{HsvO80( z<H35%S5`Wh{49Ap<eH7g3^t--iq#=6g|RV=Yn?A%^BYS*b{LGELWQOn<&Vlq8{?I? zUmN0e$2GuL5Js1ixn1C-mD<8Moa6kVbX?N9vfAA=@*lkRIQ#|!M@BeiBa`M8ifLVX zNbf@PyvK!mzrQx~fV^lbb2<Y{g-*)psZ|D}`-fH5HvKy_74FapFhV1nvy_gz^Ibdt z0QQ^z08+KvBzSy<j$r$s1!su#bDvkAEq0)P^sRq{{{WJPFY5DyAN`{49qK7A3;q?_ z+PY<u$9d8kGYpEdwfXg9<DxOr9U&E*$=PYCaU19=Xrk#`J*qoJj!1fk%EMlz*QzgW z1z)J8J6si`rENB-toA_&fs)8+*El+)ux3C~4v*;wr!nH@iiZKdhK8TqI!@aSI>=!q zmDuBeKFY7_TXnkB`*o|WdnM~TC8V@heps%Mw7Tu4(b4vZ;$fJRR!K?+5s9L;^u4E{ zSr<jcV(TJx{{Sg0wGH2c4R>U$7K>vn_I{7)DXt#_mBQ2`Y1S2XRSUqnFQMagn#pEm zZI;<Cwn`0#qaJIdx?Pqi+iJ1AG0_rCWRfu(cCL)+H}n((qwN=3USv1k?@I647;4pj zN7#BM&m@ys<9lsRbHt79(*SOoQ)-LC`Xi%SW{|cB+cTdd@xqO1T@7JpN5TDTY=yE) z$t1fBHQrZCSh`xx1XI{7@3a@$r0XMmY!x))is_3)oeK*(GG8RwTPKu^hYVZ~XlyiH z3HS&8qOFgxSjDE)Y@xA6=Ui<}E!J`SA%93LvUFq&>0XSD(g=YjIRg%`-(B0)m!>1K z!s}$Vp9kt{>zC`_lC!$f*0y6COs#G~#9{?gZ4*yw0iulzkF9GD+RF2Jf)_~I<a4Bv zw<8XHYmRxYk+MT*=*Xh7^n7-fw$mJreC~C8n@bL5H=@~T=&p}1g2L$Gw8tc}K1)k4 zIySmD0jgEDItqx(+riAmLo4nB*|^YkLvW#~H7fi1MeLSI?51lYWo_A|gUUmFq!Uqg zxDi@nx?ar7LqGK+YJH++$kQbRwU7d9MzO|}*sQ`%m2IJojn-JEWUOHh9#>Sb03Al` z^iwWd;Baw-HjfyYg=43zGET7@WL^^1Uol?R4c<oOHLfm{vp&xaI>|(W>uZ%y9g4;O z0H{Izvc3<lJgqP{cj%q&KA8i67-;E-MoExF);NsI1b{pL0B&oh9OBRG8Zbs3pHa5w zx3rC-%vv6MY&ZvT98*N*<3<;p`}7<FjtIu92Y(e~9W65=J!uwq_0EDz&lg-6<`%mV zm{yy7mgo!)>YWrlabz(!qea73hh2D#<E$_$&u6jQY_Bi1vPdBo$2<wF<h`Ocmuk|r zx-e3<%hNUnwo5~#fA)ZDC8gJi#QH^A<9|w7n)@qnP7Hu`V=B9KDx#hVTO?98^B&Vs z;C6&KxigD^JN=5=8?ozRcpNGhMA&5p{7<wpyD*K~Ix(yi9bA=m!H7(4Yt~OJt*+L% z;C;0I_iTbWT^r?v?PJ>F($^7z1a4hD*IOrbw$ErVGDkd_0i1v_IN)l(q|sWflbdyh z(C9kHNK40COS;D#7xW+c<(B(prdPS);?s1r$mY9R_%#^pR!AbT+e9!phS-|mJS+>t ztQcf}Mg0@$3G5b#r0_`I>nIoH4lZyErq?T9M%p?qYXzzn7xi*DI-q6_R3fU;EsD=8 zq;M_k*-z}^o^aP<>1#c*!E19{(UzzG09eZF&6(GZsQ&=XiooCMTUDY4Nh4;ogK{>x z&aKvy+B97j%QcQ`b*rQ!Y?gaGdZe-05>86j(e|E?n!#?fi_d~W9airIb6yubZ<4PO z#~U+qj}wqkS*?H3n_Z2w#AJ?p?UpJ5U1BLk)3))^uZVPOTuTgq^_N;*fu<QNFHL_{ zzU<bs2UXUJww{$~!C0ZR^ewK<WVR42_PH>=9h0v8oL5M6OkE3UwAvs8s%%aK%4iSr zfDUV=Ef$z0u|K25Z81FFAaM4IQ+kHkKlFCl4@S!+#@g=#{{XlB)r!jN6t;$1Y`uFW zh}O5N8qnRhh*lELjJEVYTO+SXU5TYfS6TuN6;FMuqAeB&G2wx;8kdT8#{~yQSvopy zkhe<P-ArUQ=;BK0ZmXg#BcyBtUcgAcal5K`ZBg3h=u1tq&}~mAr24~h8n(1>+D?e& z@FrQB*>4#GwT(B^YUywz#%aUj#9{%eJyS<hJQR>uvhWm8cnb>y@o{$YtqgBz(YOv| zC?S+xaA5>)s%?m|oj{uZ09##)ro5Q)^y;zCYO^v%-E(6AjP!J&w1*C`vCx-fW4ndk z3AU>wjjv`%or@-6&5s*FM%-NQ>TavT18-~BF9i3#3D&z@tuD$q!PJSH7{Raw(MHE# zCg(bL41`qHz0c?U!q;M&y<G7*hM9`(973O>Y%j3QWhLS}L!EW;hvz_YR<4P%IyvpO ziCHX;CgTYNU@NcyrGn8sR(av*_QK~8);96gryArHlcg;m(B6tML?d@vZI|5Y?G35c zJd^rSwksvR$!dZ#3oVim+coBl;~QGPq`eU6EY9$kM*BRXT3>K*twzB>x(lMAiX&>X zIi^;bDA7RSf%dQH{{Tt0IQ$yPWsW&dS*#q;>d<Y?P17@UcSl<76(SzGb9NTj3xIH4 z57OQXV3OYqA(pEYy3k3DXbI-JTy&2{bWMSlt1Omy<FUo%7$g)r`?xEAK>q+?@IqYn z%Y5II*0%@Q1nn!MEOw@}O$=9F8gB~LxvO_hba=9W*c;S|UxcRMVPk;AS7BW<)15ml zo*AurrGet(L!DAd%Ww$vOcOS4jj-F~x9aOORAAoxG<8zB;gzWkacy`SW;iQFk&;MT zGXt3E0BLjCm;h|1O5bgVrtLOQ4Y4-KXpT7JaC}2CSA#-pnsuk5x_Z-LW31L&JE9Rs zC^6Yvb)vEYRiFpbNrzP*H)q@w(ZOqMj4?IOjz$=!Y1nS=;+xW}w_Ubs4z8g2Ai~ny zyPchL!E}?`ZhM<*v)b(TrZwO;wz+SYaDSu4T4J~Kvz=(Q%Vs@xq$m3>4Qm=ViidMf zve_->&gS%+ZE-%0cUI<~ClwE((St_iY3TvdH8#sDV3H~dJE{a#xbejpzpW&iOO_Tx zIAbWmU^R<WVCo#}n&?YIx>ji?uy{r1y|wE_q1T;UaHc~<lS0DwJk)`kPwazR1Bc#l zpb9>Zwao>|<9v4|aBo@&))YDBq_p%6t&>Ai=;3?%xtYPP$n!(`Kho37D`hWaMq*5m z%N}eb<3mi2ML0rjUUj|>*4L^r@!8J0EJ3!%Szl+DtZcEf%D2>YMbq#*Rkxx~^nzD8 z)=P1Y&DAuz2<`3;dz|5(BMPalJ`m=*PLQzL=osAWO$=*!8Y?t2bdiidqQ^r<;~RC1 zue=YePQ}tpHrYC>K$oiyZRbK~b6q6~G$Ge=qM*rCX46$r32>;U`li^XbByvbo0%RM zb%$kDi>F^<yVcJqEC!7s%`7<Zt_xZVKp<3$mF}Xlv8^n#Gq)W@`Ejk+k=btXnND+B z127E(AO`R6ma8l_84pF<I!?`MmQhQfH(d7;RkqJ%u}JQY={rr1Yeb@#T4TZ0U(ufd zut_s4)`DcYO-9~kb>zAmrtDo8CAugg4RmpldaW+Q9uCcaMEXqhJkZA5I}M`{;R~wm zR~6Eh_t>Rvl1n*jCp5ZE8Hd4it&Y!5wre|@z)dRcfpnKa&HWv;!eX#mdbynD`B%Ku zFgJcG9*-RxH--yM;|3S0e46IJqWvPyjFQ$Yy&nah)njD6RJwy>hdqkdZ<G2nCq|B{ z<S&}m`KEPgQ0AK9hG<0>LPyeLu4A7IrRtm-Jt)uH+@PMx(cK}@of^g-zAIGM@6;Rs z8RM4a{UqpibPJ>w;UE^G@FQrU6(4G&A-!~T7TYuI^8Wx?xRyUK9eZ;Aj@V}CUj~B5 z)w)Yv@aW#!4Q8H0E$gSP78?hK$s8vAoIEkkW!82H+86Y`(`|#mr|D|-F*{+u)?>I= z1!De;pXlS_Fv>4l-1hZXT1ov^^f~a@<eJN2vcVLyfG&=%@Wokt1_L~hT0`9!?G~1i zg>z1Ix1r_e_cg(Rk~lcH0oubQ2lR%+(XQzToex(mF}Me`-MfWXPjv4{Sj~~v);*C~ ziL14UIMb51M{co6XM3$zn%C12a0=m}pVj8jX)e7@W2WC?>4}Y{?0hpGtY6O+(XMlj zy^f@8s<io6hMRRHa5#pQ(jE&d<g=IqqLQ`8Z8)nGwrQl5k7*W~yRvxxh#BqDGE*r5 zy=5zo3#GwvAUt7SYp9-g#UEAVXlM(RV$vKW1dVLzu?AZ#P}&+Y5->WPs-0$wqq;Pl zYQY?2qtRFVhlluIf_2zmr7lxDn{KDI{TtEuBf;K9ly5!Bu|XY@$<`jrLu0-BrZ!pQ zVRvLXz!S+`lF<}rbDkUXMWO8^07wUV5E<hVpF;4x+?dxrpt#YXocSWl?Ysp`YmGOj z^>fDiovW?}oYRC>h~y(M*9TEDlCa(+409u3KsoJcp*WggGgw=!{{Wa1S$(&hDg|*M zc=FlpFtS*r6nh>j2uzwTgR_UU)wQdDJe+Cta@j4GcFhEoy&JMLHQ1jYN7!StwdSw8 zo2k5=F}lbkkQWQC9i}?FPwJl)whp_hacQ0mL8jUjOFfQh9!IA}G;=ts^L=6`R+S<* zM004)>)Y{BwYAi3){}{+G&)1C7j+HGxYew5d;apY*!&@y({+))=``}4X0WfxM>{Pn zj!vnu$a=ff4R!rW<z1@K=FA%RTH&;^t*8!$?E&5m7e@zL+}SLqcZnkxP5I4STexCH zHz+Kzk#Znf+$lj)K=FD$-vnjjqveyW0}>;Y3cHXk#aa3~+0nL%WwJE37eqzb8<@R& zFN!@b=&rIWOt#BQhh?skw^%sWPe<6{08~DVoPDuZ5FeW!AX4mBoh@({f2|Z-v`B zHp^yZv8BcRWKk9w4S3Ru#}Mf271+AbEtkWmxjc@ItZk&el@4hK4ms6WB%aX)u1IZ` zDc#Z%Oz|zqA`En5Iu`bY;A*WV+c<lj9C4D#OPed0Ni#SQDEC)E`^Jg(D;wJTYr9Su zh(Z4VxjJc{=Gia#Y2BQMXbL#u7h`KcyY^|ra=!SZ&kLhErbNIR9%(?B!IMQl{ZlNq z$4nU6M1!h*!%!*usN!vIv4Ntw&<ZP8O)=B>O`(Oa1^!NknGa{tF`XZ2k>T@eM@?QU zr)4cS;h2^lWUh?0%U-D218)K8Crs8Ju1j5CR@Slp%cURD*Tg+oEW;;lqOv~9*8*ey z(kVveiXIUdGYN?fyINy{=|eFh{FqQxJX)&jG>`{CLvaM|Q1!L0G_Tp&qRd+6I#})U z5degT(as=}{lFTlQ{Ni|2c<RS5tB1Ap$F{MboG(UcU@|kh8Bhs)&d$Z?pC|aqs$Co zj#x~rv<hUxF=?&}Yw>k&Hb~kbmLoG}6zs5!LxI6oXG3Qm31_Qjhpx)CXgn3Y(!S$m zhRxOL97PV`rUDz%KH41XG?5$Tgn3Bh846;$F|pK0*Gl-^x$JF4d9IRknBi))^ljqM zdpgv()5%%Qf)dunW0kYfg_sa7u>I<;iY#+vA_p=?wi6kQQnhoou85LQTGn)g<;Lm5 zw7M!-_2V&Gg>IU9JJ(Gy_GVl!jiXYS81bUC$urE4f#%B*B9w|h-)a8tMNT7OqVQ8? zZ&g8tM*()V&a_b4;tph;bS}W*=OSiYmqr*sXN59o<pTgjW2|ZB4SW8Of&m0jf{gCy zaT#%|n-s5&hxuA)J&Cqk1&%v3E%X>EQ@rmyPtfA$3T{)_8GOKHV~BH!CiuKXbE0LL zk+uj}(AKgcKnJ#}^m0kb#_;%{2pOi$R~HPofBSqI2Oy3a=BQc-+^DDk1A;VY70p+x zZFwq|G@eKT&}qZ_r~kwNK@k7}0|EsB1O^8P0R#g9000310uciQ1rQP;F+md`B2g4D zGGTFnKtfV-k)g2_BQrELVuHaWQ*+VbLxaNc|Jncu0RjO5KLR7Tb!+!0w4M~{Aq~+$ zwa!o33cg`LD@Z}1JA$@ay~yhFQzs^nzOmhOR9XX*xlo$AFD2J5i=tzvmAdgS5;~V$ zr5(=;<)1~XWL!#Ol|G=R<ZIJ&8VgR!qndaTou2P-_b0bY$WHEQC-F}9kl&uDY@aAQ zbmyU|-BWfurqOjr7*#1bn=R0400k+{5WGqX(j_!1e%A<xYC#3jc`msg5aDmALJcZ1 zDDt$UoXOo4(Lx{vtxA<W@|ng{oTgPQB!#TcIl_er&626K&ixlu`AuU2h7@Uzg$Y!= zOlw>14!%m@hSLm<<p_jGF3c4!lwOz-R8NUP-2njw(=NGmT@^ORaPXyWj_6N7iBY<M zqrjsm$PJVzP>PJoZweG4!aOG!XhGAQsZ*F;RY!ztdzWkzYJ!`}Yd}S#l?d_N<e)j( z2K8Pk+^S=I#zjg8yCcsiInivVH%s^7KA74UT({Cz;Wy16%^B*4c6);%lf9Ck>;&H6 z?h^_19#3_Z3rb_!;goK>FoQtLLV@I=Mos5AT9oHGIncJA30*yvsPeVc1B7=)h-wJe zF%X?sNeEVzvS<^^aPbt<15qHabfL#_**7AGcSBR#E3zmK<RUVuPstJ7jim<=sN4ep z_sdRg=O-McN^47zW2Yw3c|zKsUDt$kO^8(}G2J10GrZAh9OhaIG%A-!gl+3;_D98N z07ohsk1Mh<%F<IqQ)pCh*do#n`IRVlN*o}%EnBI|RQCu(!hb!=8^}%&N|kR@Q#<-r zf`Eiml%j&7Hc%BhW1OeD_UawhHg`{^=|njWsk<(zqjgcGkmhw&ovTlpIYNP((BT=B zX}L3m5#uO5_)iJ#-?>A=3z0H%qg1io_8sznw3^@oag&%-c*yu^_lbb1zf`Kthfkhz zt@nPFRy7bZD@jl?ZAySZJ@Js?MAU_$8C?}Oc#4G?ZQUU`sm)JrZg$X^-9{C9I$8+% z<w4U-clsmkr|pr=j)hIqLaDn791iVM;8azAV=$YqP>BUJr*MPXPQ`5qd)APv*U?Yk zC{}5=)>RjFtdNe`>n<?g#RfQUsWviU{pi^pihnDj0IkzpVLMXYG#>RvCxmmirye4l z-Ay4mrLKi${#)PHi;)DV1C<O#tFm(3DsMshsg&PjcWe70Q&m5Pa_EO45<`&LJyP1L zYmB4H4_AM~`Ko9B5g!#|=wbMxWydWbtsxPd8JSwi<sVZy4BpiGXr?r|(5f9SXiYK| z%_(lLB|x6lzN3`ZC$V-#HL#o|LWIhYne2}3$3;(seUp$L1MWhg4{yae`i$i65O43Y z**H72+l6q_X>27iwFL*aO0{d69V0Q%aAy(f4pN#`%~MW`-}XeSd}Y-uF@~4{#wJze z$6-LeC&(*INY}0yX#p~?DpR3Ezd$}p(-YNQjWn9wTS~bhl%G>bIzq$myKz!j8>SUJ zP4%ujI&`E@B=qxD?}Z!A%u#P6e~Q1qb+KeKR;lactT7Ks)(dpFeu}mt=3a_AIV}@D zYUb*ly**C(4>WPgleH1}s~fX(NV`Uz<D9Rt4@P@h)&~+LVfcZ$)(doyukW2~84cB{ zRfZ+$4s%G-M;oiGN%U@oJrgm!`Q>+aO5Kv}$>1;Bb!YfZg=c$8<QNd)$X6KEY89VM zGuij582+b#_Lk3CScWN9*~|cHk>;C^*DeD`C+#czR<*TOv<a5aD$e)Q7)c%`l-y3I zr}i3tv`npU`$b*iuc2`2eV8#FRV;gd6*wN^nC3m9&D<Up?kMUN^fj8c-B{iFRW}mY zAn>lSXHc&CeV8%tU$))<07QN&m2Fn-sCJgh{kK#8i2PF=9LBhCMC38YNSY(@RcZ=! zp8o(LDvmJvm8r9`9w!0Htx!{>_n)dIHyeJTG=xu@C4Swh{{TdOD$f&T!}J#EcTaj5 z9M7nFX?CT5{H1TWP+{yvLDBHBI{mwCSl_P>{8k<S#Nc!<Slw?OI(}P~eM_<QV$;5X z_=Ry$RKs5X0JTn2E4`GeFnxHRlELZH7W#*@PmBKm3jYB5nfuKB0>^ow{z<{scU0au zAUouF3gyArwKon6smJ88ryogZ@?xuY%Nlf8+EXp_g<*QzjbKvIr{Cw6p>c|XJ8S%! zt=;JOp&bi|`4x`wL;RDP`&zd3Tf>^y5B3%F+WYEG91zU0mlJm$O$wbFevT>W@ab4# zEx%~^g?Q~albz)Dm2*?l?uTbiCIAZ?5c(f=T4FapxTo0-J{HU#7B~FWKe_(XNwCB} zxBmdiL;BbG{{XD959){SF=I5D=;86WcPj^f{6|j>c~)_8h&k`$#8wpk%JJ0C){*lH z_xVcy0C21i+O6;l6|33VxP7k*6dU_7&z<{8`Eam`+U+B&3?ss)PgOdH?0{9p)ZsLQ z?(Yfz02NN3XMaJyPN(^-X11Glv+bYADzq9f>JICu&(Hq=;;^gQ*)a73N%<=qme8%K z&7TY`J+7;M9vc7><L*PceM~F=0Oc!v!mTNGn03(if<8+Nrs|iBo){%q{{U1!7pDhZ z-=FSxKH#pflSYf5+G>uFt!nDR?iuz!La@7bRi|>-4nW;u^`C8YX}^MC{8j$|^)vUF zvEFEZlDoF;tQj8DSP1+TwE9&KzTeei)^Dq(qXdv3k8&8DQ9#mj0r2Ubs|0I-tV>L6 zuPXLd)g9J0R#pzV*W%Ns+^=k1YUX&gPgsSC)a`ApU*{^Nw{M10A6QdqAA+T;wWscI z{c^?Z>QsuX69NcL#jaaRv(hFadaNeSpnq!d8+cXAXB0ba+BH+7MxU`wt@s6we-e+~ zDuyjtt))-18$ej!^Hl!l`&hlNX+v1{nffd~P4yY`{{UHHWQ;}U%TMU5SG~Q`(NAx3 zuFpim%RaNV{8gSB>xyOC^zR%>xS;z>mhUkR1zk8g#;F}?%E0}q-vF_yx~sLHWPW7~ z4cbBNw~jQd9-(E)Vfy3q2=K6)df!>6>rx?G(tS&cbdMVN{Z)VKBmUz*(!}2Cx4K72 zXjL#*Hk+I)ObLxE1*;CLO!X2L6G@M`hp-W(@|wmlLs|g$W%FC7eFIy&NQ{|RRIZs+ zs9Hc;pY~M0?5$Nk%GCb=vWEM@BrQr5C{Uq7g;DxfX(%~JQXy(mrD;wv!fjoQg{=oI zTk(^OC{dg!QQw6bIRy$mXo*`UQqo+b!iNgDd3b=Db_!JIvT)r3r#VukJS{vSGNpts z0*Y#*0pZY^$UfAfwC;mb(?QM1dcoW|b;;bR3UeLWgo<uHK&p6_BT&OeuY*;cdydAV zt_Gbx=-E3IysJ_uv+|Ou;<r?)X|CUn&{iRG`c}d8$lQc6EJw4~q!#(<RyUrK!?ZcP zpsq1$uB)PhesLtLTdYgc7|M*;`L+}B3#vDoDv_pqx_PWi5UD}!WjLNn^oLNh6)SsQ zU+D}(60Aw4-M!6~Yjbq_YVN4mECx1?0>dw>*Q)UHHfnK(m4@HkSGdyhBS}0foKE1} z8gxenIFePR&LnOc%8c0cErnM9028M&$P>wGrGJUkg4Rf$Llz;4RHs|Aq&Mn*DwWnM zDzUF;w0gHI+(PD+*<(nJx-3%ewZCPeH<?&&BKp-F%8@*^U0+;NzHK&<rd!j`EKd63 zg?6=sMv37&62xg~FJOb_)l%@BIM-&!{{V2Ah~iFZa1tBOg<<%Iq-txM4jxIkh0pB{ z%0`~hjus_zR)rS1oa%)__20A2YMYTMxA>h|G~2N9YPP(s=(Q@=Y-#x+h+<WVG~2N9 z60L4-pKV#y8wIl)LZ^vhH4HRr_%%_uyYxEmsMvF6Hid=WSFY-s$l4W3ji*V^JTyue zjwM;gvTnnm60qv`RYUt$#2Df-9rGbHA0-DlC^=4HR{jl7x>cQPiccn!br|xMR<*@U zF0rWHE}tmdWr2bF{{TM<#{lgp`N3CH`ySuo)Z4nuv^0Z!E<CIz+q0prXS9N@C(-K1 z*mA)GeI@?pZI&@@u8(LrXFmmoP&XGk-?(Id>0^ZGqwy;Y09jLyf9kG(maX?3uutRE z`>FoJz6%EaZ~O|p!|rL<by`wH&fJAzid20;POm42ENA&Y^9u(4Q2p|;EIzZV8nm~b zM^$NxR&7Aa+wT+9Wr}}LclfM#{{VyhztXs*>v!AMXVz<7^H()nR{o2}4$+o3{L??} zkJPKPd|6Aj2S?T6R5VTudp~9eKNUjmwHg%~*6jXD!L8w@tovI8-|GJW{m1oK-}21+ z{{ZP(P!+Jf9%TNj{(XzQhvL$x)@}WzMxP1btrylWo(G>GsyDuqXJ2S@SY=t8u=Ch+ z8>zWCZGPL@hb#}<t?&x1F;a_|ez1gTUs$}6$C%s|hhKU~G9k_76g!7Ddn-d!gnNLX zbE+nj{^?jny?S-tUre{b)m5h2l-$F?2wP${l}V-v_kl8no*{QvRZvH~?6E2iHf&`! zlLTz7>|Fdg)ZX1L)JtVyUX@6iZ=~SU!Hx<b5T9dvYhP6mNa#lY0E)G#id48~Xwn(i zXjhGMt!jWImaHCyYF)Or7S5hk^{=E*)6{MiFWR=!*3)!+C0mQfT378s)IovrR4L*# z97`!UPq|rCzJF`mH2XVk3kkNkr*$u;;5437s$%r2I0m?hF}9^+V{+@R+NjgK{7S`X z)TwI?X(v!vRXUXIVa*`wD&_d4JBRjR#f0s)rB2TB=I32j=@Y!|mLqL&`{L%)aULDk zF-EOU$3qLFR|=)CYlpI#9w5fY#05sm>{g~t7kfjdSiQA+mklhYTr_m}s+93dhOv&O zV0)oH&GhiMQXVIJzAD1a#@4+$tS!_;O2eCXZ(Oz1ouHln0P?TwE(OPHNKBdAkX1%v zmqnrMcHU!1RW0jMuoUQ%t+B$XQx>aC({(pN{?$gx=8cVAKgDz%kCJ^{UF<a7O~!w< zS=iL0YhPEk_VB1u#ArB{Qg9@BDrWMfjmK)!stj%JR2hj-)uNMx^WjuoiBz|^4g@+I zqw1$s#oWV8({Y`>g@su@fODViJExK;cMg50xobaXzQL2bffiE~ox%c99--=+d@2W$ z(%&VezDgV9w2vjEc`X4rB2YW02??SapePH%6twV<C3MadC{dQnZ0^YvcG^f?Gbncs zda8Yu`_Hr90S@p^dE!tx4)9MYyn2W5pUTpQ83UI^vT)sW%2cWK71~@GA8Zo3W>D@N z!KkLMmwBVURPMYnl&;m@XuG?lPpNkZg&Qu>o@hO%f`<q#;P)Xyg$g}j?i~APuHEZO zg$fjUhh*L|cL*2iLow>kM_#{jbvMb`QDcEJnt()Y>vmGNg*lWvg$$?H5hyUqh)wPc zhC{ii1+Bh)!<-<S${BUaJwZM4RH57`W!Kj=6)JW^F~&nNlYvE^^$DQ)=I%2nCh~?y z=n@v8mtNsR=mAR7Ax7QKo!0T0L~fax?gTkQDE088zEE2z3O!8ko~F!Z6PyAEA;)>V zX6S;7rJ3&pbJ=|UgTHWk{SDVW!epVwK?G3EnLNN!<tNEgJLY&%-5#KQ2xKSE)gJrU z4^o;SC_^IFkhH*4lR3(!PC++}j|x<-iM$|!JbfUc>k1yIWDeQ4oSY>>DmOqt&D`eP zq^UnhD1A0UBuXcAc}@0|g&{oc>qpRv8OP9_aY3)rYBW2_RA;2*;o&M>=C7__Z1#Y8 zY^*-xwxg@h>33D%OwtX4TzE{rtm=(!mhLCy30Aicscz?z-Z}yor`PP-?*Z7j>_;6H zt=sC>drWOSblx{W61-*38ln#nsp~AC&(yb$SBLXZ7lM9k!}%`^Cq#VLhCWI0>Mkwr zJt5$+#Fu_5)DdjPJny7QRgY)VQ008e@cF0vB~u<CvD<r^w-n7b7dJ$0@mO8IV{X8C zk6g%GNJ2LFEwu%==ApV!14`1_`K>Mdlrar5)Bx1B0m~56`>j$&t_LcgVrn<S;B^2F zukI>^z->Jc_cuqF**VOp$_>y)+I_f@vSX>zXKL1xOPt6j`<1150s2pfaI3!Bv1vO& z{6~^?+cXdx3P+lel#ewlP(6FHeS2Kkf)kx31*ok>9v1*<<n>h=RVEg<>325fu84M> zC!VNb`gd!<Jn_^SLk}|F-tauP`6k#sx=yypB<|g}UWZq+EbT;UBzS!M)h1}B#>~Kp zUX8SBMug9a8-MQVjS_yhS>w$*fOCnJ*lrB<@<NOVpD)P@KoRaI^UMFl06!4`0RsXA z1Oo;I0S5yG0000100IL95fBm~F%uv`Q6gc16fiO~agif5KvJPl1tdaKa)PlH!SF+3 z(c$s`+5iXv0|5a)0vHSmwk%<DtQ3MsqViC9pI|HzqmNq^EJiZ2pxY;7Y%j<#*PN#9 zg_^9?Er|-q4nT@%R#sM4=XUrA9@!k=?b8E*)Z<a3LGw*FXee@<Um7smo2SG0L~g0! z@=xJJJ{2i!thhytYjZrGMEL&z4?9knwTmiy75Ocy0N{Cw8ZGe%Vst1TK!`!g{^{HE z76)inQQMZtwOa~8#<Y{sOI0(r&?e1LI+V({Z04tMw+a3S7rWpfY27pb08&ie65T)e zK@W)x95m(`usq-`F>12WKFE>JqS0xFnCZyv`8uJxXjWEM<tw6rrrRd5y;epY!gPA* zpYqBQI13{qow>pSGgz#;t0AByr+A(2Ldp>wxn0`};590sfv34QT^B{s1OY1|b8U&h z;5S2b)33M<xlN6rZOZm$YJ6^K<a5igGF`S-RtqS>+hb*wTG&{eV`gLvz#{9%`_mW+ zH$pfG)Rk~F8zCo>mnltSEvuG9TglTKXswMqb2ZB@s%CgGI2>+MU1TI(7OPw{6di<e zcjfAfK~mH1N4h&;(T$d16JFaK9V0^ai3Zv$xSi1^>N<zFs^hf+4<?vf4b`v!CrZYh zO1<{R2jy{GOYRievJTjsPBvCqRhCe=VHk?aQ%VBpZOJx(6cGxcmVZ6gV6K82U@((> zC4WwM-DAjA(@R%|+pMGk=7O=ojt{$E(MH_2?1lx+YYFSo86Ho>R~6)H;+XD=1~tUo z>=W&uO_LhKADBW&&f6ouCY{y7zb1&WFxfQe$v23G;1rQH?MHrR4Hl`pD<QWem=6?( zm<yXJ8<xr^UAUC}0&d*Jj5~~Oi^=4KmuYn7<#o4ClF@w-mwm!zvV=EJ8e^ENxn^<_ z6lFG9ie#%WO>%3mh;Fw|b%oRuW+Wd>CvBuW`rS1*2IAnHG%1=rJK{XB{Ayj4JMtSM zfYnJ1t!>=fEpD!}9?6m2%OMh3Kf+Xp!-H1J>6lIOT$tS9Gh{bc2w6M}rrBtXxtQe} zwdAOi{3F#d*<1=~1Y9b%X?}9fs5e!hTAh{Nvo0e^Tc<G=+2K3lJg{z4)PESn?1hNI zrq)$sJuUu)5Q4LNCJPkd_{w2&qzESbS|ZR>nli13!%J$L8yT=o+ut;(<iX@Mw@?hh z$GVZZTtcLl$0<__#_w^mbNVJpZPyj4&$?rP#mu^s<2<l+PY8TN3;2NUjAtqOCbq)L z$Y^y5(q4-GR>nDD5r9UiuJI$6J5AKi2%D7D&x;ogQ+C6my}<WYMX8L;_KxaGFxC^B zWej=V0+yeK%-PC^i1*t$Q$X$+JVI=uF3i^EBI>3h(%%tv)2V|a%;)Nv8{ZCYA?m5% z66V&{Q`7M6t-@tEbUE{P{uFUKM_4amZ|^F2xNf#<;RGB&Np+P)FpkH2s(N<Io28WW zqA(8XDptNC>#GF}Pmt)nh<@^>iShN(GKG>fIGy~H{{XXq9vdGkDtbue2c^7`{?3la zU*S{H$14fjUdmd23DaVYFyLpuMU}mjG@<c_W=^2&<m|%WsA)sln42l0F*sQ6qofir zJ2vas^tTYFM`;Uu?7fA*y%0eo4`Cm@24`?@ntcNr#~bWBCP$gUy~_Upr?Xo-Ts?um zzxY#9WEb2nzV(U0p{g&PAJt#Sm&sDIA(Wn)DvfEr%88me-iilj;$OOrxoTQFITixu zp31S#;#;8ELHSYiDu-wKA!(v;>#~dyNa5t=L_Y}qQ(`l@?k?)5fX8nY?}fN@o_7BL zl_26$`P%;gWkb9D5VWn4r=cN0(%2pkOW{O63)MO9&Ru<Os<nXnC`IXj?h1*#7`#r| zQ0)Hzs+O8R8?)=FR6i-->R&#e&;IECOVbK}%8#K`PCQF=vaBD9Ji?ps>A)UARW~#t z-L+8s)_qlW%=J`^%`o!S7oTbard3wUOMdt{)^mK+aayh3TK&_Vng^dJDkfe+dnP>l zOUIY|Du!j4{nYW98DM&<f*S>|(#Y@ci7FHL(fw4e%TW@laBME~Q~p$XA!wlb1r=U* zhUK@amA0yk_f=0UF_Gq~7$l1@`h^GON6@MrpXi~JB}2Osr1?+!6-hH&c9ra`;${b} z)o~sTd#GEQMuj~<irQZ48N{IJYbsXdl2Ds2N~d<rZk_PE>4hZCZQ6Yiq>R5c{S{oz zBPc&r8UE}3(wz6ZcM_;R7yVR}Y$eXjdX+=+o&KdqCRgR?RY%Bv6yKL`s$_7%bD2hX z)mP{g)jT&^*HOGp8T%-OrfB~F+^e={s-kU}Pj&6W{{RQ_s~dQ)+X}Z>F!pkh!LhmC zN<Yc}0JX}Mp^)FcskPT|89$X;-5T#jR^lSi+f+$AoANi_0E;xohTa}**gg!l)n#Zl zCkP=Az1?NNrvCsueU}28{PFf)&rAMz`!8mv{{TEGTE?3P-3ZDttE|g1&E&IrEXy(t zTt75pSr%PZMUbqeYJ&+zH(hYiWtLqx!J_=vS6Xvl5sm%JQ?|zE2h7(7tlm4P{9CGb zMi_47;o>~GiVhY|8mkpztW}D!Rw^f3b)Be|+jMhXUCT4na@%m7CC9>o5@?Kogu}XV z7F}0#!)0)^S}hifMW?<SETvehatt1u@dWB>X78t}nUUvck6WQj3mbQo>PVd1u&Q{& z<t=w)X{qyC`u$YU@W||nj*)@4kf~y(;gZw0niTOq9L?U!nw_r%xp2eg%FUOv(=*~f za;1tPD+5mIrU>I`w{$mEH1V=Kj_N8{_<5dG(Nl2CntznqdRKM+Q=p}8=H9BFnXYDM zCq+w?knOzif}S@;bLOdHWSOth%BG#HXf5?oK^vehJL`os5y-+%bSbHwdpr3}hx^7t zjXf|R6Th0Mr{UOxqBQaQK5n^gsiSM&LF}IZ_Kb~23TAvq`BFm8D_X<4_IhSpG5%Ds z#3W#8-8!0`yXoqm96V+Qn}fcpsuo5YWY(8B4(r+Jm<D6~s)j_%Wjp(kVzgReWtA2p z6H!hnpBsLvW=7`tjkQf=ZK_~txK-ca^;AC#)L)#F)fkv%4!)sP$(i>3sy~>|s8zS& zkFFGQS^0aam+<1S>Zo^mslOe{Kbp^|Ro?@CsBRT+%KrdzhN3qxHQz;1O3>2g{gofi z^i{t*(Nk0~Gb5C=tS!4RrTn<{R3GE?QMgDXVdzLvw6>k#^v#7A%0KE+a5Fn5{Zn)M zZ;OBalj39E(oV{<O+z~)`A_yLFPMMQrhZDS%kU>3`E>ReC28GW3CI3jJ{}y*>`I%3 zVV`CAUeg{;v{l<P)iK##4`rGO#lQVkFmV2b4{+_)cLk89gUCYJUgbC@{6p%h<YSmD z;B`%LYk?gX7M@o4kKJ*p;$@qvrg-BL<G4~z1xRo=7S&a7oHGXt;Mw7omU@-6t&HU& z{{UO4ND{LB)nnM~mN(r&FAewU)jQc_)VO`@q~M98$*{H5j;46=IDORZfMv*f_f>L6 z==5+ppxQSyY6q>9^TZ;^(|s(d9M;RJwu-IIbh;bpq7<%;xvhV)bkN5hRvY0@Bw_#y zhgC-QH$Ba^O_wD>cT1G{qR8(U+X^`t*v;Gqsjj7rk{!Ae2UQ1b6-2{|$28u`pu;0B z94-_S$HU0v(@PcXNBpM9$5dv1#Wl2zc3|Q6PntK*+x#I0(g$V^A9XbCe5|&>sU1x5 z;&A(?a9cciPMuVSnfAMf-8fS=NbPHDeyBE#5a8kWRdY4=17YrR`K*J#JBqReb>s!b z;U92RR#TrAitg^Sxo3BEncdcBcUg~hoTi(F7&p~8XrXvx?pht>p6k5yM(X7Tx!MF+ z_GnPGg71clMW-&fw|{wNcUFtPF({PXk<g>a)}!tUj42O@`-t~ZtlbJY5rx&<cPXT0 z2wLjO%G{3SYU}RKRc3P(T9}*=#k-<3THgySs@$RE4|2`ZA7ygqY`va)`~^t_thQlm z@(qp}E+ARL&K7X91(<$|hSkDqy1Kf$)c}fa=<J#i!J}Lsz7b^2;!cjAhkX>8Z>A^R zj3abD&th!r=$TUuh2+-CwxTH-(9^L+5{JFjSvJ{)(H10o+~H>nI?l7MBH|!*!pXw` zY*BuR#`xUx9k~M>dLt9ZL`E&&l%t1FbO$lr2Hj<_{S!_O*;zRRMUWTd>&kb*j&Ehe z3hB=WaE|8&?}tu9`YRQc!=@K*TV?hA6HW%m;h7FjyszI7uAPf_<|uxHIa!Z&e{rw7 zEF3U_K&OMq2Pa-v?~8LQr<03!9H(?p{)>DX<m1RTM|?^M<0cV{m1e61@un@@EApS# zIJfruppF;Qk*=~P=MtLY5p~M4u}>Od76S+GfZbaYfkXN+<s7!eZG-+C7;S)8PB%ES z;Rx)8$l(+$EYvCEV?eO}mN`Zi#O#PDZHv0A6=I<tED1vqJcF`)nc3&ef20WBMe>_q zC8&_S;y}hV-IH9`G!e1H?7AqU7E{p}so|HS@Av&&Y!1zSh{Dj)Zd8l`;4TX@5wB$= zVXX%n{-!x2;f+PCq#(V-ZFLv+L=`f;=F6qdx)cBc!xOT#?4ol(H!7yPiAxM4W^4Mm zZ{qqRn#WPM{gc^qeF3@vw7lQXWI=JtPe6Z#D>_{U5!DEd?|;{wm93&TOl^ni<9=J| zd#*o{<L}{IVXlYnxUIeN^zNK);#O5<Wo2b$Wo$qH!~iJ~0RRF50s#X91pxs800000 z0RRypF%UsfAYpNVk)bfL!O`&X;Xwb|00;pB0RcZDAuEPldl`EQ#2Yo{J-oyR00w=< zWs;aB%X)yk-enCHFAPr{k|2KQpuVGZo(>$3iH3&b3}&Ehm?ssD!%^BX+lB`ymbPJ+ z4~GU!pyoL1fGx0uqM3DxpM>xOD?O)^J`>?D+F!KKiJ6}Sx5V)+G-q?;E-{;_4RK=H zTHLsuGj1Lo2Od)5$57gZ(-#Rt#PLivU72DFS7;$mtZ`|M$yH1Fo2=V9E*SzV)q4=L zSw9i(e98<H@S-hPxE?OtJ92E|3BkFf7Xpr%b*Lpb01;NBa1RqOLZm(Nk1Lp$?=Ez3 z*)b{RE+MGUo1m(SC9?2B8A_hfQMi1$Soq<61m0#O$Us3nGtL7Ag$=%a<w{z*2)v#t z4>Fp)%Zx?*M$eNf4ldKjCIcx-(=+QR;}~FK)xyBXqjMx-f)`%Q&R10uVxkzSfsPNd z9>_hR0X$BVcc2wF+FqtsDqOw<F>#yA&JVs9wF9NEnUoMB-<z44UzMG%YNl>e!U0um z>;)N+i%N=ySbC2O6R4;q?pYo#d(UMrsIjIcF~KXU)i5eeyOq+riS|o#h*YKaMVza_ zH$0|;NXk7lgJki>j~j9LQ})m+?JOe9z{cglloo6i7tmwGGfxSAh)YutVslcYgi1;{ z-YQDW;K2qKIZCqlk!uI?%BXJPfFD>auGlYgqDvZnW(?=d?KgLR(7=mN*#R~T#$%;S z3ZodgdE=za+A~592A(SM3d=9%QpZtOQrI2|TvNpG!OXn4q4Zu);hHk}n3XD1DTq5D zz4_70Y+%j-h8&-s4b>8k@j-|KNst1V93ylqo5r&k(9dy|xLOBLIb<1SVBF0l5(r|o z8P;Xm*-8||t_~qaF<A8&9G%BWs9~2-$0j&+#Y!x>xtaSV3&RRBhW1P1$z~$rTw2+v z_+l*1^9F4d&7e)4K$t<`>(FX_q9TyssFl)o#2Hofmryx31ve2~q{}wfP0CH_nP?GK zPhtRYlU`wWo^IGrm=KA<MLr?oCT3=NrZBM~j}%%mK3MKxo$d{T>g-A$rs#s@jCI0~ zh9}dTmo5S?QPuvF80BIBR=W6rkwGp3@Ni(2Ekq-0YsE{C8%Il5GqqF*c`+7gmn!b( zXvi;77by_oi9`uSaH>x%ubT+b6kz&hAeMg^^&5hBnW;->kqOpd+VcSu`&?f&KcqCU zU5=hQn?MpmJ9iZ(87xU^L3v6HGL-_<l$)vciWd$sQPGER>Mf{)nR3lacjhXL?)ZC8 z0yOGvB3hNq+!#5OJQj>OhXM_bp&{IGx|u$q>3AzeN=F-r@s?^j;FKAa3Q}07;2xFt zCs~)n2~p!-W23_mpx=f(e<TGH5Zanywd^ImO13@WeB^a-(-TPjr?D_&fWaJ98QNw$ zVJ=@U3y9?|TH_p(Bz>F+30pV`O3YBq0*^A@aTrV)gKWwhxGRX+22vQpGftVG$LUy= zgvdR(C2kcA;V)@dEWON~GJ*4R#JfzeJ=YKxES88~ppu!g_&J!(4m*}l%p(pji{V*_ zAgnbPZ9LTahLAN4I|LK8hq4)6=$Cg`2epD~tPh<>vuw=4M6LP1>Q~CIXzdm?L!CGe z^9e)9rC|brlUH{X?w>}#+`2As8&SAZ-7HAr@-8v0z%8or7BC_Mb}{s9*{Z3BHDI-! zz!?!1`G$+3?VBGG-`;sCkM2Sshz93ZD411Shs?>vIcBA9;5P1erG=f;8VgGA4w76_ z7VT>1{fKe~>^5A{bi&{Gj4%sPL1+}Umok(n+g>_g)RoQ~E@!;|0Pl!6xL>v|JFxtw zj1c%^sLBCl`y$s*@~FTphf8W`;eN@C<&rITD=z2#kM%Rd9jv6#<m4gB__=)Au^f2d zF%EahXYD%xDEGjMv;g=nSO-t6;D!vLH0wS7VGCc8Fd8ept&hxd_Mv#L3_lC_K>q+7 z_XsYws`g?1P^0BwWHzah^5KpUDB{=l94rF3UcdCH0zcJR`Cr0*-~<p??L+GEVQRP6 zFm#Oqd4wko-xR;Z7mFDBQytb1VEeM6;gC+<^n&Fxcz)=VZ#sSZK9P-%-9bOZ@I=&v z4%GD<Kkx+%v;eKQrvdqjs~^E({{T-=4nOkB!wUz;e~8KqK)loc08)v$A4bWtVC?-O z>-n9qkAwUsAkB}EFYhV`IezEwE@_@GHLPDwSIPeXh}nkJJ?1z<G#;jh3`z)of~Rl; zt{C)LK7_b~b$@2REDoRa!%?e(mtXi0JF9qh*Onst4SnC<YDh|C-k~{O%LbSofAAoF zm++YT``k<IydTmIR;g{+NVV$)fK@|)MO4GNNa<gbf_1K$%J1z0F%<>``^<Hdf1F&U z54t9wS!K6?du_sux)$rE3Bb{x;pSTD5F9P{D$S6kVKF;E-2^VL`a{KH{M{IV(bxA% zqwRhB{iWjFKF~l}c7I_N!keJg`Bqget&_KZ#KgR-yYfNO`~t5(`8A}&>O%hjx4469 z_xyN?U=_X38-l_Veo3-ZY<<CmE7{@y00b_t_{B@Vym$GQsJOv(*5YU%S}?>m6++9$ zne7mTABZvD($hgUgndT%?q4-32sQb+s8~8>t%;v9mtFhMb@!j{&vEzQlv<1w+f(Jo z=PTxXxcue|M)JKtWK8;xy&4ZK7!KANcAH3gtlYI-L}IRaO&gX7STiZ+QRY{mH#1VE ze9XYM3Vfh@&G=g7{7Uqah|c{3Ck#tVDRMg)myXo3?aE3^C6y|A69lK3WV6hqq@=q= z#e@T-1iF>&sqe)}hUQGI#@j?4lO(BQQt-?>hQg+Y7?_tXT(iu-M$e=}NRek#-jwq! zmlrG;YTA#X76}EVk%?@)>Ji;zpvLmdq?h1*<-Y<Nu@dFgMW;vBrLDLCYdT06(9USc zJ&2tB%cy0fscVy)g}p<GhSa_2i-zlvR$WFAZm~YB8k>Z!%#2Ke!iw%9PKGRFNlb|j zK1*Z`)Y@EJ0C-5#Y+{KlgBh?&DXj=weF9iy66!wWC2zj=*$%XKi`7QY4S`o%CFKF~ z^59SqS8UpU6Tun4KM4mM1{~9p3H{+<`HfH=_#{L6olwSb5msI)X7LORUT7lu!zID@ zly_<XDX*F4lo;Q5GYhb+FC!KS1*MYAjIY!|R#uc&=J7)ML|>ve%E}w8GE3c&n?8mR z*(*3nM@Q>Dre<3XBo86E{*Q@uwyAtC+G4&+w$6;Iy^p44*R}ru1Blh%;9&?Ib#L4y znkz$~(eoI6gg((o?LQ-cBK_n3rIezZ?Jz$QrPA->d97#WKf>`p*Y7_eH~#>vkdZ?@ z*D#(O09Je?{{RQf{{Umb4J}A$osdSR!Cy2d`F)9}%2>1n_NiyqcNTs#u0T>yR+QJb zbu$iu<#oG+D?*Me>R;uhM?mK0`M(GIqc+wn;Vep9N;`ny!-q8NNUSY3KI$4AK{b)d zD>LH*kE|5m&I1+gKh^h(r3-HtE{N~<l&6hq8gH|X-jTp-ea6jCdNK4NEFr@LEol2e zK{OFrvTPL()(92G6nl^e90}lf30+fzI0TKy4Jw+)-fc>g59ne6EJ|fUWvgm>5zIa2 zPQCLT@&Fyr5n`2fa`-gXzJ1^frL2EMo1QIKUcx+RQ@`&hggbXc6;)!hWccLyUNS&S z1J4h^)XH+GjxQ>bx+DFlI6tx+eFcpUgN~6TF@T5mqL)`ustl8_^dSg?uR{0_5SA}C zn4<b9ag3}5ZKd*1;%m?Y!L#CF$Ve;`q*Uq>6troC_lm({)hl+y0odbzSZI+*(QRA@ z5vBg}s%4eUP|@?5RA@Wn>kF(z;HSbks;6eYVAHxHyUjzj8$J)cOkUCDLx$dwz`Wyb z^8IB%*()5<;__*XOOS@30Ixt$zeGR8p*biu3Qa=MP_j#&+ItD?C$#%dW%i!h9s*O* zA5~GiOmNf?vhfcAJ%T7I6iv|+v%V%mSEMMF6%<N5L^iNQ_h5Pry(5BxMinYoIiapz zc8u^$Qm0R>rW!eVf{Bu#qw)FxjS<4g*|s#cCQ^ffCGjavI)g``T)r+}C!=2oL(pv> zK;r{bz!_XrB}}r8GahYFfb>I58BNb+_K5c$D-f!dkI~&Pfi{>X?<5XV(fcXc6EI(= z8~Qa&%*&T9Tq~As=^O$F@jcR7US1Li9)`J8PWVkg=`ixuI{Nw}pV8JkgF&QI5$8cX zIKt3diwSbse!H6ZitjQn&mHPl#M(ZQmBx6ld5MXMiG1IpguJq#;?>44jX3QtES`sH zgD^MFvf<od6GYRB%2Xn~RVa$_QD*hUGb3Jtyo^W*QoUVD#2yxJD>8r}U#uINN{ukZ zcNOyss5sdP=g=!0EW%t~4i*t;j$V5>1mLz@&u($aXX$k>9NTeJ%3yhnP0ur^K@IUw zJE>O?3S6!szBS{nHsIDenR8L+xp8lrmCBwc(~I`?2_z}&t}Q)r*k_cun+b6)C^f;q zO>-?RWJO<a<4!t;Wz;tuScJs+aQepOzo$~AJS~?VLof6Rf)4>M?>f%{<Yuhx5piAT zml*JBs*VeS!l4dEC6zGh4koj&Lq4+kF?}`axpL#fFm{Z~moJP=OmibThPdg9m@C6j z>$Ume+I0zAP%l!_;^1&VnoYRPO_6st<Wg%DFGIM@z~PUk<$jp)MgEB#0nc0{bq@G3 z4Wqs^!c?fyn?hKgMtnM&Ohd^mGVn)`&o4FlEIDryRDVLrc>NX|oe`FDY=s*qU_iGE zk8qyhI&Y)6a90@gQetD#f24W=#DDm}9y<$yrLAxmJ)Q{{#0;x_<wf#7>@v1&{hI#( z>&3|KZ|iu3{DXW1{?TI+)kxLTdzdC&L@9Z9oy(@4Mbih$AwaLR?#*45=4RrSl`x)v zMdv_o@f)}d%ZF8&UJvOFE}wHN#~w1i5mD1Z0af0AKy8SKUR{&&V1Puet36E_sHCgS z1)sAlM(AZ+sX6A`sQO<&tADM5A7&25^Z1IZ8+RJ`aXXDu=NOWn=d#njr?72{nVoOW zr8o@jmKGlu7Ngs_goGTxzA(kqz>W}l_t%L|dBP^00Am}N!4SX++Ou#;AOtd-T9%Ph zmJ;&;RZyaPigf6sI68^m7~G?>S>|iQI+XJbApLzRZNE~l=<HQ$r&UK$Q*$&}s8xpH zhoiI(()`92?Z5s}M#*k{{VCK|VEvgQtdd*0Lg(I~>0w=%8ceSPabR@WbZEb5zLPI; zQ!JU8cTUdg2j*VAtIVzBagU$wSgkv2`hDS)im?9xq`ng0;-_83u|utA+BOEW*SGqM zK1bbujz9mz04Wgw0{{X70RsdA0|5a5000000TCfF5J6EOVR3<xq3|%VK*7=B@&DQY z2mu2D0Y4#T@1Vcfvodrmb_r_@-bQNk4#g1AQ|z4NN4QDXG+%MHGF#&YY8^H!@+w*w zws|_?Z-b!Z;6Ri0F)%fQ2-&?)VUG}MA5Jc0>&E#J!y!0a9SVo?M({pivbbQ!8%D2% z!%h9qFi}j?qa;p|+0o}LVsj5BUcQL^2!+H+yd)CQr{M6+{F}iPOeK_22SlqWXk#{o zcrBhzxLe^&Wi&L=J<i6QHa1Enr;?`f$Hg%VDT_q$IGoNAqm*_eMbSjGl1R9O5wVfI z`wI%rk(PGYLl>X4F{qx~_Z==fw00y9Fpcdxy^n=z3B}lW8%ACXp$T5XQE*3-t`^35 zi|+fCcp};ox$YpsPFV(oJ#fjy)!H>G3j(5s(4g3DaE%2j9>u2yTo5h>h(Za1I1qm! zKOuMru^z->@DayXhN%f)T3STSEyF7Y0-+`zkIN>-gmz2|Ts6X0U?MIQnd9I6i081C zJ&B7HI7qrT(4P!KyiBLq*d`~)&}$JRoLp_jr$&wzEC?mT7Z8AqP$kMj*oP{(MaslB zOiisZ*AZ3*?vG@P&@6SqQ;8lrBs()Rp-fet#M^iE6B<Jr*<)qpO;SS@=8Uy1z|*IG z1}zX<7_=T>_b8ZD7YjjbS`6=K#4bqMBooN(;IK+t8)!ECD-sG2>Ix8?Spqzv%cJ9p zid0lm`fMZ;80?BV^`g3Vv`#!m!OcU3WZ9-=(-L9dMAIcpvKW?lXgY7gG9py1SCKo% z&JePtH2dsNvU0~lHs?+e2=G&(pHOS)=q@#S8`xOYCQ%w~G9B?YC6q$}1FaoR1i+BN zLa~>G79XLjeRf|(yvw9rjWc%eR1FPIp(vbWfV>`rMRvf&9#4Rve{gUbMq!U-LTsC1 zq2?9%I}f;I+HjUcuulo@5#&u5hTpMUTm`-hglN3vN+gX51|((U)pqh!?9exDK_Uv2 zXy1tX&r^_{i$z8QwaQVO8F1fZ77A8&$F(hrB{*o6V~p#-(e5jOMg;WREVEJI&$Oa- zwlO5AY(zrTGz*BkDOkMxa^Si%m@-$gJVnBjHrS`PL;iRgK5d^{T_ZI`y$slEhqF=O zv>%b_Pq7(?j-PAMb(sYm(IveVK^vJOAyW$?rcmzOgOOy(iHY9{Q$kZ&nr*LPor|&w z+0`EWrHSq$O1v5dj>c}q@i3G;@d!gaFbkGD&>FX)FGDdreRNqB38y6@L&%b4iUMW^ zCTWM~`cp5ltMqOE00N@Msv6DOF%E%UY(`j3#Hhf1@sNm0#=^XC%VE*>#iOW7n#h&n z`N)nA$;0#{C4uqSoH2=bVnah|3w*X3W<tZzioAMO-w9j|MhmPmO#+tAsDeQ<(MVvQ ze^%sS^YGb38on|o){2<O=`ZLBewcF7g_i{TQ0zi+(&2gd7Y;VK+Bj;5@F?u1;&O|D zGh+nNl#r3LX=ELCZnEkzriWTpD|B1D(T$*^bj+$RWmHMhUHB;3)*k{&)a%IWsym&H zG)*RuedxZBp_+UNUc$PNMR-KwB+e_Lg*_d46o(DRjTnQM6T$2d4HDb%VeT8NO{QkK z@((VB+6J4HK{j+jp^*VgS{smme`yTX3h1rG@W)EF)>3Gyq}I`9Y2yN!ttRw1&YgAc zFA(tsB34V>UPst*Hk^DoIM?9BTZ1HYe?WxEU_(V{cqUJ99XnmvjSX;IuTtV{d;{?V zR?Xf20H%Ag{h4Dry|m=K#{*)Rx$wq&2tU4a>N|WR>b;+_^3U4&8HYyO<!O!r%1iu+ zOzoNBk*Zj|?!0kKQ~sfga|HUf;+u6a`{A1kWqcuDmm9INh?{=z$ln;g=W1lz9>3U2 zcS5b~{HOGX7XJYFL-qWO{m+(HyqgA@Wk1|2W5NBB#nb3#<Im3&9TpLKKD#}vw}?L` zzZ9vMz&*x?(xcd4<&8$<CHnax`ffg?#8^5ty*7#O$c4TZy-SXUw#s}0@1~l6QUSMz z;1)`+BKKl970ySx{{YGxW}8nUzE+pe@P5b3KWpU59$J5WlD}I8lM{76aj_j;3qxrV zRPa1GbMTJCS93?w8E(&K+Mfk0*&lH~JAYg(ZdX{z2;J~99i2UiT5+%TpOlAB!Wktu z(ei$WvNNau08$h@f3b?1uKxhoq-56H{)m&<m$J}>Z7aA|l(m(kA8jIkbMwVlzu^rG zw7UsX_xv#s{+2F#)&Brr*|+rBLh^5cPN6oH>G&#_PQImc+H#owvAnlPVrarfpT*G& z5Zjig@M;M*;bOg)fms^FL=iLz_^0nb>H5$5ev|%|*V_L8(;HtSJHZ99`%i)KFM;qr z1sAapd&kIB*(1sE4F|ys6~UxPSxPB3Bxw8(BGKe{Zw2-%;_@#crF|_5JfmJJC(o(i zye>Tk32;|S7F@{i^b$4>_!P=bu}TiBf^w;sY<VclhqQYKuzLrvdjb<ES-g(~S~g=n zfr{?Ta*uH{qB`W%1T2}_5je4VB1<BNO@$O*OP;9PeG8U#>Uw47y`qWAWLO&lCYRen zO7ZUk%s!>boIg2;`VHo$CI^Q5qh%-SVp%%cAn+Mlu`iQlz7>*5e>ZEQ%}W<WZ>o91 z?13z=*d4K_S2qogHFd_sz=mCgu@v4{aU{euHKeGJmARyDDyEsC@a$UGk=<!=U)aR; zJMQpfx6z*^(wYk?!tLXQ67D<pAe~m_K?!rOY;{L~PeHm2=6Vsr@NL<AV!z2F5_5kP zjIuv4W_RUa(oB!H16(xf=Hamwc=v-tiwf+7-dnj8eY76ozT6Kp;_j|G^moThqjYY! z50MBOa5Wo`3Lk(Jl-g!w#b9rDF^=)*JBL*fi^uv6Ye8Uc8%medROzqx5A5E2R()&o zFGlSz_A2^FcCS)>kI{KQU)cP^U%-`V&9u!4t^WYQ{nJOpSi>xRXUM^c%Q}ApKSSL8 zUsL@wg~7cj2v55Hkf~Do-|R$8GJ4Q?i<(zu;C-9&e_#4Pd)*Jlu`lSpWBVY6VYUAN z3904y(hrr?XWUowSl<4GPbK$XbMwPh_M%$b@bHByJEMcwt|(a7g+-r2SvdjH_`>bS zi}p7J)M?Tevq|+GC3_RK`>`d}OusFN#R886=Ie&8(3?!95`CwSvnV0t`3Dn3vSt;= zQ|M{curcg?LFg4J^%9e5Ixu_7VrAs%jaI}|HYCxcij^g0i493PMR<y{kp>z#le_mA zv<K3d7hQxRb*=838%oOE`*53aZ!g&hnRd+`Ud5jfO)d_>W))U*JP#vMif2s;8#}|H zi3>I!dccH%t!{Z`EUnRb16fC?+dok$Hj}*se5})Jdot}b9S+yG=oUiLsI~bgksW$F z<*2V-U!yTnmr;+<+&jrgPNLwC$(~&)49%vg8Nzry=iK)XaC?ur?mpta-*et$b{X)M zq9d6$Es{yNhEC6LcAb`ITK+}Q=$Q#Dp9A2040ti%bHLlB6_xOJ!#g_lx}Vzp4~Q0K zL2?k)^g&+5&kwE}*C>3Qg+71aZ3Rtebm18^nGnc|O-C5iZOz`vi_wvtzea>1q#`Fh zzC8qW>q}?5Iio1lZpYY(=tPXrbFlVh**;I7HzU~Chx`u|g_vcN(4RRzN~yiW*vAZs zXvJL9*k`uSo6tjbm>~!u@1Do&ZEW^^0kIo_FmD%tnc{)#kFyg+<X!YkKSgQ7Sbm4; z--}72un})i{1yP|qf5ZqBAhZ4_#pWUCYL4SY0iGX!5<#j{6u)=`0&{iO$iYq@QaC& zFg=S#DMok0*9no|I(B?Di))=6JqeBE(VGip6mQo`33@(AsG|Fg(>}v`Hoh)>3VIXA z@n_*ET#P}CBw%N59=K)<QCFHMq&}krl76Qe`7Mlm7R97K7oj1G)YzTS)UR$8u)dQg z@vImIghCl0-L#;><Gu{*jB>4%A$uPhxajP9`j32e8|I6ih$ZN}C9F@fFv=S~{cJ*) z_)%ra7K#*e5XMPFvP_TDPEAQM2nXoj^z9vRD1+Y_@Kxi8h`*AQ!Gi~U<M^x33`}&x z^duVvvFyRI8(u_fu2Y1ByBCyRY4G^Z#LhA_>dy@ui)7_-2g3eo%6Z8aBkEvjFW`;~ z&>@SY{!y5SyhSLasLaLqJ)!+LZH@5qY8j|llSI&&<eBxi{uATB;bJsiycYOXk?ext zWLm=%)NM0E2F2mwY;A;zrfhaxUYJ_X3|zPT>(IsgQ`e^$J1z!Lf*2|yiXIq_uqA{o z8V_Nf!#ilJ&jKOO$=WoE1i3sWc+aJiKjASK{3u&IrxVjAond(%9Y!BJ2H$zWsO`C$ z@Qwcf&+^qZB%3urbL!F))c4y=>I|gHd+c4VX8Wq(Z7s1j`x0ua{!1-NMDIcf$)ViL zrvR3Sl8JM$q>hNdojX1@`ECCI-}3sgKF>t+3Y$~dH!s>ANCNV^W6wk=Nm7@vt(G48 z73jz<+`gK>#a@c2W&DLy^^w{8{{SsHa5d2D4cgZFY7Hlq`N!06j7=qsuwl}|e-8#2 zmDqfbne#qlwr{a}yb;I$!~iM~0RRF50s;X90|5a60RaI30RRypF+ovbae<McvG5SV z(edFRKrm4M+5iXv0RRC%5UQyUwMD@FuW)RS@EtcN{!`>A0Xe-ujJ(Xjve9Uu@I*|d zXIY}<T&n=#xEkXn<g`djc_?%7P@=LDG;B77JW3IizS7%@(|T2xjkNU=oj!nnT*ZbN z^bc~Su;BO_PIz#;GwyFRG%opW0TONKbPL-7Hw-40!?>Imd@m5;F7Wv2uG^2Ewne|B zZLA~;R#jNx@{Vk^xCNjvc|MR0fgY5c5#iD}n+0U{tOAI6VYU5Bxld(|eq}W)10bK| zLp)uFCCJ>Th_umzsph70OeoU4#uqz=#PNP5xD!^&KlWOMEMstn0tZdTiGq#G1Xad5 zI+Zq<Jxu@#!>vOkp=A~Q5yZJh9ou?>WwPzkj@T!1JFJ>Nlx>LN7a%A+L<^~kdW5*| zQ)jkVY#Nspz&Gw7YoLaS$Y1IVMpQ5$0^uD>k3lKTGjTJA9n7(j<dtbg4yA3CJ#J+N zgSm9@106?0;s98gBYVwFm|A93r2}D#MRhAaCRi@XsGN?)i5M%NBAvxwB;Qiz#ux5! zN*ie-g5pCkC=0-G2ns5Yfhm9mp)r>hmcz`ZLxacGY?Mi%IgX49w5WK8t{gR2#0wU2 zIb}9n#FFZlmvKP?yQ{dPq6UuL#$sMr3}{6TXC!#C$9bHFmJw5PJn)tPZ5F2uc)$e{ zP;dl<K|CWt0*3M#hYwQ|%RK_a5Q<a6zy(Qx!+pRg)wAJh6Zm0v!P&%98??G0WYcov zHY<=&d`hhroWpoq53E^yVl3S+^odzpb&brJkyV3<L8Aq%DjF)BA6P!}1}PgqU!)D| zhvF6!ZUSS77eKW;URTFZm<U<rVTdqHL!264tfrU2pph>Pu;qDzGVZz_B7&iVO=2(= zH<fn8*E5Pa@e0s}3)hJFEJD4Ll$=hRxJVQR^%&EZ$U)DF%(_h%?F_Y4I#-&9V|W2- z?ygcPVsCz<APH)2I}QO}RCOF>kTe7MMkrzH2y#Su&?ep{R3vF(7=q*lMGDpqSIi$w z1<~#pK}=DPh#&!Lzl(?x1VxJC5f)UYxDKKeEZSQ|%wNSyA0ZP&2O!s19xvbp5GGjH zD8$xi;NKHt63hZiRE@Z5T(F!%>;+h|YZGaO9S}^T>2lk@FcRn+*k)W9o=-7-N%qk) zc%OIvBHECC$!m`DD{Afm4+IfTh|w!fHUf&sV!CEk^&TMkmI)LWKo>$7oJ?-8P`Ku# z!?-nZ33#M>o~!{zxf;1pzvz|a>q%Z?^=U!DnV^6?%1XlHEliP?tD$>~cT>b&5UU*g z!!4FHjY?uo{eX(R?o$`qY>+BEisBT}2QK3pC7@*Y7WSJZz^@9o<{4A2qYA<5EwJOn zQ+2g40c+i6TD3jYDc9*A=-#C@yMWVk93&?)?1*s`t1Ys$e&k|g<Q2>Ta2hp?N9u<U zQsa3qmZDj3V~xTwyad_?VZj`SN%}&>OK47?=!48@F9aoSH5iX?2F{X?d+{!4T4n{q zRt_b`s&0zc?Ql{Tl{`y|xHpT$Vh%Y!6SM+ewv`rE<%9sivxUPp;SK`o-!l5v0OjEq z22WM+OR{qfudfh;M;x3j+)G8eN|wcQh_u8_B~+(BF_t$a8aGE0yppwM*O{awFNbi@ zoo!e+fT{9QXH8<bM+dopWHgbDDb>ddc#t^=G<t)@riNTAvhBCBUQpL}(E6J@i_a`Y zp@5|rMBvp90ODuBX0=B*4-c5j^@waWh?GalO`Z0tulp#4RWVuHf}WfW(8`T{wKNiq zcu*|p5RDd%`;3~U7>!2pL0gt+h|Gj<XxQpB6f{Q2ui8$8#6gYLYs9w*mCmn;TBswJ zIEtXGYG}m!$dvkv=}NxbUrHt;kEyv8v1RT4A^K1|`F<rOn`h=3#ag%s#X2DZ;=7j= zfykgj{GvjPgg|gT%E%PeVmxbkL0XL#%K%8))H)`L;i%N9r5#OZrdeWU7VZVoZOZ=u z<a_19THcQlQj<Q2?FrHqkzWmQ9?($+#y4CL8LeCjI?W(lB1NF#JjDD57Xj?-H3FAS z)EB4Xw<}IFnF&WA(U4Sk*GE?u$q)cz?rzc$YFsW48H;lY6Aqy31S&2Q%=IK7rXVP} zsE~jz3D5i`(dto~{%Gl6FU-ETMq;6L`QcFXh7!s?(&5<q%9f1-c6uT|D2ay6;SXdr z`k0AGGjFzI0luN3A)ZnOHq9|K+Gb%F7B~lNYU@Jr9*k&WFe=CTOcvmgk9O1&$oPlh z&SmK6nm^!Wv#)WO!GK+28#PihahkxKE>;>_4G+~)(~GoFS2)$xCNWD;tzsI6IH=-W zF7cUU!8?lp`Hov&Wq<+F0m$<ZPMv|Idl_sanN0?w)Ct1#6>MM$hESc3Ky0rwG%L0# zU%-tEWAHul!3IU5j}j1&I5LNUn8T2F8wmur0Xv0IQ<CtVLqQu+q44=9NE~)`I}8ni z32LwmS)9x!n^@q8h2}YCHD`ozY-1D_238PW))2rI+Xr>jxJ*Ut1{QmP?B)?J84M}q zfa7LXZX1g`WbO=}BEMAo@9~>Yh;s4#Xt|^AI(;Rd6gChSn7(pur2|n-1AYUT_=^-R zZ!839s_GY^6$SqQI{IiWZTKRW6=mvOf1+qyGP!6B#tMXwGFGB=R#*Vy*D!PXqLvuc zv5RX9)f&Ge5imx)CpmkILgU2mJi|Lcxe9nLP~T;{m`ol_iK=7l=~`cl^8!(M9T3(9 zMrr*=U=rwl60uQb<%Um?#i^JR+W0~yAx=09%8(rY01#-^7V#@0%`&Z7`mP}+w7}Le zD{xAl!9%$I1;xyN-8?_c{{T^l7xiMP3{<Z9f{+7mzbb#aEdJQnRmpJL`qBRYMJ=w- zd!sP@kr(F;-kDU}d4(}5rtPCU;^0s^b2PfDvlvEzkBO!9p=tsHoRWhLY^$|h76nL+ z1{&sTO0r7M$t}XGUjWqQ4BF^1sKR3~s0anWnL#@-WGcz=E^)#m<17KO1{kY`y=?1| zZyw^*a?8GnY6t;J!;er<6|`Z-p#rpT2))!Hdu9c|A9HVZO{r}Q;5RGI1M;<)0l7NR z&RhA90TL5pG~5Ulxt?N_Kwo4C8*-CX0gp?nffe)e^#=}tk@z57Pw{0iSO()E^SBZW zwcj|H6kaMdmprg=wu7%TwM!|<nu66a8F86-vp7Rnx~PLpW}FQ+#YlBVDcnT2njC4K zPU*K$7--s<gm-LcCpWR3EC3)y$c2f*Iga2VcQ_jZ%r#VsQRJ}-s9L++ByO0V=g^^_ zQ$FuELi{jrJVuYqykwTmcMAF?J_zgBl=bv{s<*)l2t^@7C^)pTBBMPCNe!45z)I?i zh*}U7{{W-Z14wNEp3<3o+akAXYvC**V-+nmQ8P*pBE>Bs4WD7(n7K|0(hnpmg;C<V z_>561-cw6S>KNc5;=yM%C?aaL+^eYZSHE%asM<9E)(0$Zln&*&zbTkZjlNNFp`7OC z$W6I}fI1{tD<DRRWD3j+=Wt6Gdg2l>eD;oyj>@QgGR<uv{4mP>!9^C{BdSE*#2sHO z6&Ljw@jp`q{*21i;?QiL+QA$8nY%w!rO(z5+4{}Ryf9aWJxrq(W(!xfN5_WgZ_BA* zls5aS7=(PyV;sgGXZ2#oQYh(hs?+p_xp$~~);Q_{CPCr?)*{q1${ZjGXF9h{LV&l0 z?ggBTrh~6hfN`R$l7J+(?%0RP0MPA$IhjyE4dM2e`pj}$IgvG{B^|dqNRhbV1;j>R zxXbB+h8w;g%-HJiXxnP{Ebtmfh&e##Y!d}&<p@_<&<D#eB}Gc^E_AS(ggBf-%9YG& zq8jzwVQ`$`vJ)^y`Z^$1rd)eG%eiT|J4UL2aI19<-yG>puX2rK0<X8kCw!x|)S}Vc zU<Iih9@uN`8P9P|ml+MfvUsSZx&{`a0yc`w#VdLsi**@u1&XLxvYT6SH4(b3d+<a6 zLdd#$U_mTi-^9S!Fk-HyI~jR90}X8eW@a2g=#&gDrFR(3=3<E}aU6r^_^w}H!Qtv) z%ea!^h^|pCNJ=F#OAGf57)Y&PzjZOmq61`WMmII=S7Q8(9Y>5a@iMq<-^9v{F2Pd{ zR}G{&6HHH8;}xs7!<f#jr4Y;nUn>6quxJX`hasb8Vt5#0;_>1Ry_;diY8oFnbHp#( zMOii;k^<7v7rM9%lyqi1$TV#MoHs0B0=wt?l#<j?!+`Ki(6fqQgi}YnGcS2<7Cr8Z zd`-JEf$;-?6oycTP~_B4MuGgHJK1Vto}x(ty4f5Zq!HMht>HL`C=Pmrfv1SDs%5w! zV1eTDf|$USD}iQOh~+A_hD=<X#{$Jy!7JBSm@<X)28&*0P6cj_rHtIx<y2j^*+zCn zABa5s5}cSPgOsz-maW7kke1mj<~vcNlX0fIW5enik%3Y+xPnaYYm4NK-~twm<RNX_ zu2IP~zV)EBTK@o2g?a$1iX#knTsII|<~rqq@RlnoA`jwWT%c|pZH5Vet^<xbfVn&T z!iw0|1h09~)N@TT@{lFHtE8bf;FqReweE2w>MA`ldnW#hNbyc%lDdQ&dW7BdSl<|p z6wYb>;D8Ij!7m_2)wogMX>yKkwJw<O6jUs;O|Hdwhh+lfQtk4?qncSdO&d|y0!81B zAVNm-3h8wi7S-T{R=}F!U;tLpZJb4+MW8y14%plAF04}gz6icSM=(PHYkfV-CM^|U zb5YB*1@4#)NEgl4;I)ZuW?5=D%~V~1!;vivGqyb@EjW$nEPq9ct_x=*1`DpPCe?NN z5V+I-03!DUcl<?+uIgYX1|!`%>NG<T*2??Ky_${{J9)WIuw}(e*kbR`#8IQSnuZxY zusUh@itsK_UzSoZ!rHLqS1ba|1729$C_1-*_$6DQbtU3FK)D8KzN>P}ihBk2KQXLs zpe@G&J!w@-wYkx-i(qV+(~z~w1y-)X?3DJFTXEokC2fJngjE4SoV%k42PwZXl4-XQ z&kMBRN+G%!8XXTVI)Q0rqc;hDG6k*2)oXD}DpcU(h_B+%ZC??Dn*}jY#7M$Rm$0pw zh#G`Al!q$_!NMDhmKZ*ve6S2WKM?LW!41F+mCzFQ<=VONESWn~HxJZ+g&;9;0wauU z1B|mTSgI<E(FUG>0Y1r?*hC1e8NZ2Z6aG^16Y)ko!Xa<OYbtZAbw4D%On*}|?;$^< zxYT$9382e<X|foBpm6i?)Kz7tY-$a&A{Q<(f+@-seO$1$JLU2aGZfM0bCRU(OIi)C zGBVx%(xM6>I-HlF?Te{uMcL1ZmZZrj23ncXQ(%*bAC}{F0^X{rh(kiA5@?{cq(GFk zE%O_g5u*I2XL$^ueT2KFpw%4r9NI^~3O29J9Kn@1%XzD;^%|sC?2pQAXhivX!T`1% zGWaMLQ#39^4W=Or%$Fs+%Ora?<Ca@gPNM$+xqc0Ypz1EPR9HurWX=YQaKbYl2Xfjl z5iaJ~-MOt*p@sy5jrE;QTmJxrqUojv;&az6KPyyv3^xYApr|kcf*L175lcYXvNKR) z=c^;#%HX9~nEH$ZXdI%N?GY-=V_||lF=UEL3)K4<py{)b$(HV%5gMUjLjW5*M(W^I zMzVI8oF>)Qt1&)k-(Mg=Ns1ph`gI%1MM}*}je$WnI7my0T!btg)}K&wA+VfkQmN7$ zlOF`z)uNY8(;s}2>`{TZZ&Io*BP^x7M*yj2uf!_u6tb=e!bNq0{$s@#t8x(-Y-r-) zz#Qof9+sh);AzZ70O8pIaU45diEUWg7xNhn!DT)eE`Xqh>!t>*k)v<S9au&V{1U>P z<(sYQU6%T4<H?{^+(_q-X?Vaxese8?(vVX`3rf6k5y#A>?nN&g#k<UKbScVox9xKQ z>`g4K;}HQiE`p%Fq#hTE+sP1t5D_$qVt8in18liO0HIo@t-Z3ajaI^j2)+IUa+W-# z9+qkY`&fv703}W@6i%PV4nA0cVmUARL;<Gi1*Dn7%%QIOy)UC|#{r2b!m0(#g<1?b z@<bY7-*)jXBbhKjzewxWE*H`w;un~d+|78En}XZoVJg^A%ygj5A4H~ccZddq_*+J( z#JA-JkXr^~lsCt4O{sXkAb`lR#B>9z2QvKtrD=khQoRrgxCeNdXoq<3iCLj_XK?Li zQOf7kd3-`6^3*`o%BzntB_C}nlD!9{?rIrT!gDdfY4?yIP~(T6b7TQ`UCTpv82<nW zgi~K3?1vmiFO%kGNt^}G5TG#8NlL5qKoJSQiSu8IS=0l>17SGQ96?1wb1t-51*{%P zTgA;Rvb|itYmkellhh|i9m?hB%wG^pzr8#_mA`D+cKszC$<Yo`K4mwJm}VdaV=)3K zJ0&?;k!PQX^`%m)dACs)U4qKy4dzE8yku>FK{`RAJ>af=!p@e^a$+&2t&?<HyiCo@ zpedy^JNGV^f-vS0hczEF6t$+3s`z3qZK`#9i+BYzxtyxJhf(a>*bP952XgQ9Q*1fY zO-}e*JHob}Y9$XNUq1{MeFhWTBjy(l0{DbYpW*@N9Plw2=;0KVu3S^LUaSXKGg71A zJWKfKE4B@DV$N5?D?-(QhsGckmo9t=Uf9gUAJs4cO4Ip?;hcpIA&C{8hUO0l2$3W} z5aDIM@~k80YbWx`k7BKnf4Bq+iEZJ`s%u|8-`=Ctx*1+ap|6O#g5iSjSE*n$L8uIT z7AB2S#;!*O;y3yI)*CDa3#;)NUArp-b`dY{@k@mIp-^!&Bgf;3Nw|&y`!`N*74yc1 z>dg<B6>%ly+r{96komA_({TQ<!|E?2WnRty08{2OQ<coZR}%;^C^q;m5CvYLY(>?g zTiW_ewOj-xPEueJF*k-9S$snP1Pl`M5qB_+FxgWPt|ESAxtB0zScvoIDVWV8+ZCwF z;F8rv2EE5aOsG%OA6WbU08>c(Kvtr65z{W-V<)?H%*bIJ>3#C}go(0^J@qdiv#@2$ zDlp2bd_q>({;po{+NK_3RfHJrk6ijb@c^NYC7^VOFF;CU;f*4}UZ6lWybm`J4JEJC zKW}XiEzcO&@EKzyAR=-zkYYicl?sw8%v~eNz_lLMWRJuVN<y2pO9cyqM5r~>MjzB( zg6*iKu(C5!z#c|saT0~E(J<i+fc_Gbrm=Ro^hGv!1y|xEf93}}QK}DP8eCg{0=N1? z=vnu+u^i0-j+D$xV6Y9>F@ZZcX^$0n=2snyLQtkkURjtc5`aV$XqB_;F%67>jNL4! zu&5$w4j6S5z*KlaN?uligmac@$Axq1HLA=JPB&-NZEnSNXPJxu7eej>plEVMCUx0r zVo!(>gGEO$Mxp>WQRSDEiAYq#K`9J!1*_$S({7wg5-)2K851M$%s6Ss2S?^407Z%_ zV!TY8Qp2AEej@S_m{ibHm}ICDvLdg5$_^kdL~P;!1?<CW{-CZ<8(6}e#l-&rfh++A zlL-J6>h}Qje-OP#RJ=~k9i+6Ph^Vufb|q=dy^|CyYgRyo<;lenH;Nv%OwMq(Rg$B{ z-)*troy9d@B`*9yblVi7v=Zo*&2bDLMGhE7on!d@BP(x+9z(Kb(41K^B?pYm!BI9t z_`sC7W=RA9E*maVR`&##E(?KG1ZG(M@A`(YnL!BT0ftX}7njgwjJSfpzEz9?^?DAD zh>uaqa)w!mxl<P6TLudM0JbDz_e5K?hNuE3m^hRouz!ZtAZmTyCnxMnq&d9Qxn*%h z8805AQ&6o!S^<OaFs|+ZSD_KG2iBm;l*SmLm?AaVst#@GmPs+9+nV@_H+%|Yhzym~ zEVNn7$QM`)7%VeEObAq@9k3e}33%eQh8bId?i86cnLWAx07N2F{{VAvW}0-zOjOWX z{iU4^MvWHZq6*E?;wGG#X8nw|f;h;$(XWUu=uNWy!;@qr#)4#uy8zNM(F8Z9X3>2a z!aAh#TZ?IWsGbQ>Vj}?N`X<O29EG%dgcfk>GUNvEMs!hDr-e_vB5=Z_e8fmpE4!_$ zxoH;VjxHx!6sj_?82YkmIJhlK>Y7!p<}^?_a2&w_O+&BriHr_mLj!EUQ=x(e2m?qb z3BT2D?f9NzmJT07#Bgyjh@9ND2VhkdM(l<-h;DG4*c)QO%O7iv*vG|2-wFyTh%2J) zIicOWvWW&x)5{0{0DX)rK%lk)O@>plWWD|<1;r{?No6~^QF7vnugy!K@ZPMHGQ3={ z5I8GU@MhzqA>O&BIZb@jD^7750o=QTEnXs&Bt>Bv!G!FUKY+KvHD$q24#6CjW|le( zZfOH6GqPpOq^x2W{4GVxK4T|sd-DqO@BEA*eGn^Zq41iA*gV85sCsk+g?1sKsdUR- zz783Y@Ve9xu&9f<uA+!A=LfieR6rIZ(>0VV_26R&yjI2WECf_cVkkMm)WNsu2DJMi z;jf}vJ+#gZ#x1A{7=*R-C8zf;W0z#PIF+3~W?HdP)U|M`vnv3ICC*1McmT9w1L6qi z1ODW>B4MRqp4i|4OaUFDOcY#TN$6;UMx{FHE`Tc3igL`n!Yq#+$1$+MaSjpTGT2`+ z_LhsL=pU@AwQXeo01+u#g>`S3H0lINnRP9w$1w=HONtu&4*>qoW}J>MD742n#KW1Z z#LdP+m1G%TVn|+243E}fuY@1r;g}vEmB@hv1v$wuj;=+);1yWnR9lA^S_P(1)TOj& zqJv1e6NXjUPI+-Puxgt4iMVwr7y}`)7p!a=ly9rhTn_4xE}Tn?P&s4DI3TcM244-! zLX`9}hl*TPmu1-nZU!T0PSBug1-(ef1&h%4+(JuNQEL`JDV2a}2QZ7<&YJNPGS_Hj zEv`2YSfxLZL@ACUE;nQW$wB%Us^g0yHFEOI2VJX9Vq0$R&;=91i+J5fY&V4#(d<KS zmmFk3WJQEFz%K=T-s6h6szACvSTwQZV>v}DcOeAI_)ILeR-1?`Gk`9ND5dy-I0&Uy zO?51-gT@g{KD0}Q`<ZKDE98=rgsg&kqY^JKnT|Iu!9z@-;Eb|fE;R)KmS05FlrpXa z;$(|)0qK`XLsmUOj!#FQG0%SA=$t59U6P|>1BL#~b|XzOelpeG2<*Vs4OL3W-L9$} zZ~+xym16@d+_;g=D54LxB`t>}G=Sq!cuyJHi)8|i6EI*BLFC+1u$>gLs1Pb4MTe;) z#l;>1I0Ke8_)CXTmxw~zrKbG|nq6a3#j+5F*p<D<jp)tA#NGgkak{BSFuq9iA<D3z zXWik2q_bpa#q%ty3w^~gQBM%hP*+n+*UTkp+$eDx8i7cwnN7?^@{7z}ttW59>-gNI z9YfAZg<xMd4QPv_GlBL)CB{f|fG8y`kR!~sDZwqduoUL0iV=kbXr<wjvvnG9P{VX9 zOU}cDNxvu6FkEdJc>QKh+VaYYsD<?}+h2ldiFAQNp5=#8Nq6Cca~_1P0<GXTa2bPi ztr6veI$QgMy3@q*FJgjUf55=tEu`uYFrG*(x`yD1A_fh$1$m|L1z`_@@&%#5u_Buq zu=_*tYCWg*1lD#28O^yM7PB`*;4s5b#{xP9jCi1lS;gP?9A*n1iL(bq)s-&6LrLY2 zE}@B%s#TRN%I!JkBn*oF;)KtOW|>(r95g?u0Tx#%Z$kj<%YK^x`a}Xy78WkYIk=@M zO`NEe9FMr+17eAWDvsFqzQ{xtp%zDUC>ogaE;@;VUS>;vB`Up2a#AF0L`}ht0~Z3v zQsc=S+q~3XUh@|a;U-pYh!lYX5sIRVFA<=6ij|$OM^cLWAKV?+<{ZhKHM7!lGf8IL z%k;LKrQbXdb!@}b9$;qSPTg8LUYa2*sG%#|tTDrni=6cagz<_Dgff^tJA<k~)#oWZ zs+D^R<F9iP=Fan|cWmit{-NptqBai6a}wlgE>4%I6^2rXwDBo(i<c_q8NIU`8q^sa zr9|3L&k#@{+W>Jkl{h2x2R||UmSPz7Lx_WTiV+p6mR@<DkeQfjDq?O}ma!{pC-SU_ z9)%7aC&Lh_Yuw&Il*UV|#xL_Y-o}c>a|LFue$x;t>IHc*6NzzH=O;{6&d5!c+6*B= z%vJ&Zt8lz%LWf#tY9461kTzO2aLbjB3M(SBdDLP^kZz$Ja3#xN6e-H}8>PowK|Xi` zh}NN0^s^9wyD~)J(M%KpV0$IDB`P{m0(rQQhQ3kQ=JIF6v`Eu!(=otC%)^aB*N7!* z5ekK(P+x>`()SISyr-rBu|Jep#DOW4Ps4Cn3KW#e!%glr1K~+ekR@uGO<1G93WnR? zJ|GK7E5LUF7@}s^%tELWaVjuS;f7i_;xNWwyfbwzUZ6wf0C@E@DJt>O3J{fWSD}hs z0OW5iGez44IGQdW-hR=TP|mcji*8wucL`^pj!hf^j&G<S-~i;p+Tkk`G70e(;O*)) z;!~SMLEKHwQYkEg+ROOa4OC+x$|cM(czi)3Rj%WQ@py}IWt_)f!sYQf64ZH%obP^o z5glDUcz^<D0j2@=&miu<$VV_`j!=~bBBo!6Ykf!xb{Le(!?q@X<vAm6g*Lk!Mv<MY z#_+KQ^A)xWC#*(<uc2dGEAS?gVM%=uB0=2~<r5d%^DM<K9^i3rw7#_q%px*va8?>w zvq`jtO*~4kb^T+t!sRN}IAU9HLZMd?NuS`NR6)21YaKcf?$jt)9tcc@wC`Z_#qEHq zarKtL<gfjm>rzqTFtehbOJAgPur!ZeV+xilR9Ng8UNwVG4~kpUsPLsFad6t3UBY?- z7@z{t;>f1mtn|e$T4-L#@0%<Mz;DE;Mwb4Ngef#VK4yKxPnU2Fmm%DvgjFp}P?#FM zOmI}gEm0iR!2vxb^(zw2JVBPQpwrw%OGYKej_nBYjh@+N4h3+)kzI_WHmY8Ok>O8? zNDFbYd*SfJ04n3L1!;Ur0b6bN>NRv#RTukOW9P0DUZ30;NoCEzW~Xx9Cz*9v?rEb^ z^^&7*%8pnC6-gd&382BCww^G<P>YOrF&}W8(w2)hsUljx?X8<^L>zYq5d@39UDB>o zu2<7pUTw!Pspe=QWzWh89g0M<7&2kz8CbZkVrLPD>NMe%hf_Z(Vj0X@1;;E@&7x&n zUCO51!a^_<{v!&UTr*x}guG5uaolBBL|iy=5LbhJ5jcEx=1`RD&BsTm&&CN7<`Jz3 z#TkHsh$GB%akEoMsAHXw9f+iXDNcG0qrpx}kE5p-%w@TLYGO(;@D3mrUQ6g-%sZ~% zNW^S|$sAvF!)t-3rdLAMp2={a#X_*cqC5$KGiV&8l(BHV!-NO*6|Ko|yn0gLE^Pwm z2$+JU_=Jo#7B_}FIfDs~Dmmb>eTRg^N!C%&vM|^k)}Ss6*@g#47aUzh(Q|h3fPBwT zkGX>g2al7HYPc6Iu3-#*Up2r|=q}2I)ioBvxd&z<g}tKf!u4dm%AM$r^+YMg(+{By z+Di#S)pE+fVM6#6#3`DoL8^UFf-Gw+xFrQ061-$;FO+jF3?TS6g?i8qA*Lmpd;`2Q z@FBj2@_F+U)FP~j3bkbmqiTC0q@Yl8Bwui5%Rk8R2_{wDo6X0cqFF`r;U3hf$`!Kk zQbO;S*^2LGfC+gIaE7qhfc0=!X}p+XuvtNRW7H?*sMy;BIMUE-?JE_o0~GNcDM6=& zgt{1Br4?DxYA`ssPq$l|6++`Ydm(CfjB7Ei(%Voda24m6mkd`j^K(PQ1E}#JLc58t zVhzIBW|ai!wlifSY3rsF!-<7X*g&vyDX0L7{{WjZMa9u;#88#B3BlV6+zW@c3(kFd zhNB3FZl%C2+*BSVqH;^4K<CPT35bHIadS=o02eD!XL7=CuZcq{qrAgwnY2MQU)DX! z0H!Qr{VXL)d(2e!M_q70xrRQ7lukws(+ZUYx^U_MUH2TV<dV+WxZqA87|`Z1v@dW; z$1vDxX3iq?^(mDz9%gKs{{Rca9g8R<T<Y0k&fG@_q9|r75nGKHQU1hNNc`Mx2)>ld zl{S<Rb8ra-zc6Wmb}rS=nQ9J^l)-UY$*7jshi;`dSeiOGh&XPT)_}J)!SNJmwbZ6J z$+SMa$3h`P*s)P6c^OSEEVK;$<_-j1$Wr+r>TiJbo-3#^0=Hx-bygmu>mW-c6_eb< zvBb8-Or0>R#xV?xz(iQA=oS51%%o(#+J;c!qVO{BWsT`9i^4If8Lphi+&dz0TSALf zz!KWpD_<>g7DZf(Qq^a5UukeoR@h6g2&kz#pK2uvlpU#<HOHWgHOxRrv+6ogT$E7V z!9sj50jc>NYcKkgp@goESconrRoeJ8Ls?2gfLDo%CbLxqAjg%&%g?37)6G+TK-zs` zm6S^f_xOp8-eMgxwvhk~dv*rG0irQXMl6(vF`5~7Y{U)|%r3?%tOQcuFhVO(?ms9% zj%5=hM&4tqHB!!AWoluhc<~*AiHB8xnEsd)y!3uzmMOb?jfsQUK-&t8V@3FI#)hMm zDk@bPjkqENqC7!GaVpeWNH%g+9!LnGRcu#qLm4e=+tO8K3JuoL=OaRf>=-b)0H5rn zJVtG9SuGvIYuv2MRv1JVw1&w9NwAq7QaTmQMcdK>v|zMe*nOq+DHs#TF;jdg_kbnl zG0*X=(gKVdCL;7Q)7Bs=SjkwkNO5su6f3L<co|m!^{I=2P1}!|uYzgYiOg=63WYI5 zwzMDYLyDcqM!pCUz>_UFzCWpK>}ALn(y(8_tO`YIp5P}M;#rW#ex)k_o1w-TW-N1W z#Io-I#fv05OP$6E5PoKz!wYA`4_-S=CIZ;{fCsK&E@ipFB*a46E#!uJfETXi8XC)? zn3aPbkxD+Y)?sbzFdm3%_;AWTBpBt+4^sJ-^OOogYgPpyD18eE)UByqE_K@+Iexik zozOMrRPk6e1+tRmHKML~U|$SeCnur*0I`Ncu`>_Gre&3_Rin5j1zH}^BaEqa$QV)< zyp>eJC<-dA^88Cvq-hGyJ7a9qRpSTd6-uh2_5rx+wBp~<7~76vWL1gL#JbVz(@RQW z(acLo1t?fKa}$t2lB$|fV`<3&tX5(lf>YuQ;JEUtI)YeRsX-lPSaZw*&fqrJ63KXg zD{I`tUiUUEW$<yhpobitOGGmICSqwV+*F1FG@nGvic+IC(15PO(`fEm$D3TfJVA;j zLCARUPGX6pgYF>VX>l9wIt#QjW@&s!4>Lp#Aqt3{OH5r~@fO706;80nw}|st<mvs( zU1T=Lz^GATxQX#RjIXi^VO?^)+`$~VmS=c`TnTB)*>z540BSFdGW;lITT4D*z95uL zOJ_*&wu6w=HBy@ziB-5lx9$0x;%ZfqDgHE!RF$iQj+gO)S$`fg2m%TS*QHEynQcNp z_CtSJg#y<+i;HvOR-B%(6>^*h9!-8DGmwJRGUKUOF;lirvnsAIqheArhzOtw{i2oT zQXO!EB;w(!0Bg;wZ{d{nrH(0LOYOKtXbLDVN4c#D4}K%Wr3uXur)K#z_?UyG?@>L( zrCC<M3T5*LNCCO`1U=TY4%O68U5J@Juv?PJ-9icjfP*pJfxu{2FA+po*&5dP@zgX2 z;xuqoeeba>Y5>-TfcHcy?vTPaGm)mQFw3`oM2sOaII+<6HFVev3;4K8g3KDQ+4O;S z&q%-Ul@jEtrXNEq$8OEzaK)o8B}U*CYAuXKVL=9Kk_Vmz-25hX<z>uskC;H<0_q?V zP~y$SmT)oXl~I<vS(-ZYue3VJoqt}KcwTm4IJxQ)F94`|a+W+XQMhmnq<{?BrsL^V z^BUv~h`PZj7Ptv!J)xh3tO_>|A}+Xr#VcImBCFx35N_ds#04_|$4P>^W}l%1gxXO% z<_PA1djdWPmxET#2I1aW`$5GhgDfI6jgL@E;Rmi_SZb$}rFu~3**T+K$_>Wvh#pZl zGQC411(Y3;td^qUVlk-^A0#rdxCX{BbuJPP$8wrh1`42vC@P>bfG~Q6VxD8DGZ7Jo zFsB0F#PT!Dxg{411YvPf{$a@hq^}c9QLr)|CH+jNwqO{xBXv4V344|#QsPOpmUo96 zid+cX#p6>k37{V=gQO@nxr9Pg3)H7-mqS}TVr(7xICW=;J!@5(eb!l+6f|zi0q0O| zp%)Zd<Kc`VK&7h(-P05b_Ka3Snhnq~w7ozLRU1ba5UyJHRT&Lf{*g3eiz!_Z?l|%} z3N@JB>`n-Y<<t9uF>tK1fO?i{)|X5q;2&gD3kNU-(qf+w2`n1uE3Ywx7m}j*vL_+B z_b}X*7Uo%ZdzV-3Gvp4Y>ttrYUvk941BJyN0tCF@*>GB#pz^mW&YMbEN76X#5}GO$ zl9X`^!r}OUXtg`g4|N&VGztKQfU{-N(eXFZ#Z>z#$}x#RLbZ*0mc@q5L9+x9wUa}O z{6qO%uz+l{b<Ir(n@g-Q%*;HBian+enO}5Ed_G0!A*#EoE6$II0w!uu@ZZW-8A#^| z3`#lFKPan~Do#cSgt?`dzHf*f&T2J6>8ZbQ6rjh0icLeJQz(%|52P}}wGkDP5x1Cd zF{r-b2HU*2I+hfIt>_#cWnfl7ZD*NxtS<nV@mVV+-LS>AHxZs6w7f-i%8z7Su!Qw8 z=%)0#d_;*<6MY-f9_;=CD&w3*3<3&^tnKj*Sd~gC8B+xbR~BHFHoS}=3t-_5pml5w z%oSc{MMrQ<Rd*UFtEf<t;1zQL6K60C`D0CRn2~V;v^+y3E)jCa5~b9C0RX7FhoDgy zAJ$<6?IA!oZf+OMut4OviNz4pD~f<v@`6_L{L7vMsAJbL45YU7U}K<Wz<?!ZEB)qZ zHcaGnS8P>47nrVJNzA#vX_kEi#ECMt^J}4qI0bTtfj(tNaa+eP7A%)A<YA1;(^Z{L zL|V0NRdbnk&L<^`dWzV`C$>1k5I2ijhj2%PUZWDjL7c5^yvqj*NlNiVexx^T`prmy zws@Q`3d{!PW})9UGP89k17UDfwac~MJi}p$OaMW_*X9W-)?^@UQ}TsCp<ie{)YvM8 zK4!|t)_IqPl=%`g6H<L(n|epw0cL_eg8u-hFcfdf;`B7NZW|oXeMnJF#tXZZ0`)3b zc#IOUdh!JMJMj`$Aaos&xI7WnFOeA-YoAdSieAS>W3h6>f{m3nR=Za7Q!+hCK%$pM z3>$7;qq4RR#QKL3#r?(aW%!MA+$xqWMn8xG7&(Hm(Kc}NG8D?AZsuwjxn&^?h{p+a zHwdmoq4xrCuC5_Ip_V9b=4PB(4o)J5TZ^iXJ>V_uIw7+Dfwu+Rph7~%yd^O?&#&U; zex7gFQs`iR6O&9ua)9}Wsf5fqSmuKetsgO*tg@L9xRwr>?b#7+*x!-d<~&3pdC;JF zWq5Hh1KcL=k1?S0!7YmbgLssx*_R;6sHL!!bVaRE2{CfSxj?=nwj<i(i0W>YgYyZd zQlpeK{LBwZ!Qud%vk2lY$l=_fapo+;4BnW^%eD6+ELnmh_lU9@pe`5WOo^`?k=Ydj zEp*aZFmd|KR}GI?;2}5UZ`^ZMG*!Tl5R0&mU0(!U#1^{;+r%qXD)a|8$vco+oOq@K zZg_)#Gyr<3O7s?tPiD0&%u%vho=TirnVO}w(zWp$MQ6>t91n8Rg4XUhumHDFtcx>s z>|)A?$o(TFt=P;xG=8@RWZtnds4$K*1J#5sGps632N2r{MaE7Ifo8BAHJ#iLi&!O9 zh=;*1J7Uq2=C|_)&61l~{iWje2K+f8FjZFGc#6S`A8B3>6@2p$Imh0W0HHu$zqg2< zst;dq92_Gr*Q87Ou!?K}$kv}R+4zL7#uE=wQanr;3&p_6f_6cfrC{}gWit6ipQz*G zC4FIK1m-p&{{WHK>Y?I5wb9!O-leNBO@?s+WGw;1U{_-wH83Z_L9J1NCl)=>!LUI9 z5MdJnDQI^HGu$@wqY(#AV3EEh6=CvAPofZPRlB800t8WP7WrX;Pow>bR8sDx_fcY) z1A>-?Lj1w#PhtMz&;v_Qej?YAwg-pcTJJ^5B5TL~qv|bI{b?)7-iPZiyle(|wjdH~ z1^xd31EyrmWj?UN$yVp<2k`2J*H;UQKn$<M2yw~%kN1Glw43lB!)X?m3BH#GUOKaW z2n@{<iQr|a03C3OiI&GL=C>(_><_dHU&8C1u7dXWaLPZ;j}iJ0_7g2o1y_K$Ra9hc zeNtRXQwdM@BqbC8tI!PA_%fMl8pB^!$Ay3ZReA}3S>gnyFL<xy7?PLy#8igBzoe!- z@KyW8-bXg5-9dr%62Xs}U`<V3>+dqdxGngJ@IWXaEm8`X^8NdsckkQ=4Nw7fUZ7!{ z06nbz@cYV}JfUmdaWPWx7vvct!dnGTZcn&bI4YL5i&1bbOkrjN84Df7fsQHKz3vu| z>v@i9mjd6#!EFPO8cOCpkDX$#E2_PyESE02dKu4gb88?FRp7i#oMM=#t}_U<p;60u zSVM@Ky{8=Sa}m~&7JRbF9~CcRuS*(<aJzGNcXu%^n;NepKIB&Z;~s<v1l_y1VFkPe zdHIfr!0K@{56TqbdrV)|{{UbDDL}Hm0gi&jfF2rommxQiEFAi#T-kenuyT>4FVb0n zI05%iAV`o{&Rr03Ky9jNZRTbH0@3skBfJkZLqrZRQ55*80RG@TFk1esYrrr8ST%7= z3UDf4saO8!5pKvcbumieD96x3u%^nf^#dJ(!&bnYVBRp)x(%`YdnlGxWE9S+7y<wn z&0~bkj9ER2E@-R38HHy;yMntl2+3aCGw@mm%q23xfjEF0lo(>TsLMVx7cv!dC~W+} z1_PYTL4IJ&b*SfUzRa!OKf4{F{cd=O3u<53KY$Y>2hsZz#6lP(SYl>@^L>DueB!yQ zs0lTN^?wqkQPW?EnlNnq%Et<L2z^lFsL}q)LB4`${140`T)+8*#s2^_KexoF#}N&r zPbB2vS5GIy3-$F0kN&A@(JqaV>^3eBJjH1(cDY!vwTEhl5gU?nI`3b=TPqTuXfLLw zLjVBEv0*k&m1NUDp|dB$7Eg0pKle*9uqfD>7;8bE7Voa$yGa_!{6dHn02N&U%s6F0 zRpnhg5EKh8fVG(dx=McYE-O09@1rR|N#XwEF-_qhUj!@21#Pio!N~#L{;5fWQ}qyP z{2mYwulPpo6G8@qo!{)Z>nJB9nU92jDqO*(jDHFsmtSxvjVjt9XDgSt;n&0*R5lxb zxOijJ<`}>m6XpEG!D?F?JCr>E_KWn~Ph~treuE^Z!8`=`L}80qf_Y6JETyswUe$=r zf<1`~gGbjs=kU1y0Bea$L}N4u<#M0E7`qb4)^QYle`0uuLjeUvib{hFdYt`)a<aF8 z>oK<+Pa(gw0@eaP9Lp@#v+^v|jYK;LG(JjFh(!t%4FUO$Lfv1w0B@KtHwduA+-l#Z zz!-BC@LvEhzgV#Nj+`hg9eknjLhF_TOlksg5o<Fx@t=xQ<dDv$6wtBZAv~~%rWkii zPlj6>9+uCe66J(dH7E=!fs2$V(JCex5p4k9)-mE#x?02#CS}8FaKm0R`{wW8b35jT zw8!3H<LVYjvSAa(Q!iLwN`51=p!)*--{HU%pjGHH&9w>O;QB{)3O`?L*f+QhpGdoQ zB=DC%ebWt|&&;LL>}XATKQQ58{bw%_{0t5Pe!q$PzLWR9CZAB{Z?(YwoJ$AV@OFHY z^!-oN_KfW?-7c<RJwpUEMctsje9fvUYOG}qTmr`U{ys7cN)p<Ss5Leh)GYhPrJ>(I z`!f80tsi@tk8|~XrNdNI(mw(Q2~JuKzI6xdar~}`dZ<I=Z?O~w=#|AuCTk!(0Sc>T z9|<4KL_tWRc`*=;)c6J0)c!UCY5GuvAk+%>V~U?_#Ghinai_v|SO_l15dn)sEf>`N z2S}|@gV42biqBqpeo5Fd=D)@;x*v1_b|FyVg!}L7D;SvW5F?(rJH8?Hsd#b&&IUCB z>zZLKvOUIMyw>jD)_<u^4)pt#BJ8E)!T3dm)N<$Z9`YezIcD!ueWWmWcz{p=&{6i5 zOZx@+sP(ve?6=}277wUj=2+~I_`~j%Dt@b|e`xtj8RWC`a*nqD0D${SmB+Q>eTeQ~ z(m+@0#1Xg-8z8v;!F_@f*mvQt<_eit<x;k`@2gL=aU7)&wtyz#g6<yxF=#YU@o`o* z%jl_J-r7ALB@X*0mvE-c%BM_j#%s(u1eM85^%GfK7lJojJxgF=U<PQ<BD~8By4f4I zR~*}ug|M-Qi@0o^=GnZq@<1ef-bK~kj6X5JMuCNyw0DS5q{LcJh{ImuaPXEza|c`J z-c?X44U1jO59@im_1v&^_)`H@pGbaXvDo?l0Km)Wv0t#_JN=4|BH%S&<^7^n4~Vds z@+8ka%Oz&8{9+>3XQ0p7KWWqaUS8!>g?UtdM!%2O@jpk>evhVU^&aN#CYXP6g91Z@ z$uKWO^cg4UdqUd0-+A^72{g>`P7^(LiqFWOujv3saNSDrv-XGGm;9A_ZzQ-F@EOr- zHR4^ZIs4z-Ly~^8v<qVW<m2STx5_>dKe7smQFxO6cmDvz$$Vwd`)I|DG<^|p_Y!kW ze^d?pSJj{4M7-M#j1XW@-Cnwia<jk=FSn^$i~xtqX!Q>_-5@p6LK&?9GuqG4bdHy= z%`eM70sjDm$nVS;NpeJ;iqEx1V4mSV>-zry7Fl6oiLt)zV>R&K0*Xpq9ism66}2ao zYzjUgP6D4%%ch`}o)f8s>Y?}l0EBC1>VEgOeYE}OPvVGdk7i$U3)+J_Va52Dyj7Yj zBGj~+`)vC)5|sWwMc42$rtf<{*)L}v@r(9hn%$p8ej>6^_hNp3jj<vDcJK>k={_Ta z=>k(gARXt+4r{CcDM^WIxV!3q5x={stJGP3t|6Vyy#3=s!Y%0!mvEarA1KV6Kw{=v zo7R%-!WA9Z5G7?l6B6Ql%{nCm;wy}4l|eV<iduxy)LrE1xETbuiCP1vV=$RTi&4R2 zrBkFJ#nfLX>jS|Bg8J#THC;`i6lZEULM*`t#Hu%##@Cs5qTml1hbW>gN8c2=R?`_{ z`*CIZ?tbs2tmYkg!S-RRYxZisW&B`$$M}C6H!*K8gY-VpN$PzeQuTJvtN5R_>7VKO zg;pB_+IE|d*#aCUMg-_ygTM5jui}31r2XGhSFhmID!!KN_+e`Lfz>PTKV#Yvmg@YW z3Q4Nwr>AZ&+^De&s>4;CO`fOe`cJ%>4=nx0*c2I!_fUA$RcMXnT-2z3O;6T#hbQZJ z4MN(r!v5eT$j6t2^Pl1D;G$o+g6(#nNrdpqWm>8&BI5fc;v@;1$}j}aExfQcr|p>h zzrqaxqy-eE*$7$UN7CkoR1_ZsHT!u@J`*=k6iZH{lmGy#;0g8*!XL5RxiwS5P46Q6 zIDSaiSe})-mc!C42M5GQ2NUnVtR!p305Vu}&TSSLl-L~&5$0t#dp?9Q+yVNp-oJ;| z4+&_r{>5QR`Df{SpKTep{s^NL>`=k-#jezyu2{UP71c)T4Rp-sVf$~mfjO`FKfzvG z+AP0mq(q)Z{?T_JJsJB6Tz|9tHX1Eg`5$SbFFJ$5ZWG)bpGd@7LF{ZzUZMQkhA7qu zXxC2%rMNaka8~ga+ysqUX2z?W%BHSD3dqbyJi>^P$eH>_i!$$pa+n@ivYi!adgSsD z0O%=mnb+0<i&wi>v23qw$o?}GMp%lGp-c60%5EHT<SE7&PUj@<MFoXT`6DUoFbieA z97kFe)mFl^CLxmHh?_#9ic)AJrdyO9{;+XT4GlLiv3j|Oc5YCZEMObNvPz=*sKf%i zMfU{1F6kFMVODa_+JD}~WEfeR(e@%(QOYJ2>?q_+8AfJplT06IhQN4?x!-~!&)6eg z+n{xmP_u5~klFN(;X>sXgk|-gCobhEjUvJ0QCyQ?FG2CCQkoU2<bO<N39RV8pjMt+ zbf2s0QFQi}2@#co>-&@Y2!H6HnIOuE$_+xCt#voc?bJZL*o3jd-vq#0^K<HGl`uhl zfG}J{iFHEb0go)As2sUep-8wiDr7`f12vDlE6tqe3vo__=Qrkk(SF!LqBn|DyfuY0 zb*t;Biijo>_+ec$dZCvG5qX!=h;WYl&m2Snn;3ra9Xjb455s8SdJYdP0Yd{oU?r5g zh5I2@6Pk`vvHV@Zf9{l>U8nv0GS|s&kSPMGz+VV`${o$#kHl71f()xq!$0byP+ki} z`=|ZMu&VaNP?~iYss8{~9j3Y)6PGKr@PD$7=7aLDY$P?fsagL368O)0su$0g{{Z6< zF_!6@Wv_8_m)as%ic*~nA)@IE<(@Tnf&EP(E{A};?!3mlS;lc8U~o8F_mmtfgM~{F z(94xd%2Rll#WUoV>L>KH&-E-*RY067MTPe^Y#e=|eic|w4tx^?rPMDyB7!E>5ZO3# zPlv>PkR+fjZ`uz>jtR&9$2tBbhxVh4rOZ=?KGLhFv_U9&ik5-55S6;N;-Qvz1cGD& zi6)DXa0nG;*AlbHt1CBqZZ=DT*nY<Z%=%aauK5V4shu}M{j*{laUgOq;<O4)9Lx^3 z#lS)VY)=Hpg5NU0=w~OD-T5N~SRnD+ki1r2Wn7vQ5{2dr8!jk9K_C|qBZz6Z9(iQ- zF3oCQAzpJbPFRXAA27;|LV(X^h>l3c0svR(zYHt=pRBj{{_x@bFEDrZzObhswdQVl zkM)>N#$XX!#~g^HW&MzTWA%3&D7y?CD*CW95X`+;8D1ji<H!5L@74Ox-Twf&f9?I| zZ`u0B-`D!hU)lE`-G4C+{<oNAVJepVvkf&s%KS$3aDJPG1eL0PmQbG!A@m?dKTFL0 z1N)!7e{kXYU-loreq+!29%3J<_lka(-XFMrXYC){Hp}1pOCQ(z#Gj@0gP8r#Fn^=^ z%zz$O^2*fhzuZMqE%HD27Gw1M$~Zr6<6dk1xteF}{{X+^L)m_t`;@fX0Ge;#xc=Mu zhac|wkL^F1e2wV{rzgPye7HyF46-7)u&*171ZyYha?NDye=NWsv0vr^mRXV1&J63A zpPN>1q%hL`d5;M>8y?ujfH2%Q)?-)dePx^q{dXxTxl?5@(-^2<^nsRshnYnwZa|-z zR>$`4SFhaj7X44We@y(s>J!s{aKHH_9sMM~{Ae2=NeBK&9fz_0#Qy+O?-xIF>j#u{ zoAg+T*Wdo|9C>)5O{emQN*8>~?Z1pUUP+h|Gz7DG8J57RxGQYjv~e}ang%U-;sQF4 zU14&!a&LrSCUgUDkq2SYChg{5?HaPx+x8<x5;wyyZ<z5QrxJ-pMZ8=Z#5NsQUKipI z7<y$3@NbCRyO_Qd$r#LNl%oxE(sah1c~_Jz!k{q%Ic_$O@dj9^_b`s)gBfNpmI(^w zxQ|k|L`zH?%)|^+kKPeQ1kgB?@JmfVHW1f%B{#Cu;}8r61guNyhM^$mP^N$xtQ)9q zs_v9yK~ludqxOnK%<=y5Lb`Vk#0A#oCoDp|pVyhDP_evONn88Vz<e^EjKssEV=*Qr zp`xeY%dN&|YldP(16PJ;FG}|XkBB1bI9&laV=t)RQCCOOTP*!y672+_TybF2P*vS@ zLqOoQfv9R5QADDhks0(JqtJU7zy_<Sa;<@=cS|l9kJ1!by#=N?hE*)v_=i>=?J4q# zru|E&tzfVwsoV-A)AyLhgg9p;F?5lo^<5IL6;W|xUzQ0<6sz6BcBr6MxNcV)QRIQC z#WSc+(V)%`P@R)vh<q?3&<BP7>xpP<XtkTm^C~IF96*pZYPGk?1kl#C#4pBXN?R!2 z4B`fYTZk&{Sg1j66N3D>?923+m1z%{B0*?hY~HYeKzE`Z!0<|ox65#_0QC-vn`83J zJ)VWj8>>pdP^I9+OKa@1#{n-)9u8+20fhMTDi!X-KGhE`0;bN8>HXprkKQ1=KY6%k z<`(civc?5`%P_J+u7LK#6`7%QA1xlB=mI!Xv7>rxOuZHZrH2-~rOOq%#^R2(4l2;Y z2B`5I@wJRp(W|RSmIWcrK;+rczfoYB5V~kPqcqnH5I}Woh{qEtPyri3N^5<_>VZ)J z-8oFrajSiId4eYAn5^teW?mRW7>4P@wP_O}xb3V@B6Z@XC2S@NxtI%q0tx00?j^TV z&kQ0$AKcuP*Cfp{gJcugD2D;Q5*8uGTn>5SHwK&S6z(5FQea9ve-y8H151t$kpURo z31kVPaWN6*GVUvmU*QgcP{9v}okcDg>N+-Op623PVMokkMQB`Cxmfi+w1Q7iHI(Xf zLaL!|sfY=7ky_kN@o>&GLLZiSlP(3=%fStd;=Uo6Tn$rJc!W4dWwB5$WhITVP%v85 z#|+Jnrc_c3GuC;5N{X)7jTLwU!xcnsa}e`<%J#bMS9>`N2C0JEf9z{(1URT&fMMHG z{85+N#0r{ok~~Oe><9M&Z(BLbO_sx=D6kS9re!&gR@suR3o%zinlAkwq6cU>1#glY zt;>br>Rg#HM<&F0Xhy9)hW5AUN7!2%x|vetQ{i&~<`m=WPT#Eq{{VP@@6MmE8`hTj zl<pL9GwhzYiHeRqZf6JUB1XgXO6>+0c3n!WSgOJKsGS*nFghjmLmo+i0E)Kdn>+^n zz*o(s{bFE4bpRHS!~~0&*^i_U?OFYbgQbw+G-K38RX2i~^g=Zwf}m9<u@3RwZZWF4 zV5*FqlbA%=6s;s(m&+FS4E(C=wki_=_FQ*mBz0+B!Z&AK!D13;F}~%9x|G<0RhV0Y z4b7#H<C1YD_DOoxL5$B5liXC>00hB>T!k9UZ$GQz2&d|Qh#&X=01zPWxA}yjG={*3 z=%yPh+@tEryeB|C0T@b(!o2Xp3dYC$AQpWUAEXkn02&tl@o~IXKgJl@C`QjjBbi|8 z83eL~GdCg`3q;Pt`<O7D>pW%yKnBrODy^~%Eqj;o6}d}Oaqd+I^Zp_K0NeaTgZ2La z#0TX60K`A^nO45D{zAO{NBoC>wTw^)zws-j{{H~PFZ+MQ)BMl)na92#@h<{1xBNkA zj`sfmiBU18>G+-Glz)s8?{DjfX=l^^U}*hM@f;@aKjI(VuHW+&b@P9O%cwm+h*~tO z`cyycU-KFWKR@Owsye^(5Awu6h--1?gW{qEPZ1sM-6mKUC>n50^#Wr<4j?II$PZYC z8e_8fWT50S$2?|$VR2L(OK3x5{vqx?XZ%a{h=K`<H$=tEg&PY%+Eli$qUnQ0+#&+d z3v7jI;7C_tEEvJ8FwNUvAhUf(4;CV0LsiW{s4gID5@O0q9Q|MM06)L{Py1iuKl)$d zB+>K!CW`vs;vM6h{{V^q089KuH}!wS{{XB0C;i{?4QJK=01$eV%Gf<zMvvzkzbk?L zIR%!`2N2{RmL4CNV(Mzn0wxybDzegIpx$%%4tzlf(VhLf6Du8bJ3ld?(WgG4A5-{) zelO`c{yx*{qf_^MDRaCJRv4E$$I(S|%^S9U*OCLyhuB6T70lwVIfpelFcd)bpQKN5 zP`+UN#Q7oYNGEaLUvX0cQQXK|0JCv#a^MJLEzG}|Iny~j#)&S3M$k16{Ll3d-ajzW zK*ECem|(!P?DPaC8`_`h9T|*-Kh&c|hOn-Y$q59GSS40m!>jH80I2m3HK*B~v*l^& zIG0dbOGPdc2DK<6qPLOxBV2c2f5=}o!)LA-?#u~-W4IrHK?@vNQo%F(<pnhC%sBy2 zKrO+JS(RrrC=?!*h_hIG&GdzrG*o>SLR!u$sdbC@F#hM(6+sJNdzTFb9Ieo}I6MCU zO~GS8An@XDV2MZl@e~4h1@(D@sCysqh|#9hB6mz+gsDZU#%J%J-2G$wo3f70ma~{l zj9S-<j#y4;ea*|5XJdV$G+-IU;BrBRe8VcBsN^p<0j7|%lU>RRU+EH4Ql5{PlB3Wm zc($EHr&5rkguAQeAE3et7rex*qHlKswLn(UN@v_eO@-ditUvJ2_7bst52<9<HUSZ_ zsg7q*HO}rd{m-nqp2Jrk40^1Vg%6an=xgf`9Stc$&9TE1fC|HoX5G`mxQ6N?1+2Vg zG{*V>tE^Kf%Dz>J5|q^YoA{b$b~E&a)POPz(~@5585B|$4@$?<8x;5bRR$t)LG)P- z%RI?X6ZOyTXZ_Etv$;SQywefY12)wgh>Q9j;E_Pf-3x~&f7)ge1+uz)x}E{tnFZfR z)VYhzp){PPf?|BH6SD=tpF=+}2%6L4Eq{N~Ka5562&$ex6to?PTAM}tmUcKu*^p%c zAVN&&UydKFeaIj!02{VODxQKw;j~1eRz=%<M?X^1oPfbxv%DY}a^@kEj%OjD-J%d? z1{aEsoR}c!hT172qluW-W$SyH-P8~l%(@->pNuqmhbAo`rPi2j%i>UYfJKFRkbkiQ zHq-EQ91@sTqOq>KmmwO4$<^@AdZ^=@ux5w&@q0a!_PtArce*QX<8>{iSe$!_Kqzo4 zk%+zHh?+U6`DMaHuy8etd19q7wV-)jMafHpU!zbUrDBnCvpAM~XHUsE6lnK}n2~p! z)9@fP0dF2yZyB%h<Kt4+QW0sb558medTUkSg&GfY{{V;sje(Y2CL4?O^p3tEmvR2L zDfWbar_6V(Tx%EXoG+4tg8jrm2J^p0m~B#YCQKgKu8$SjVa%t>lm-s6L1ttGVYXOR zNuDq-G1UV>&V$m*u7;Je_7}i@;6Tt#%t4AebC_(7krl~)zqs4n&(?Pm$j7>##=jvx z+xbUPl@V6VKroDX^_nFF0vM?4X5xNAjfuowi1;Oh^%M%xN!AHf*((LA%&SJ`vgI0T zKb6>QNqJC}V<sQG{l`dFjve)6Zh=EzS^A!n>|caemZWp|%H~nDG_7U47PPDP0#J)O zC4e0{iAh8-fVz8+=>R!af)4^~&+AnRz_6CG&@65-_J1TaK>#I5N5d~MG-@d{K<R^b zsCfSX<dwfxE&;)_WjxUXfcW<Y6bfDPJ>sLp=aP)MF{mc)(uKM3a}G6aVaajS@wbRv zCbJOr{{Xv#KY!MJ5M~~XykD>4QV`dWcHI{Uqz+&x{{Tn_6><E=xNU{M#QM+Zi8v|b zsAi2ICf&7$Dw9<K%4c|r#Lj(GEy!>_<G4<g%*1yDZv;<A_!W&J3dtI^e8o*vENQGZ zs)+`pu+&X2P%9ppb-c^Hzs4GRh=qxeU9RH~K^ROe%pe>y@dNW5eV5eKC+Gn2gbOUC z;4FTmRt&rhG2w=&{^anOp>0tp1uIIxz+T}8VISM@M9|=%_>}>18GyKZkWd{(VA%cM zzJyhooKy?x;rL<8Ob=h|z9M|~?eoOlM-uhGd~Q3ui;jh^_!>UMB#MttYv~+r=jr%I zUlNI7KVIcN(3BVNap18p4k<nvT4Iz74w60vacgP(K42(D#Id`wDDD3HkN0?h3t{ng z9<~_i$OgoRD7tL^kBO}EC7<>>U^zf*ZOb38?mP#UecSUeq<;;E+?LDnE}w>7=>Gr@ zEHI=l5yA0^V+41wkWsj>NuXV?0TDy8Z$kXD3|<Rd0SYo<;2g5(LO{?sRhLA!n;!=u z@o-9CFRcA{WU+bA(teAge)oi1jRnD4eOVDX0N9+zVBJ@bf8jO2M6>?@7^a+Q)l2aZ z0EmgPG9(QkVv7F9#0=HQ>FGFQ!$$uAk{X&A8uwh)K>q-ZFi|=L8@*1M{{WIZKB2Xm zA<>8I{{R#qs4yR9m;n!n`W->4j$J^w1-YB%Z5yIc9$=M#uk6QG^Zg<lcu18zKQUj` z#G`_SuSLYmOg|h-i}kr%gE)%(G5nv1oiLxEi2#c8YP{U2&M<$d<x7Syx@?(|rFo4} zzOLi-?+MwJQBy`IlmKPcE?MFMd-)^Wh<7rAR1Y*3K&-5Ix{qbgng0L?X!QmPSOuEp zhtMSktfNd$sE=(h5z=1_V#9^A)0nDI*WpbbqA!H_0x_@mCJ>6zZ5+e^h@v|ai9*p= z+o9mgK|+2{pdY}D=&x#-pXB($g6-P=qo}c$UQv~SpOv;Mdjs&McfyZ^uj(%5XwTom zI{1ahvp-(tKG1--?{QXrXu@{5Wh;hw2dzI5zynGt9+8StF3jNMc$On0`sRMOsjv>A z+BG3!J<E~(pKtVyiTE~lc0kee-L)RX6=c6?MG<HExrVsq%>8F`uqfzM%j6Jrm`L~6 zaw%K-{v4AKJegS=XnMvt7+ka|V)$8kbjqn7#Efq3ZP6am;f&+)ktZMlwRn_-D++TQ zkYn1L2O@Jnb=ESj>iUr3^DB>leW%)g2nx=KSdR$_K(=njuk??{_!~PNBuzavMOW<Z z20%u{awH7tYs>Q4>Rh7L=h!gE^b|Oq$o~LPh#M51lwqhWtB?cTLUHL{@7h)acd1|0 z(kn+oX@ec|qc_J;GH>McBv4}H<C%6a8ovcvL}gzutgxLI=@JYA=kkPC=Klb3SxlGH z7c6q%^W*>>sN!Va@m;7asBzcqN4&w5e@%nsP-&Nf@GU}6-|HUOf|*$IVTgt~h=B~; zCJwBFv!x&@Nh?tEiIY*rAjCq@ml({b^9{*>a|!GTP+u&r_I&O5Lr+uhKa?eh3|y|q z$u>o%c!`0b9=nv%Tb2hH(H*dW%B}!}l{epcpoJf)5)}QaQ)X38J3%_v?H}2)0%3v3 z!7dG=m(Z(~LUQxccw%S929uQc)DB(Jhu1JI>ae+8ukrhCnEwD|6VyKu7{66;ysWp5 zk9@#D!Csq(`eKS6LdVuM9|w8js3+<8P9uCu08^6UBF-`(XzUfK_JpYa09%Dr5yTw{ z{lr_Y6n$)zqyTKRt9-DUXD8^NXbTNL({uN|5R8)s@f<CS6C)NDej)d$lhtFjl|^WP z?WlYuufRpM^2_d-T9lk$weBhP=j%I(KVvEQc2lS1meO7w{C_?vU7-rAe}fXnEmc~e zp-`_YDZpSLP~}_-4Sfg&>{|N~8FiKp)z<F({{U}NvShwa{4)_cbDrS9U?*ShKwxbZ zq{2Sqn3chm_GY5DZ*MP9ZKKS#zOEh8MX^d<nHWOXnh)Nhuih1`ssiZl05;t44X?o( z)XCr~yq^&O=132v7-7I2LRSF2^A{lK?PG9%WkfX?Mhp;YFpiQaV*36#79@77cx0tT zfb7hXpr{3dDr10Smc!`-kzu6Ccni!FWPoj41Qy$0%S1wofZD02sma-f0);vK<Lc@6 zit+INV3ocD`inV2>_1fkfpih#gvRdQgZNgPj{S2FGKT0a^llcFQn(v)Uo*z~%T?Hv zm-L0sPl>em%|&ZPn=Ra6V&MkT8%PWxa(@JL>YyqWpv6M<3I|g2!GmCKYPh(eB^lJY zqQh{jf4`}jGc2<0aLa?8!jP*KZ`vAsl86hj{^}5+>>up_(u}1S-9NcgFj2BBXB9L- z-d4^$W8vXEk+I3=3yC8QyH?+)Gae!>j)_XPtEA+QtQ;X<N=Gx~{{Twk#%V?<-rdVP zBTG_nC?}Wo^&JAt1Ucp@_J?abbgl!!MQ!&3uli<cN6sJ2q)eMzTrL7@{H&tva>T!X z>Un>EQDHAv_lS#VFF5kLfr9ZrdW=owkfp|D#mVOjz?Lz;3*`R*Xf5WV`c;WYS1RRB z)#ZeHK<#$CRK)<S%NWH(RKWe4BJXCSlzBhes7N8oKV_rC2C=cw;DlBlB;phVq^R^! zj}i3(_EUd(Vr;4I`im{0WtmrTJt~Xo;rM5p^z{>J+APu3Q?f2e$;t{a7T<`Qzt_}B ztNlGihnoRU%S2Wp`%}vDz%!#zH2llXnIO>HSd~cxOpllgO0cLL*sdlJRa~NI6|x-y z<kCDN!bh!#317~bJ5~8<{LB(Ly>G+7^$*K{C<B5&;hR9Wvm{oA@B38)fpUl0Xq^K> ztaLafA-&{z6_PqUiJ#I03G83(GgygYh1fs1UzhArc5RdyKYvrn`ud%Mm^Ds{i+w?f zO5D%W{{WcC(qHbOEMM2urUikJwN%I2PQd<<P#1sFxO5_CuUo${lYN=A0hF_dfY_MN z5D=rvpICM6PrTRhlxNbES3mWFk_f_K5MwmjwR@x#K0lA*2vX^=^8Wy2uw7E#Www$n zwpP0Z^@za)9Joblm{$c4%A0_ytiZ^x@d0rhV@@Kn1+1bPmnu1yu3$`oL8#3Gm`pn2 zP%kl*K~v_T0{AFFmFbV<g{c(;p>kVd;y8wl-ZSnXbr*+n&eDOcosoHD;rybIK}^PG zva+dO;Gk)fG^PT#4)ehnn=E1A<%8+WUL=&cR|50yRI3dtAIFXzpYa@~BFJDQF$J;K z6pl7(W+W|O)B{6-SW}3n6J(kSrGgsIz9mR|CG4PCb@wlK6_V5_FF|2%9)I!k*%sqs z^H#9RAkD<wI~jBH8b-F?cf@N?NImpHtaY;JkXW6owyWqUUll16e732Khc~z^pxOZ8 zzG1YKlsIhd#4^7FXAShG2r;s#&%O+<7@4^vcNViB7FVPn)I}*UX^tUh#|t9?95&lc z8kpzL09P9hFLTby;Fpu@%7oK`*5(8I1k$&X1$*RssMqz$#UPp%z!F7}cv;PG#jp4P zBv@x;KmiCQ{lPO3Znp%{n?r7GD-(Mvpp$nFql@SeCyRI#goE^knW1@WP|PdVj)m}C z5Xqe2T~^ZLnMW3TW44fY{WAv03}aE#;IE~t8^wyc<*rpO-*fkR{x8;lMmoL!0MZt} z5l^W;^83IiPv>EN5%k10``G>oL>l}<m$Ud|&-BC$2FmnKa`QWQjTd8BZ1sJ?t|i`L zv2!Xn6Si79ggMLyB)FywEb?k*Ap0W3fZCv25CSGh*akUgxOmJrv=S|U6-&ei)zN=U zz!ebBiA8jySC~m{hRnaBqt_q`s}D5l;0u#I8~*^vz!U+<jVIO~OSO`MUVyImMXTV@ zM4~7L;TWYY@QrfNH+?Wrg1@!CxP}vCaV+8+j3Cn<6_kajm0N-|f5Ii9hk>|t*H|G1 z;#P4j?iOV(u%S|L{saSv6k-_v01cha>Ncv;xB{zCBAnmjF+m`8sPb6XBnnc*4$^BP z{{W(*EU4ZVRg}I8lN%KBVXDMyHUyLmUDCN@gpF{r<;<c8J8g&Q6^EG6QGYUlf}$k6 zKtPS6g$LF(=DCTQtL7?8_D~;)cx(G;JpS>a!mmi*4Y=7Zxm7PZ;hBYe4o~J-**xZW zrr9b9ub5hvd`$4@Fg47x#J#vav(p~v3qHAkVA?YO0J8%Z%L^?C9k8&W&#@edjO(bT zUDmEAv!|(c{xYZ4yXx+LXvA9ILj)yf_BZsFj4l&aiacT^O8JzqSK*8c!k)GU$g9bW z99t@*B{es9%o2bHEJ_lsapDU2{{U%mt6!Qo!y69_RzG--$7Age8^HY$5HH>%RYR3W z@i+WGxGy*SC7XCft1BIsFwYDCii;RC#1>v~!3BrdW!qzo&gAY(!Y;d(Jf$_mslhee zWL?Y@vb!v*vecLWcZphinjE}q_#E}lVd5z!0lBOAtYX9J5h+-@loGd%YvvAHtFgt- zglObqAMVRS*3BA&aPMWWF%Cgr!pUV2xm_}WnRPnN_LtzpUD3ND{30#eh`_}s!yW?s z$^aJ>+Yp`#qD|efiMk`eHoOD|!IrI4mI;-AjKNf<<t#UtK&Vd;^N4q;L8w));-*%j zRq+;MbA-ZO2YE^$p;}Y*WgXaQWyUH5lntPj7lQK&_Z3miIyf^RIR!`@x?dnvrQ?ke zo&#GH*fayjGNSOca1B%Q30BWyQ(KNpb1=BufKFOb8xId8y-<<?3c3Zrf(R<=gYOj# zhwpH^7%#yd3Pp<O8hu_kl<mr2MO4of%0w|0-DU<U^|KAye9UhB5T$O}M6wKGRF-ZR z7{B4KXVL)5O17XckpV@#^ASt?U+e<Y6Ablxg;dHFhs5J(7mgyksyn!4uO|FUOk2fY z=2HTgJ2c0{;akhthxIp*6@@Xe-J5E58+)WhrRp@|n1BE@$6NYSv+hM_C~-m{JQW>) z)*M4e<%Wl!%)mSlXVHD9^e4y<q}A?tdV<l=K@U><Kfw|lyidmbv+iGr)P+kNAmpYg zwU$5tgR5ZZ6%5I>SAb-IhlPzXb8&82wqgjom9^Ipp_#&Eo@iBCh^Vro3YA2$c@);U z!m>ecXabGJx;@IV5~fL;=3<OWwxZ(g(cpk2z-kc8=jtUC*g?t<PJw-^8tgX`u)sb^ zNCGMNL;~sQmGdd*24FdPzY_Wc9h}qw)nA%xazdTPZsp8Y)ICA9s`kpYYCRD42n))~ z<C}!Qcz_d#q-RkJmay+o=$A*1Vr%$h)N<6pgBLZ6l5oh}v_&r*!^PW$O)%Y@$Eask z_31Z8lpYPopa7LXRvmE<3frL4KHx&&fjeZUn8nF&sQg5|#5lNJ7cO(kFJUzL1v-xZ z07hMgK;4iLK(!*OkskYsTfyFgmKoUVth{O;aU?BFPz~_B>Lvv!%pq^5#7q@!+k3=5 zkojN<xD{oJZUkbzKwhEyxJ|WCWySzYEa+KNMCs)G!?~|BBB&tfbBro5q_SjoR9e!9 ziHieL;Xt}`c0o>6*3M&*VjhJsy-SQ42GC!qC)QT#AmJ-L%ayy=&5=L@E9M~G{{U&$ zR`~*uiniu%+EV@=BXJ<!7Jbyg=G*#WJo)_~u28TJYCa{cys7sX6;**e7N4TIf`(UX zQ9c+md4rBE5(88e)pFN7%!EP_RTVn3b2wf5P^I;WYs|O!Tq6V{aE>5c;Em>`En<|Y zd1I<N?h>_;$q9=wZV7f?U>WI+wa#Nf9zTJ@xwPSguTsJ<45@<=jSF*ED1DW4O7nAv z2uf9oUS+k6&`vGYPy`zUu|pv4q1Z^z?iq~tp^HzHVnP8r1TO`}Q4e9Tw-t*J`*#zL z?i*XwTr24t3WbGnxM(sV_aRqC;Xo)Wy?rG!J};a{nA&Q+MT*}ZOUV>Rr|`eS%;g>k zi3NW$@ctpNo1G@f8O?#YR8&&E%+S{t5Dvw29418-ogrVCC7ZGh2SURwppD@d^8sxR zhPiF1iS5ePdwfiAQ0`XWjLN&3aq}KI_?ThOGODDAQ^MsQ^$M697~E@=A>CaD`)_XH zr<6!irTJQ2#>?23pwu5TELs~TN;2FWutFz<B|_afiJ1EXk_o)Z(<nKVUE))SJnkM0 zr%40c-AoRUy{4ng;3kIL^Dw!dX$xqc=KFjUxk{O0TJh9YP}ODs0Ag9gPScpVjhqz+ z3q4JNc+f$GoXaR!XMpR}QkOs_9eQ4mFj<2XUNMhwuxB!{^DFhD5I05u29^!gGv<OY z7!HWp&B|T<<3hFpesAJogJu1rtYj%&SS{lb9h^Ujk-UFn`-v;(^G6}~Po+#7_9R7( zys~JeptH<S+4BBO4x_7(ePWs=N{GaQ7g~;5_??g8=pf51N-NRBDk>q+%C-Vo6;oWm zb<D|<z${c5!yctcENi&Do2H>c45o=>9+wNX-<AO7-LBBnnNoJD-^xm9R;%HMfK;n} zRWBeLPLK!CTXA$Pd0<=?@eHVzPb?HAUW9BBI*jFAnuHghaVbf9A(ROKFP!HLwxekT z#kjn5OJE;5H4!Q^feQQ*q7@K1Iff!K;>CqiB`Pb-u}zu&A-NmHYs6Vhj}p4YCYhOk z24R87;Dm^^Ct=j5(rVap_VPs)B7%X7$KNz#Jp+QilRZ$QmxiJ;NDYAJ#9=dO(#?EA zlUUvg?Tkc^5Jq!Z7|9ZfE<h|!sh0?*1{*~~KvT@QQn=0qwImo){{X@?kAT~(Zh>ts z^9(QVz+Xb~xKmbBGMfIN?Xo@AA$L8su}5<1rtdt*2wt6ipcePRni~B#0l=P<=?c<) zlp%iLR?qnoPwIuc1yzVXkm^^9&LF!3cu($A6Z1aILe+RNU$jCsR<(XAD>#>?VRkeZ zP5ldkXtimh>t8HIP)p%`GS!L=Q2AizON9Kzad7~3R9uRz>2V$0I!G1K1&nv;0Y-8- zOr9VSnuXQI4CuUVg>Izf%kdm6ZpJZaHhC@`rd!Vo)HHK~Uu1pAoKF7$1fUAQ;bn*( z3;zI+LH(*F^6~d6n^*O5wHY3zvz$jmkGKp!waj}!LL3;oHw1|*6>9sa9YV^?G)n<^ z%vE6oG=ljhb|nLUd5RC&E~$*IF%Dr-h8WZ(d_bhDP{lEZIhgxcf)xV{evL{B>LvgZ z*ZA#Ah!rDjsYn$WAbmq)<Y6}f7#n8Z@Gp`vyjlTk(bWft?KB{o;VGuLYtAF4(Svx5 zCui*yVCh-5u2>s7hG#pAD4kqN!(H5=U<cC1;AC+V=%BBU_A0TYyIkA(k18ltKUk;{ zg^MZv6-1N_^Dg#(2UN5!bF8w`XnBh`K<QD~kxQDEl#OZtbrqaJmKhP9fj%N>ER;9w zbsm0D*<j6GBtQ9KOv4JR-sUZn6g0SEf#sHetW`kj#Lg%RgN>F+M=aVB;3z|OxMTvX zF*BL~C0)VySP(9S!*c0)SnB+geIZt8mhT65+(h(O5HV9CD(Wef&^BHcTLV>#ySL04 zw$@Rtbx|8|FV-F=BCl2(vxt2ywA23pq;Za1@auBcBUWqr@d~{AZ6~{6$?gk()Opru zT8tc!qxBa>Lv};yik3)&f88$%jszW9R}EcqAB5%>c0oeLx=6SU5ME^;F3cPk(Z2)$ z!m~rGovpHgGpo~K$@+$&O;FI?8NYC_bikA<F>s}%Sk`OY;C%>&PKp4y!rV9;Xv(TN zzRA~S=*{>L696PWBF|d;4EgCkq+hMX*!jl1vd6HjC(wh%RBjE;Mm#((%uz##Vn3_N zIy@h{HH9}6Y<J^`pQV*L&36uH76rR7Mu%I6k_)-zN4xlvZJd)IBt>F!p_PaS5W-=_ zER>B!N^T)NBx!PP2>i?whA){!tV_cL_{&DODyx;Lf7KS=Q0j7%=*Qu!@hOv#hX#8A z_eH5JU16D5TQwQj?gy$%%3B{+2)cB@Yz!<Ydy3+oWiM6M>a`GUE6!%IR3=?Z;2s|1 zXyv<eWV7MK0)Oz4=1?^+V&-0@@iPAa7Qb^M@eG!?uizz%7)uLR%o39qh{@(D@dV5@ zi)^a*4x#0Em=X)IFZGGhZdz10?Yf(wx0Rsx4VNC_k*JV_w`G?gM96`>ktss1^(@}m z0OC5kaDdRbu;r9%D+Otn#H2p2UB=6$Yv+JvLu_*29T3q;ph`3t%MyrJs&H49DR{z@ zm|I9!xBZkS@sz)aX=n-^=h%iUwM%VuMRA*5Z;VP7VL^6!-NiIDG#`c~X4123ub3#3 zbW?tZED)ky(!Ss5h>tz=Muqu^1N^FA#Ys(v@L#KJFzEDw964S>-*_~DJ8*yOtPuQF z{bEgA29sNynbbzekh2l?t|A8_gLYOTy{jDhqG5WAi0r!Y3?Q2cxS6!<*e&;1fTc5@ z4`vv;#sMw4O(ZA)!uU&s3huJX*@xw0rrb1%2ePuU-qBwS)JB13#p}8C3`-zd2f+c) z_b1_hacV|lv1^|ZY&1BN5gi4_s`zb?9dLM!*l`?6-Y0<NnsHNj=I}YU(heX|LIJr- zST1&lA`-^o&S4vZi={#X9iC;bBUKSKIbB3c!6@+ggCNoKxC}EOU|%{*JhRDG^6qXN z36t<lgP4k#$*WygH!8+7B{X872nbk7LNd|mf@-!5Q}Zk^oyDD;K9c3#I!!`_)12c_ zB}>lLY2M|==u!SmPf#m-Ld5g`02o1n@~eVgkX~S-Zc~<kYteTQ9?5xnn&x4}#$iIy zO#lqACb~Bsx~-U1320Z^UG|0#1<j_lM=vBR@Hya08sCE28V=x|xLoBsWr1Bo+Q<=m zA(0U_mpi0Cn}n6M><gQ$#FiT^Zs03{#A;d?&5RtdDwVW+FmIT^v|w5E`Gy6pQzv-5 z&B8ZZZ0k_Ov>g4O;_4Gk&@0N!;r7AfTes#Ij(sEBXb6h|vkxV#g(db1+W47@J=i>y zKHHcA-laVazc8&F(o24LJ7I$bYj>|8BFilzHg}#rAyxpdSeN^KI%6YEEiCfhi0$32 z{{SY)i=d#3Q^LZS^%sJzsfj?~WatZ5!5(wFP4X+{k1IN5*$(TTVm$!Sw@;hyPz8}- zWmhZ*a+1D5HdMk_sI91W3Vk55`yf6sQ1t)}t>MW;N>55R)3l~hOt+j3c!&T~c?-Bs zUB4E!@DRH_qMe6q(5T1vnE=vP;fWyz3wa5Oj6LY!)M^$Fr~FdC3657gaRhK+Q0811 zjEWst8c3|RP0a$0GyWVu<(+gN52Wd#(Tr&tw4Ho<zr%X+gNzbs9h#y6X(`ZZk}$Q2 zO`!@qF)fyAJXsQ!O0t+8V3lB7GYgLN^f{Z3(z@a>!A1s;q#+a<tCAlPCwKKP1*c@f zgf|#dgHbQ>D|l^f(H`kzU$)>@lI0x);$dV_Z{&byJMfK~1=VLkC^1h8#d-G{A&L~7 zUx?K85UBS;FE9(3GiD0~-w|9;>7|(%Tc%`D-YPZCCTza2(A>$D*K&e*V&U^Ndy5q{ z2WZ202&uJ(gWL+#+`nTs8_V~W8b<mNprj7`Fc=h#c3LNzHslFwDUm=oL2BYsMPdMK zCM|(v6G~0-V8b=8oXTY40dy15aT~FnQNOIxMoX|gW)4|#cu~vaq9C&t*7zbp08*9c z?ogwqMvqeE8esqj?{G>GCRRhE`;<gEw90bg1qo+E@Hv+XBB@z-nqo%EEmQ)zxnF50 zV-8f~!7czH#f&3YEd#G=`V$}_k+W^ubMYumm37odgX_f7j)|A%SY2D`bL|KY2VnyI za|vuHt<b)RP))WM;Nqc3Qtf#7m|_ab?et#|m@>9$Q&r%XK}onNgU0st7{pz|nlZ&p z%K$f)eQMzbL>3!s?01TY6~G9d5FcJ4KJxGff~^pO27D!oDW|PR7y*DFv~1ZNsmody z#fl-IZ~%#BtwKOma;!wvTDyF7_$n9RyYDO*hhJh*f>CmhvjAfefC{t3Hr^$bn6)2t zfUlDO08$^AF)~6j39A&volFv;tGgEoN?a~_g;Y|Q1;HMOU#a7Tnw|+;)TL@xI#E49 zm9-m#h)N2XQMq-2{6YlE#sO4A8s<A=F!2PZ2ua#HoT3VpLB`JF7a|-TFo$h;jeg?v zIF%+=zF<Wl8~BCntr&`*;=C5RfYi54)JYASwxMsaL)^8xo{qm#Z8XY5X4);Ua}Bv( zqp@W)bM@!U;Hs9TvSYbm?b`9v<{OHdgf`+Gm_->w<Mm4AxNPDVN-04l4BPHt-okwD z91tEG%rzBjr*f2932!7o_#r>cRDNb(DAldtlY>TcBHPKtLP}`;a}Ys(0yQ44t%0cE zqI-ZXf_uflmLC5AR%Q9uRI=zlGTH+O0NYn74CWZ$#$3{IM(rklj$VQvs?9|c=3%J4 zZy1kezJR^duY-v|pxHfX2xu*XE>UAdCW~~>GQ?Z}rJuA55^Y44)Fc2^7=^nAuvU0S zk25}@yoedyR8SBtPyllrCnn-?Mg1VjXkSZkD^~^e*;uy_DGm+@<2-ja8V35*3by&E zkKB<y5Uec-p&B)ArSOo+M&{jXDa}DhXHE%6O?;L{sL_lFSHvJ)?aqZhnTIfxuOpWo za6zaL9_6Se?VD^vz#wAM+({dk1s>w*09b(G6KA-#8?`3r7c##I(}gvPtydA}JXS?- zw&HDT1;YIyh-z5Cc?!*uLF2Jf7;{Gv01g1^Lu?;TZ>+JHD3o<@`tITb#C_vy=^vOB zSwMSgWAtT=OdZQUmI3|i7;<<0P`ePYpeBswC5LTmDZHaF)CD*z)l1xDs}^wyXha-J zrlLTF_=yY?Gjf?G2GuW(t0Z#-h|=X$Tnl@hA#%Q00z%@P5Mbz;0;k?IfL+ufBb=Ka zxZ#!`WGjp2FZ94NJ+A~4<V^bK#H1rYUsnyrT)eWmRSYkN5+S=-(MhyDMm1%3J@Nb^ zjRk_)Gqm>&mMEIr=fp-YG!=+|(6y{%2e`tZcB|xo45M!!c$N^_aPxf2wVSYT#TX8y zNw1f3>^MYG4iu<UVC9B82AWd?m3z)&@ZS{`MOWgCKZwfp1s`mu)hSKlY9oTd5tBbL zS1kY!07nO2A3+;83Bz#cM-*m@+^n<${o*W{Sicn=O16eRV(o<$tm6^LZaamVaSAVz zUP76FuMt!owW&e?V72jigG!^vKUtE3c@wVubt(iL>>QQJ5~vpe%`ZH_Tw<{=J;k|7 zhBkvB;VIhsxKawD)Z3k|N{_)$F6XJTRGFdC+(;CC^7}z&UQ9U48DSJ)O<Xp;yhB-! ztIIMnWIl|rUKMV)C@@hgKjd?8FKHiVw|PETvz)rE#t5~N;LvT=l{1BCS2EnSd=qqH zbh$o^Wq9>Xml3M*4u@<LnESN!vAUHZR5HaB3&mASItnnfYWR*M+n_Vr;!zWv+WIbE z!t{cc*sdo2?Qd*CHrUTUnXjKVFWw;8Dd96&4(4_VBH3rQ2s5axH!(_$Y*ejzWv-Y7 zS5?$&0W__PM?PL6%ad*Vm<5=H#0;T_E~5i*I5>SJ5lt6^+A}xS>?&Gd)&e}KH-V>v zz{=_kDBb6cLVbUOCV$B~nKe6R3zZE<7?+1ot)&V?A&^+CDp$c~a^?Yr03rG%Kw+K{ zJmMg^s+I9vqS5EXa<W0VbZj`>#&zaiSVNdg^_DJf{g?n%T!<G|C*E9!{{Rhx;V-<u z*OvZ-gQ$vyBOF&sQ8$JI1?_hNQTK(PK0L)jw}o}T7`O%ufKXud3c#$(nT{O6ttU~1 zXA1G$x`%yMA|NJM*$p72N;95ds)DgR!!V`!My-gh3b;q;LrcuUrjKyt_cuL7k)diJ z3x7)G6;<3(EVOhm@h$?Yo)t(|UDUMIMEb4`1tj{WR|~pr0v}Rb0@S_RR6#&QC75Rg z$93Z3+BAN!vISMSJmO~ZF7ECrdwdyy28VB-tQZE&a2MpMXem`~>N5qx3UR9=_>R_t z%ETas%7J?xNolS2a?jEMJsnE-aHs1IZn<ukaHuA~T+F;%ZRoL#)wQ@c8lc@c5V&l4 zhbj}8grF-ga5|f1zj1(4rV5ngn5>5o0c}jWD)oq4-qZrI4g@Xp&zSXe2Ow;(h>!zC z<SJdUT?We%f&@!C7swxYinBmtPcC0bjni+Cf)EavdGHIO*qp0Y1V)jueD7s0D7CKH zND${mVke{=COaLE60(*SWwzHRmv;^F5roOk2(VEUtezWXl6pWdkD89L2!W$H4o$`? z`rt|tlb`gWGpF3*YwiC4VIChbQ)(K8Q{pD^h4rp=5fzkJWqV?3K~~VbWUFNbZK#!Q zom^aWu@f{*2CKBD!&d(Q2C7jRe4~cuMq`sO1BlegDQYFuR}$M=j6l`PBn5y@NHUvV zM5TZ>0+L#pl$hdd9FWx<#R|L^7PT`L%E;~2OQ9CZd4q*0e3*cOt{RF|l#Q_-;C#J0 z>R7n^3?OvBv<j<Vf>;e&5K(t)>kAtQP##FLP4wIoF5IpPI)Q>55$yx(Imq4~;^36A zZkx_#VR`_?KsvwGADg*V4kBM4c|err>f+O~P+IpbqFsI=3PQBHi=B(JiJDB)WT}FM zF1GOo^ib@m(G4~_OT5UA<p`LwqCdeaUu@luM&#w4(ovPSB7P$}s{|ryasaa{YA=tH zEB&A7T)i5V4y!i@#kFd%-YPLMMrgULu~w{HMy|$bvA!Vog$AjoFP1D;P-?D@?0^f? zX@G&EZy4P~Y^hhny86y6RZ+eM{l>=0(7aSDV62L+Vp=kRlRTH9>RM*z6$D-kmH9=a zC=AU+jlp?Xcb*`Fm<+9$Tkc#joE_J3*mco@P^Q;@qS9S#y%&}G5O|3!T(`I^g<62G zmfAf***CxE_W?&jIW3KqEgD5_vWhqEX|^cNX)|5iyb+w=hCb0%&_J*+xUy|{RyU46 zNM5aM5UAx`&fs5w;sCf7=x>>2a0Quw9BjD<9w#)qE{hrF11zs}qp5<2Hd8N?`(?BQ zWV0-;2Zf)6z?5$2tYhLhB@=)jOy*;jh^DG>265bWG0tLIOj4)O{0>s!gPTL$N(rHa zj*dLAS#IAN0t^Ax2xjV%Yv|bf`iAinFFr}-W#ic&;}^KPiza+oJosU<QN(P=5I~M) zsFbQolt$Q?(@mV2h7!f7;LNhaE)bJJm`ccWl>OxqBTQc62#QL{?-9u%4e>B~Cxvz% zyMTNGsDx)C{$QCd-`tj7iWvBv)qk{jRIPm6S1h*g1aR7ceN<D08GZ=%cM4)2t zOwlq*`|4tcz>Qi{7y!lCz97omHQ2mDdDNyn9${peS|S_g1*+<9(8&&El2zS$h%oBC zOfj)U7`PmPsN+nu4~@!$#6}&n<56GDn<tN;+pnY`2DCy2Y9F*GL|<q%UQF3KVe^T& z%YU>lCc)B##Y}6KsjpG<S5S>!MkHYuK@b&7mUoJ|O1F@<uGUbYWx9HV@t})H@u+?k zS}pJnTy-z$OH5Ai)#5Q|1)(dgKr1rhw{?6*-4LS>y>TeSu85{wm{t*p*?OHv(6QiL z1TxrEq3OW{Ra?f5!&r+7z_odR7@%mq;$^jn8gs@m2CdhIqJTQBY(oZmjf{hkJSyVH zPKl!!f@n-Bzq#sCV@;Bcxa!~sXiF{l+XS`Xsis$~M|eUaQ+^K;ltHyt_e;qsy8t%m z^791SV?npysQeHB<ZZ`3;fXB(#xT<zK#78tO%S;?AZo{fn+sk6jq9lEuCr0@3xHZS zFbhL7gmf?K=2_}03SQ1@L^7R;Kq!2364+XSP33!L-!%ZT@mIK=z+s^3aG~bA<_*B+ z6zA$>Q{%YkBvz)2i)lh~MUXK<hu2jB(_R5|4x%X`m4Qp>xFE&AvWJeyO@tPczv`6C zpX5>^rF#AnCE`?S(JQ=5M-jsg;zfF3(+b>BB|<5=X=&lqd#fUS<te#fY>e(%Zm4D; z?%lHCfWjJLW+b_|6l1Z#3=KFd@?s=0^B0dTRJ}eAq)daG<KhL~uf`bwwP(XoN7Ed} z<IO?{bnEcWt5mzxU0En=NoO#5r7eA60b8ocm@ANRP12COiyAAMAP)!{Z2h=#AqNyo zx4CU48B2=9b#0;S&r$)~0rdmBrAiTpGUZY?4WRueGMvB{#0zn)LATr#qgQ$UB8FHt zC0)g(!ue__0Ysk?@`oj#69=7Gt{N*`IARQl0ouRz7A_SF75Q%9vVrS}z|0lh4|0VB zkSeMg3ts^O1m+~Yx%U_ND@%$(4pDxr9xNZTgPzW@1Qa;{`s0|she?aYXELh8?1!d0 z-e@IaQw4x`LQ)skYd&*3P_VX{i+i@AChwBcR4LHJ(c5<4a<XvI(}Md|mE8N85T@>P zqA+cxnp>zSlnoZvzc3yMwgs9?>KTUO(;ylp;e}O!UOAZxsFp!qV|x<Jo<3$c;}jO2 zj*jLK16dzy9dsh;esh^cLYG$4tyHqnloHUj^_B9?pw+|*T48nPFSumUplRVie{dZb zE<wM%4N?m`K8__NdR3Y+w3e(a-85JB%>iceDX{+lAn`Vi6>}ow0>hv7ECe-zSN((q zic~hZUZ#U>;UC;ylHsHJGW^IGFHF`_#?ro~YmLc)dw`@ssZp(%m+Ll5)@m?9Ld!Kv zJ_j%~E}j51W2stX5dcNR9jTPI+85#KA4(EmV*O!cxpSCB;G$gr00R~NBwHv-R5pSd z2v!^j@+?@FRPG8>!5(FNP0Up(>JrS@9%^gYhms&qGX#Q~n`l<UvU-EiORvi5VW)`k z8gh<y`5#E#ruxO2P@(-#Ij{9D%5lF+ny!U=r6mlD3GoHl3g{dc+)RblyccKr16IXA z<v{s_ArW*B2w01CRr3{W6rr-NP}sKZKm!NMe=%@27s}=v+^9<ipv!t7c!F<;*bz`Q zy~^7QySbO>FNiv&hv_Tpbq7qLKn~4RzKar2)I%=WpHRp5N&$IIVELDb?hO^;%qie4 zg|~iT-Gn8ouBF(6go?SKC#jS)+P4y=8-4Q(4Gsa?iOq~at_FVaYrs|MRKu&+reVPe z47NVeB??ruFOwAz8d=L!{Utj%&<0}6R25Eo^D0!Jv7I?sE$v>>6XHSu+8;8Nu9sGO zH#0;y1L2qymXs@XoCk1QG`-bkIB^lUw)()0iDhMSG~5Z6SL5jj334p8GxE_$0P~2| zR*Q>=gK@uZ(4{6gz086OYPR=shWT1m7s16qylh=&pc>ggu%|oBt2hR}@pN033{~O> zKmfY><98YuITfA(G<k_46>+BhL6aC$MQ6me6&xxoHK&4kc@WL8g;g11JaupeA|}cY zGY|u06wemQF?-0gZ!(s#6__0>6su^Z40jr^1&l94l!hReX)pf(B1M)zburh;<~0RT zU{ja~xE^Be^jMgTHG6nqYBKPuEoy+;xFP-d$P8O7KtYy{Je1=Zm15vgb+4qYWun;F zrm%(u7kKJ<e)8^5tn&Tku*VIzEaq9y@CM-(X(c<0EMozNHsKlKJaKRhU(9c)T?eBO zH-WCE8Q4S=YpAHKOiq@sJDB4cNQ#KcV~IzI6?%qYVD=P5y7wxst{b`>CI*FB;s~QC zA6COG3bgrzqrvrNlId6CJi9GNyKXOm06qTz<U!BrTIKhB%mP~3RT}pVgH41y!D<Cr zwN>=?M8kmw!!~~K+Ha>{7=Ue(Ro@XGPZIPxgU4}Nf(^#%8mb}<Yw}xB46KH9YCI;F zS%wg)n~7J|oZM(B9L5}&n@tdh>Rq1OoBhP#rBf+{4c<xUV35+N;~Wx`WyL>RNx6(j z<;LMwX<_3rV<pM-nQfYTraQR6(9wDLh_@{ouqu6~I+Yu}<~D|W!BTA$R#*B$vfEm} z5P2Nh7p^4&G;wiStOaFARX7JP#7dy}s60ZpxPfZo>2(9ma|Gp8#LcYb;v(&{r|BsS zD)SA<C_wGE-AbZ>1?&Q)tOh=_Y$k%HODgv<Du7omUva=IR@&>Df(Tdwk;O~y@fsHC zVQNHJB>P6$ErwQ6;BgCZCe&T`T7tsUL0|{>0->#<jdM&FB-21T5hCasF3w)!0by)y zt=rsSLMv&r%pz`&F3EQw({1!X+JPKPZ?)sZpcxyR@~;x$VpMc<E|v`gv&0>=qY9(h zJWAprZeuOJU>OPv**vVE*;1Wa>f>M#xIu0u>J?_iF@Qo{*|nbG0JM6jaeefa;w)9% zu}0twA(IJOfXZP`YR_h6&Z=RXsx9Tj%kKDtTHM##U|uCpghsH&gT@?*4vY~)qM#2f z)Vw^c4>n)RC>ez&P*Z3g;Yox|VH|1oombXa{{T{~KH3%D*;Dz&>*qWqe=5XWZ*Zh? zg1`gGiCRs-)~j30r`lM;OcerR1lcNa388~bMRivQwf&mBcMn1jANYeX+Z$);1Snzd z&Pj0_Lsjz!j_GUe;<mB_@%_ix&;{#8dxzkm$ZcFoZc}Akd9z|F+o~SA3c+!Zz&B+> za1p^)lD)xhZqvHVYz92sS8e55`o*C`8MAdci_7K|ji;E9yh0<)3$iyGwH_|z-^=eB z`Z<~xJ6qJcX>4qabnC9BT+nW0OR$$GPy^~+^M+?VLdu}$75U6q8F1D=*kq>G+%;Xq z1{H5os8E~vk4q}p6%%7d)i6Z30_}PiA4x@01(K4fmYu>kNH-MCN;a&c7*g$|DFqX* z0H}=yO#NjhS=W{}3yu){<^@)YgCWD>4T65Cj}AOb$tu+}6|Cih1@suVpK^kP*D;Wq z7T+Pn5W_|SYP8d<--uSYp?seDh@>1stQ7YIES}D#$Z*Z|DJa=j`DGLx+MFH339z_O z<9?;lqVr_0a@C{0QYlZf9aXOBhtGix*>u{KZ%2p^6IIKfq)VQZcj6&wNgQ$W1YX>w zmCmB&(XoRRu-<EA^O%c6!(in13LK8zU&TN|!@}wkE2vA?t0e*j(*DG#B?QSs!wR8L z1xk6YV+SajOQ~zik%JHz?WjsQ6@YDXlI2>9tW;ruaRjXbwgg92s{t8rCR>S_wpb0B zfV!kjv@gNbcJbNLwdeqed1gx*hj*a?6!%Mf>LUf6vd9Ctxsc|g1hiyW0IK0AW7G^< zYH$;9*SOaoQWsxR3*F`-t!@@a$lM`X{{SR)%@yk5>8is?!YKn#BPwc8Mu;xF1NW3) zr1x^*xQraJgx1upM?~R<-P87hAq>Ca6oHc<9|;d4QK(Y0^B87uVHATS7o-z4YXexg zj7mUkEZtlyz*V~$zlfnpQW$X%O`48y34;w85HyB4V5bdBp-IYA;;Z5;i7duRHaRzA zx6Hu1zELr!11;Q5o+gSk0%4~RL>&snKm~CR(O}rM$*ehubxt^e&Q>8Ybr{c5upP0! ziI>V+jIzx@xD6T9(i6Luz_tz5u(@JLR@Cu=DG4}+i))C;kZF<ecOAsRn0$wEZL?Pe zfLD#As8B&jI|n(%L8{VoS;xeB113)q;>&(WBP^J5bXSe9%u?8OW;F%!H<iUyw+h&` zKCw!~wZ1#~hN<28T8O#Bj3;Bf%%ETOI0E|WC59Oncg@4n-uS3a0vWh{=3f_?Yn(pP zrwYcd0CXQR`30yIE6k)yco}stOjwc_(7%`zNl9+l`(j^P0yj~#pdeqXi<B@cS<g_2 zm4emaAVSt&>p$ef98IRY)NslSF}~ZENp@Y}C6<6Mmpw};RoX63am`?8dX+<gSz02T zj1WST%|ogsqdr-W23#nV4%Q4b3$~ho4h*Z^>hX6hMtRQ5jQ&8%$AH{Y)-neN{{Ud4 za8X>>aa<wO#BZf{!HCqSxD2M~>JJJ=1brYP3oEE%!6J4>Zmb|#?SE3C#g>QW2V<Nl z*+snDwgONVJ}+#1yKg;lD@ZT6#jr=2c;lJ51d6M31Pi7bNRZ)&SJXp&MwW*2^orJY zL0FRNm_DV$;J*_BXfL#7Rexe%76D&KBjT<AEktbH;w5Dpu2>7Yfn3Wx#wc?H^;BYZ zqVrPMd+K3nK$$spxmt#x!f!8f_4AbolKN}7<kc<RyfMg$W)HM(q-gWkP+=+*wyM6+ zH>{@=Q&9Xye79O1R8DnccDak8Evw8#jm2=a%G)29+d6xcrg;7)G>|DqqY1h?j1Yy4 z_?3z2<e?TZ=2#}fo>ErUf{=kYn~B0NVG%t?^<taSN(}K3085E5w~|ogacf^$VT~?c zo0rAsaHcCw113-IAi!!DE_MF^WA3G6mabCl$5U+Fu69^aebflT8mW;|qsA4>&Vk?{ z5~XJ~0KA-H?NaoIAvP7Q(J$GdbyF4)`$REk9Ht9#<_-!8VG`SI?iiJ#Zk+WGmch!; zi;W~XQtRms6tssla~h%k64I%N9sO_?=Q3>Eya;8m!~-5BPPH*$3ZK-%1-Wf(tbtQb zZ^<mT0_-b3;EIIM<o^K4QIOlA=<_eB1>uEv0Xdd!xI0RZ9%G4SZK^|oKsYb?1*XOT zd5x{|E``f0BOn^Ii2!2M%D(uDEQPUFx0=R@d|yJwSJbfr>7B2Z4**Lpfq1#H7kgcQ z$yZUmfz9>m6QRSJePRu<pg!u1K?)Lp$58<=zG@>azPpsRjl^9wwhc3$rGmPfsOp_C z#V9tH0ssv}EjTa>xK>p{&9s6I=|z92wzv|Mp5b#eMO2poxFl{{f;fUHE<<tgFeQLN zfb#=MkeDtpIW6$ofO%;bfqlDCbPFt-+JFN|7(75M?i!9*;K!CL6HF5w68SJE49tbs zVU;D4rfC#Uh>q<)D~6FUFT`JQG2Oz7RKuEjVbH2b0dREdV&LQEAvM#D2Cu|5RK2ve zrTT$R*A$CehyVmU)W4d1!A4==Y~o`gU0)Ed0>3bRn6;d!U`^r;OJnaS^$;kvP?6*K z6AQ8P36?JeIB7#Mg>t1?YGJ{rGOyaq8L6Q7frV0eTbd<3EaH?fGR{hCYe0N%W?ZUe zO(pXvEoC=s00CDmse_JVwV;qWF*XG?ZLs@#<#zE|L_1PyOdoJXFZ+jNQWZg%!oo4W zhugS}4Zh-?*N6vJ7Q>GZh=8l(3c@-KW1qx977C%hT+YrV3rpMqLeH2Y7mi-z*m$ZR z%xQoNZue1kAdc>@Mqx-<LrN|rL8;UdYKyyZ_K8he8Sxdc0K4%lBDLtON3}GH=HjtI zt+g6mp4y?f5YAQ2hY=kD8td%=O$xU2Y`)clJ@u$=QJjH5a`~1esLQk+LKIB$8$1x! z-hf*728RKx1@#l1vfQtNQp=Xj9}<;8w&BHh8;su=Bf2!3SZmZkDa0&iit1K1f$<IH zlQP2F7tSEK3(7q#K%NZCCzmx>2B5mdV{xjLThCJpgm72IOwdyZG0Ik7C|Zx1u)pdK zOxF-j$Yj9WYGQ9fi1jSbxDV-ZY&S^-#j$vX2f?pV(JaXvh0HqDnwi|LUfY-;T(So( zL0y2jO;IW0pg5LF0n)t8sJ$~L?SLL5@QlQuPADo|VID{W^<M#q(rlZG=r=BEJKN$B zV18qRwbV<Cq9AO!(2)VjaZD8fOlCVKTOrEg?Rb@~Z7|ouIuOfi)ErjDux_EVRS8zy zO0x`oLu1SpSo7j4SO{brcEqy-+Ya`!i-`g>iJqy3OxLiHyr*Y$6*8t6dtQr$@5>sj z>&#`6;M;1;A*e8+57RP&C}`Q?EukH0%DQ=uP~B~;M_@~D4Z2Q<+kj}YX6QgfhiE!> z^#KCV67mQRUBI)79UAch0Rey##Y04<_B52+fWfHjZh~9SmUSq2idNZ!h<yl|n<AP; zPYgjVF7~W;6+y>85YrJ>op=Z<nL<#oX6Dtwd|VGD!&keFJ5^fEoXSFDHH()Z5DwF0 zK-qxoX^=KVfev|zDB1|y3ewqO{^6=QcVC!NC~cZ;SC5Hi0*XUF5o%Mq4kIC*_%Cmm z4k%nL@)MeP%B<*(;05K{+*(mUoDQNRmT9&sX!lX&V<eU`kT)(}Le>fAI;nIo;c>>^ z`G`eYz#v@;o+bgDOez-}<N<JHRC3=d1W*}IH7v2mK8s5>#-Z{dh8Q9oOjy^i_%>0q z*C0qOGS*A%bM_p}EJy_zi3fqa6>#|BT|M_4sjZ}^b0r`M#~pKrE9;z<&NdKpb$vy} zNf~4}B8Ke}KU%d@0k6xCCM)j%ug#eY!RHnq%ZrybE+Ov5lt-Ed=^+R^u{AJI#*BQh z>}x^1yOeHPmyv>i{NIQ)D{_E5tRT2qw+j{ZF{<->Knf3(smpw$L5b?nrHSt=>34M4 zu@9+$0qV3}FC0k7k&%>gv+2R!4Pz83V89OBir$zH%zV{0As@&>vKDa<aG~M2XjaGE zuoQfzed5+q{n_Htg<cZB5a~{nx2Ob{#6emCM82}QjW*M=F5NVwHkwg4hb*4rCiESS zZ1oZ*g@!>wi&rT%nqtW%h2~pl!uXUJHCGRArA=a8tAHxsoU?TN;uAn!V8Gkl9WeZ% zz+X7zp<Ui8b9+jP3*{DsmWs@3R^HN*w{eshIX&goc6&-v&L~hY3bC~_6b;)_>18~q z_Z*AQg<d6hGQ#tGv1H%~mI1D<wJvp=QA)Fv>Xf>ur32nsd_!>~HW8ARRf|xfO6Jms zYRhdUzI$S}kV>--=6ok=`VolNlIT2nj4<fMqI4?7+g{;93b&QSC|9<#TwD(deCYKo zYYBk1A`|SY#MDO0DSc6}p~A20E<yG~62tAQ6Ec+WT5>e@nIguo4TAQ_>N6ZDBOa}` zs1Y4#wxTvR=P7LfPRWF0XhgnP2MY|N&(=};$~^sL9)7Z=N|h>9sZym%l`2%JQl%bY z?yM5N1CNkI&QWZpkzsqc6_|3YV*tDgOQ;5=GL>;=HZJJMRpwFW>nd0O097masfeyA z38l4s8->VlpjM4D@dm-pWlH}5<(-UlQLao!Phl2-z!ws+f|RViLm>;~6z&ATfhyHt z3&z%3O)F-`QCAyMznYc&RImR4jY|G1RH;&r)=}r{EB^qh{{ZtX3z!t_Llk;5;snr^ zvf2+h#v;3FEQ4nSW>Un$t5%JY*Sf6$wSa6&RjnB%V4d|kbX*4Q7J=z?3xEVh#Cad( zX|S(+3;^X}?>-Nxc<%QaA`We+$gUF~{w>aI0{S`s0Pko}{25l;rn~<D;9%%z7KZ>W zl(Um;s>2C$2^p0ry4O_Sul_84gc?;s#>^mlAYoE$38Qqovs!RsOe0Lv-fgj^EUOAG zDy)`|$sCMfYhdizMA3rk9>Up=oKC8+c}s>FVW|d0NUcOc>ZL^25M8)o1&VGs1JEm_ z)@m9{5J%tt06tRU^af%>Ko={aaSt8z7hvE3wVGKl;O)8zPC}NhrZB)N9LIWSAurS$ zPAxEC=2Mc5EkLQ$F*!D;N2=Pk)c{ybGQ@_&2`jOF5WqeNOA0l#A}LIuf;?AT1qKxB z`Ffy5cX+7!%W8(4xFEcgoCwMx-A_DOpa-ghfPvf$PQ5W^hB+%ov{89!et-SS1;9YF zg7*0S1cOon`ahHi09vgC0yNS#eZTw$I=#5v-dA76-;o6g4_2|hC2;ro@W2&wUtSdZ zK^WjghVIkypQ7y;;;5q_Tf^gWl8y{}yzhvRT3R*UZPw<iDCY5yd=KzKNnQ&SKVqr^ zJ(nh`?(H@d)z5sn70U0ZT;9#Y#t@;r)FEsxs}?_EtN#GN2_}(Z#}376j!WqzWRz64 z%S{ykQ3lKz0dyO>sYB4+I04EeRasjE=2rD*i4|B`rmi7<3)ApI@oj5(2oU{TpoIZ6 z*vtX20~Ha=Y%SJhz;w`xq)iA?;89B@!64SH?WmV~^U#*tk=jshRAH<&p11)r--?3) zgi2AMT0m2&TI#r<3w4dDZ$evHbU+o<Y8ccWLY5%%kb4EyJt^guQT%I;L7~QBRgf9N z;$3vxa}PY2fETrN1*-XvRfbd(VMY|i<{p}5DFCTLl-U_*`j<klyh7AP0Z0XBCF(L7 zFkdACP!I|=62)IFEQOqkTg+%;RzcC-)B&}LE^0~T9Y;I6nudXc*G1+O9IyvMZh0%H z%iD-3%z$!OQQa1vzlFTC?=FE5N@nNPA#39=OPEFy0^;g-WW!j5^vgC33CC;P2|Snh zCH|t?C|k7UURN@c0oN9Zj)c-~4K1rPsF2x9qD5>%0YKORUYS>hBdK*Zy1}Tou)Z`_ zI3PExE*^aj(vaBq3qt6yX`|c|1pp@(^>0wJ2MY=pUM^5wfl3?ptazTHRhhogW?{94 z0-8#?7Ce9Y)%SLEI1yCbdaQ};?1ib-K_E4seG?$G@d#?V>BC3@tR-$f2K2O)vSos# z0a&Vz$g3Kb&@dE*0a7(oju`l*;aF)Xgs*HL2I#OC7huuEQ2M|`Bt4@A;^lCJK{ss6 zfENroFVVwIbww*kml!>)K+9SHEoKF<u$Vhr0Nt_4178IITMWcw;6EbPB<WH6CLy3; z*pZvCZl+FLm87cJ6?%--#4=#86|o>pCUWD)VwQu7a4j`(<`_!UU7!Pe#k4Sh8Q8?r zSb><voLd0N#HG(?6j3vj7J~*@H8!j-xuZAH1euT;q1Kumx3Z>;Ry8T01Q1ttBCCQO z^s|~zj6B0zlAfw;Ik#Tn<pQR{<-^1h#C>Ye1kaTyaxTns9(RNrZPnk!OPY|9GZ*3W z%*X||1oB5r64PG{7C53J8evUV(D0`R$mEPvEc3Rh-5lY?lB5g6eNyFzlX=77MgqVN z>Hh%W$O%3N9z!H*#w8NH!mCu%slGsE9EHXgmCUMy1B|4m*U`4%p>)?$nqp<pV7t-# zpI}njH+{oV=&eE=@p-&JLvTQ*9OOk8Wq<@zg`C7QaPuO(q|td-a^1P+k`@*gvGB{_ z=&Npqm%J@}L;+by0#8F)7HTQS(p2yjWiW6S)lp`eFA5UvFsL~m0)V}48P7Mv?o2e# zhwuncn+PYgU9ob{MwKCo3oNrt1ci;ILIhZ3fF>D8b-^yuoWkJvZAOrL7<|o0F>s}r z$vTY$&BUio=%k~AR}p8SxSZETxCNF$uRgVDfGt#I5vcehF9MbZs~e4@i2~|26pCxI zEP^Hz?-3jLF&gC2nI%z5a)Gh_9~-mm%Z^i=YFf@|N1U(X5R$A~2}&kG!!4l=0mxk@ zl0C!O-yT~+xNrqR7%?1yp_y0B3#iIQF-C>FV5P`37*?1d3=EulwCY|EFsjJa)swQ? zg?aPq&Q_IhT<#8yQ(HEmHW+fS&;S4efH)%6VbilrFgb%L8yEP9q$<%a9LacI46r=Z zB8q~;YSTET2K4PDV3kLlMTILia6>vkDguBU9Dn;1ck8sFvB6b!IfP;=ERF*>=3=fR zLZPt5FQf-QtS%L>HX2!&QLh{8K;;BlIflG2wg6`DE;xmwuTqOgi%Pg7%cv`EyI(h0 zi-$y%_`<2Z$3zMa?a>^D)+bQAv|MUpISXR}4a8<z>^TOar3k1(zN~_(>A7GxBAdBS z#G611i;9FTlTlVxM#V`<csI6YXhA@2Y?o6`%Yh7*dA)L5gfBI04VmD=B0OxH3`;mk zLy>03Bd8kUSRhi13R${<6~>vcXj}uBJ(ZF%rosX};fA#!n2V^Yh2?FAyJUw5S;dw7 z3OquPErIoK;vicA1({WRRw_3;mm))LYc+DjC@@0JbSy|nCi-OH7iWkujV>#^F3&Dt zwF#sS&R7)T5<-!Kj6uT9$<Ezx7x4#i1p(SI$y>JVqRZx@q;Q^V4V1Wn0@rCt{gAnX z?=)71u*)#oq>fpEqLrLP3-!UK3z!<6yG3_M#3@1nLsbn8ViTCLlII&mpx^?+=sRi% z6fH#)LN{J!Eef(<qlhS^Qv=v)J^<_~V*3)ZM(Pd%cC*2Gc$HTyQlAGqgK#37kVBBa z{^|nyZ>b-*P~x4Ltk}=9{{ZK*zac~~2MKVr>Vl>Pg=>S4;VekBo^Ut=gPicK;HX>o z*jdwvBfApLAPY&z8oLEiP&N>vw>ZK!bZNS1`a!c<(5$c|tmQOi;Yn+&DxgZ+fst65 z7mYiJ-)j)2?6eJuT}8HQO8~@nG>LeJ1CowVUm#vx${IN+<%6NC=l=kd>fz9)9uvTC zQzD|P2(&D^P{T0DoR;w;vca?$`MpF_U`<+~S>l?CJ_$(MOpPnFGad%EW@Z$DR|$r6 zL=uHYL}hlDn1as&fL6g(d4G>A0SFH34BOvB>jNik<vByO()>jLYfA=!$QWfU+Ka%( z-0BpxFyZGMkkO!zkN*J5TmAuT7Nd&NAd1&s32$^p*CJ>Grw%;-02TiL+JwB5c=gfb z3(pY=$qS6DIVuX*LsEhy5r>do0kP!b;Oa|c(2Wh|uDnDxs<Jjwg5SIba1&#*!zl$7 z%B6p}2vxWU%GkV<gYPdbukGXbwN{vS-m)2DQD?QGe%(P$T){&e3{WFVyVPZ&;0i%# zt5dpVv2C&;-J;FqrdSENEo%mmh2~mR2vD@yt07H0F#u8FQt%c=EM99MxHNsnXbbh0 z355lDV#Xyf0_a!^TOn6<d5sXn3VXzOjzp_bKwPfno8PeDMulIU%0$GdtOZ)`rJ(`& z7CGC0C4c$F@DNl*m4wLogYY2=ii8naS9c%raYIVI3epv}9dQ2uY)~sRE|>oR(HFQ2 z5yB&}i|DB_=po<{{{T{y$%R`|vWouz{9l<9opI!5JO2QiY29YGb<7E{lA}gRY5Fu% z=)a_MkhBXa-H@)xLX>?6{{ZVC#4YVuABoWVH^yeQ4nUakI0CAxii5_q3O>;U!ny%( zZBH<+>v!B(v9$~LhYCUnLc0F|@_+xt04Wgx00II60s;a80s{d70000101+WEK~Z6G z5P^}QvB4nl(c$qhK>ykR2mt~C0Y4D+auZl>5Zq>i&1xsB1)cu@*&9QR;dq5Xp?{dE zbpRnr%%@orsKebw>k_Qz5wr(^j7->Hl~MHolsH$2E+8sZ5U3TaxOX~wYs7I5W(`!w zEoqA2h^FvswToG0URaf!11~H{wbiUMv<1g-jViXiO{~x2Pwb!3@y{84V(!Mjh*7R4 zYhh8(NsvIuM&v1AxY!5Os%hd@{D%>4HtGNtuMloRtyKylfhzcwz%a;hFuOH^wJP@d zl)$R+OS)Aq<4Kii1UN6;uy8_}4M$$HHnAGXRIytsQU*eybb*m=V1N-n-Atvx+NHuP zms_SWHP^<omL+Ruyf4IH2%y;aE{kCBO2D8{dXAPZx9VET5UgS)5-i74?-AYAgn#hb zsC`2`R?7!<eZd28&n?Z$XiMwFM#3)GHO{)0gq561w<fu3%upx>dV-tH+P4at8{5pa zl?{uzfk0dL2rE|Brk@f1-xI%-*dv?Ko0*3%jKvsiVjM+797doWK%4!>At-M9ffwJB zA0WJpDNgqQQi@`^4Y3MPFtUxxc0vm--1wb<JKO*xa2GEdAbW#HB&SJNOm8nlLAZtr z9gw8M^8-kuGu!<}L>&pm#>|UbfpA;|%LvmP+uQEp09&guQuSpPYQz@ba1Ic;l?_)b zsaPLX8oE3}g!IH=O*+!{_kBV?Zgvda{K9|*Wqsx>%8g=$bo>LEff%l^EqC{DH7U9= zyNP8M;JAp6BQ@D*yvd_8IxoDy*1iL;yyf*7#Tt(FEoe2jqr@!5&rE9FvE=GB9}!i} zVhO^-P(W~VE*A3imuXKk05w~lWt_rA<SI5PZ>h%C$kLP<#oA&6K>8*|Pt>r)R8(PO z2BC(f+35(;BCa^wiC&<@o(@vvF4G>!AZX9IWpd4@8e=Z!ljD@b?n=?fTxPfEmN>-5 zSk`B8<$<jttQ<U$OzBiY*Qs|aRSWx##J+~H8Gvv*wyFv{nKCETR?N}dNI@^kMI3+# zR3WOSjtiIw(t|7oU{@8q=4|_djmzx0j$U9YA#ULN&JwWz8I3zp=MYX$7c9Dc6K1hm zxad0?lvXp%3Z|mlXeiyUbp!?9*pcWZc7>?H@d-TIXw2zC{^m&wn(c`hN-}w6mN0mY zs6&WX?iBuFW;Ifw#lpcIdPL4=a#R*Q!><$f(S_}2_&_K=RfsVYnL!0>Fs7isrM?Of zx+8^SqB`2!8Ci>&&z8u=g4Rzl$(+JqR$82_2Z%CdZrQRf3B-x1egK7Qh`v3{0g#%o zVqiTpXW?WhlofF<99D1|i-}yv+!cf>@`l{K8i{y-{lizvysLk336@)0`jj-!4oGgb z4BpIoj%BHoe^68<i`s{Hf`F31RR*uk%uQo`LfWTm`in(U)e{1c;y$w1oxoV{9ZH3! z;5#=Q8G<aiVTdam)M=E-A-}pcCyCS>7$ZpW0&S-ZX<*WD#3I0S^)8A0%Qongnh9=5 z1Xp2%bd=FO!9w;%LHW9iP-B^9u6X8jfxxda{l!96@dT&<rUXxnB$;E147r&jQ!^fl zHphs9>5^AP`hcH#m6Ss-QAdE_s5EbRZ0=xLZtD@Wx!}%a{I4@NaJcvZ^Zx)aq##<d zC2_Y<RSZUfp{2Y<eE?2c+^<nqrx5}izswnQQXssOD;-p=^BmS5NYUivN@b6Wu55{s z`j|r`0~<!Gz2a$3n2RBba=*-Zi@A6p;GX$`$bYFpP?SqtMit5{l3-p*XSy+F&;~5B zCt@Fn*ci5)Z}kbWh6rL`Gb8m1vEUZh!wN`2Z9ADa3R(_?Dj=?Ui8BYcE+G(xsO*D4 zbu5H>h1b!FO^Ps64Unyn(m>l)jpGu*!09jd6d_39-{uP<wc!;m$GK4A;W5>V=P~5N zkWj>OTv+DL=Bh3_XAD6OIauhVQxrh3&7=sXT1tm*Nk|YB9^y!)H%=n>66}4=$l>;1 zrengTYGw9v$HG@hT_I-3XH&;6*5%9U0YH&_asH_D5c@~-H$twI2XMPUe?Q@Ge;8(r ztRMr#T>c5kJ|*MKt*&I0RA+h)CFY{On#=owAj_8c5v8GVy>e5cbfWw-kA|8SbKOD7 z25HYpK*Afm)yBNXv~6=l2JC9?ITZ}Pr-zxuY(l$PPjOwujaDY#4y`c;0_fk=Vc46) z0(XBv)Ka*N-z-77EBcpNDq)AE_=w9sIDqK=&XiNh0hmb0#_9_hF&s6ROG22L)M2td z4YIjmJ*9brhL(2s6(c(!TkGPn@WrZ*gVPohVi2oE?f}~dZA+sAS%WgMf&}ePxE<pE z02A>K;$Oh{sov2pOGEudsL;R1#ck$YAYH|n76@+m%wcR(Ys$f}3cH2}Aq?Uvpk#*N zJYGnuJ=L7bg#yW{&HknbOwc*HfCE`j@RI2i{0`Q<%d~>x*QmCWL`u5E${0w65(ZsU zZpO%B3evqF^%$zuPr)8shP4&_PAJohjAYbTxEC;Sn3ESNm%>d;ZK&c+N>T9K06IV1 z(GaLh(!Wz6c724VEU47jgERfCA5o|g*{ri1Ca_p<Y3kwJi_~wxOm?C#k(s=(a7X^P zA1B3O-|;1B)Hpv8o7`x^RHMHTyaR*zm6f1irFK*I9>+HS0KZXZa|3DWP#SAn>R+7L zJYFMM+3^WzkfC2@qjkjqbx>dyK%U{fKwetJ5cFpxsFqPu8ydQ()6Gh-qwxz0HHoZU z*AWje3ehT<hlom)KtU4`i#v@$gloOb&$?jJQxD6xKBhk~e84M{Gl*7vkr8kukd<*p z2}IR>#S|65)Za10EEP)^DDg#klsA?=cg6@yNfVGup@NcRJYx#@B?IM!QL_)Y203Ty z5!Xi~t~;5a2=Rh_UN@+|bk^%Kh1?X%-QHIExm#UCD6x5iJd($T11{tG)J`q5yi8Ss z4cSt&s<?ON9U$UTtHc{RmXJ;pF?OHK)?Vu8MVt_{Dlue^3_|lM8fA&%Hiea39zj8< z;evA14=iG=RbPVG?@~)fZ6O%LodAtpa0@E>Bd*7Rg~UxiAl-3!i1zT8D&=e_BC)U# zR^Y51A5awsb`e3uRHQb~s2$vVKnba^rG}mRgzNMnH#J;F-OayH@N)4Mp?Ii!0#GAc zO94{_Rm_TW6i9_ATBQ!@ipWrl0_UgsmB<5bk3-yY(pU{!224!nFp!t6R};>R;b}tC zZMqIv+y!d0KCrLU#1$0^4<;;%9l>(Of#xvNEZFoyj@XL>Z3TmYjxx#UwRIGU27pye z4^U&*E!QF*={}_(%0ts7RKS=WmYAKPwAZM4h26mb_JYs2K1?1)B1z#uVL<Ur8k^dS z7n8i7Q6VWCwK@abYc8{BB?4X8TQlM<Lt6`X-ZvK;MeJ;p6GjV#No<WvWeVJ?haY$> z4|*Xn+bwBYtBZml(6wm6_TnOggM_hT8A2^9m1R373u7&*L~E9VLN%KB2BM*3vEn>J zjOQ>%z@?(XS!-i(@|qGa^|+UJZX6@{)e(MC(v$jBht(1E0Gc*(`cGg#nQRy&*tQ;U z^)xOi(@Q>_MhgU-x4w&%4(|b*@H|s~Z{sKXp5+82x2F3GL+OPi+|meQId6C^i}y$3 z7(;a!>=n(yOb!4|wu&5Qxb9TREURa4AIS$UVm2#z_)>qW>IpYtVMgcg@XPr(@yX=p z;`lUY?4R7fYegs~SIfJI`S#(N_89*Fg9gyBUpR`u1gj35bDn}F{kL7x64D%m$jx<i zo~DInbFwKtp2xGv{zPJeB^eP&ky-JMsx1I3MLe9sH8m@%Z`G?6I5t5>=T!Vbc7o>$ zD*Yku1(aUuf;2s+m=t(n+doPt++$OqEF68N1(>J1<n@~RpQ6f|-)9&6AE~~<T<urg zeo@_`C&b@OV$$IBuh@WBK>{$O^glBo9+7CjEf|?7tGhyPj7nH7<&+<72goWQTyeFj zqYcMj;1SCQP!hE-z%E&*{YiNG!#Any5>%iIFbf0nL%M@%Viq}KKv-)K9`{tfg4e-v z`*ByYmGogdFY7CDUJ!nx60Y5)Th-$FZauACGetu5o@VoHAY}ph&)7$tz^?EHJw}vb ztO^Gxt(%!>uF$5=P=k?oL>p4?ao!>*-}Q@e1>ITKz+ROA%>XQNRrVW-GEyO;0rZT{ z{{VT=zoQ=IOfVuiX#A`3Nud4Es0Qlkh0ME@0YdT}$KkNlV%5l24(=7NL1a}Q5oJhH zb@qg1N$ETzL7jPkzqr~H(nlho&}B6KUal*U?0P^xf;{dd6e(C$$4!nq1zS-=;GP-d zva8@UM^*$m04u?`sH*!k(yiv3muC9*^ZA0jPSea;n~WY;^dT`KS|al5;_5A+u_~~y z>d3zf0Wd(Os4wbQB1;(4_x&>2YTa0av<jfY+x?aG3_y{ZZjI{YxfwByA>|YC1EZ<v zG;5S7eBEEgNmy7VK)1owN)!idG(AmX8zNKjY`C*Ta7r@g;N`<omzH}=cR2w!!CopL z6>6tLFlO(}S1SiL)+N(uM&0RBo-g&{dEe{Q{{U(K01~aQ%)`;%T9yFYG<Jo7fp_tt z{{UHD--%BD08?-HKt*drRX+?M0Ub9lt54h$k_H9O6#&Yd%LOw56`QPoFm+tl7Y_qk zHKXpTT2{r<#&TEOErV1Z>KGQ69~$qSOOvrE_)lm!K3Ms%ofiuXzNfTdYnaLh+@@VZ zoFEB<Da(j1CwnO~++S*AiuoYQCVmP+?*}k(EbawiX;%{=M&maJ(B~H|yOf2LD5`u$ z&<@FL!3B>D^2f3r(Y=xg7l+h*?wF`mwwZ!F)5e}sVc82zlP;Swt~k-eG~aQU+Oj0Q zrK?aK0zQQ|<{rK{tVaqeaV`@H!A~S8O@RTO2~w(1!1pge;~r-{BNbeRC7qMnUuj<D zdz9`GB_)x3!ygI0;~)5%d_kv@0?NQxVp$#(J0Ly_xR(SAmA+V6F2ZLfLBm#u8J7G& zw&7{q-<od`*<tv{aIJJvt#}0q;<Fifb7`z!bsin=<;D^#aE+KXtnxEWIDj(-lY8R1 z)m>qWfOYLk$_t~+3`c@ih@B0-s#tDfDqvn*zlc(pqhB~3hTm~4+nQd24q=?7EHQYP zn5k0L(3^r$>@Ea402KkBxnl6V1EIhb*O^k{N<g)sIm8gnp-kf~8IB=>BGr}&V8zbm zf+0TfyUcd>nIUrFFlIP$SfDIRpSY`OH2PJ3<+x9%A>CyAhw|V<Mj1v2>0(yqc!7tN z-Y)V8N-x8$P~y$h%C0qr^wChTw%cdbgJ_P(si8)hP|tL3I5~=z=6N8708>@<*1MJ^ zs0T8x%FL{gCJZI2)~0_UN&}&6tRr_dhp!V8ATkabv?U%owgQbiE(1Yza!CIG64#K! zZ$rzX5VD00PN=rE;^nf8F=d44ugo;h(vQP-<Eg1Nmir2Z(S}6Gz+WM|X%U7EHbD7; z<~WxP+|*0BC3F3kXys8g8k?-lhGKM~-tx9)+{{W*vyof&!UQOnG#}YwM)V$mKT^w) zw>4txg7FJrQL^4oCBC&EWN14YKp0xL7D|5-*e9v4A)m>=S1u*atp%?JUr-m?gUAE< zW7s#RHVytNf1vL_Tl{4|)7QmT2&9{f6lE>Nak!&LI9G*?_H!*?^~7FSu@G%Rf!lz= zn~Rx4xmK~ohc};)PGBOsp@3$tEq-mAEez#QSCJ8m9or&;03b5$^)6wgAxfQAF7t?+ zEHzyULKd31Es`Z|$F)L^qyGRylyaU2u--uRW?)P`bivj1{uy9WmIV4MHR2TQ04bXA zdb(k1GOI-^Gfg<+Hnr1{PN}$fiY1mh#ZBB*{=Q*bC=9#_>CCe@fGG|t>&4U54Ws~- zs?3_n4oSDveijTL9V62@2^Qa+$0b+cPvQ*@6yM0Pa(n0~mPV`;8&v>Cp+q6IUhxN& zrnXaOlVl%o_Cjq(>l{Hdz`E_}x13{plqYi7zCighfJL*$Lj%6o6t*w=Fb5zGk-|q6 z5Km=S&MG)PVOC*u7Hb3v<Ta|y1WUAYk*n(9f}5tHRZbC%IFm~2$Qr+L%chVeP&8Y_ zzgcHFcfpluv=&w&L31jHD76RbKBSi<X`n8+Tu0g@Bl#%kIc^xkOSXZwKDC*Yh5n#@ zpa>LbFDDp`nhRS<W447{Hy2vLZUN7TIcUmoH(k`VrU;~hp06w1ZZ-u#RrNWHb{jwz zz0(MhZ2*ciE{LBhaJHZ~KtqYKQjQC|N4>RNI8L2DAs^`$9#lCLyg?dEM28BnzxJgD z048IWm~F>cmB&%Fo6P{p2|-2@F2Gv5;ncjLHB{<=eZ*WMO95D6PYHG;NxiRw)K%9x zU{C<O$H)O9<*P@yg1A=}H)DX@Z&j?cDJx9(1|Xm*l5&`}b;N5n`j5qC{#S4ydaM5c zA(l+lmk78&0#qquIh2^hY*>(m0Sv|%ZE9adLIaNC@l8W=mzoaXM$*m!UznJMJXbP~ zr5#0?Xf9U|P{@<kB1n^*mk;{lf94SX0IVneT7ToBrBU=jH~J;Evugw?V2+BEHj}8< zCwFEikx6p5%x@>u{{YUFr5V91Q2`AEC|hoPz@gY~9WLjr^!Nj*PN#^NwLDPko+sj5 zx~Qb2X*@x<H7M#78Y0Ed;g8Of`^RyVEpFoXPr2_0+`<Dh5fcVnL6#4Mnjl5Ua$KT= z1m1d<9|E4>1BMj$M{mWd8`|a*_#uY4N`ybf$xT8YsX)mntx8#WcjCH&KH$>Z3o$W- z383a%ZC7^{1{AqiQ!UiDQquC!ThzZ$OQ~|@)C}WSzs$jT_$6w8ANVvOj^c_mql`pr z$YQJWP*R})A#Ni9W<r>YEMjRNgcUHUeaDKen;B6zmHbD5iK{Lth5n!M2P`$Jn=uQQ zaoHFnxIRvQz%e<5zkns#ES@4KO1g;Jf#GKKL(lgpEqQ@B7&IeZxCewz3jWglknSb> zmhms*U&JXdtP{V2ViA@IUN`VeaWzN$SC|Y606ihn{Ys-UeXtx#CopjYS&QrBVr9N# z!^9?@e{m}-tws#4@>Dcl;3|BjJmzw)r3W&h4Icy}xI^Gv3P+hoa<a@&K)1eQ-~&a= zApuf_*#Ir(Sn4L=!{D^H;AP8{X5ZXPn_^m&7#YFL7zY4<KNg&XYLN$-qvXHf8beAB zKkgxK{z}Dmhm`dA0t~!OrKu|ARKlB;mIkH*@akQgl0Ve015nu#tKpPX#q{_zjUN{^ znJ9pPqA)Ar(`HNVY3Tq7NqLmU{{ZBqU5OX=GO~$Cgm7jV@$qfZ4$Rg!6}ElC$XuZ- zh~a;SP|<tl04!^XQU#9_VljkDCWo(v(YASkTEB%QN+1v=L|9p7aZWw~vo<`+VsS0b zDS>RU8(s(nUU-`z#h2H@0LNvBn$Hrp{$g1EF?{)nx?A}DC>wmTw)GZ1p|pHOLmbPa zL|glf+_XTrP+V7;cCvyBJj;eF8TCG(qE|88&MibXpO~VAY9yo}qS!eqTv>M9(3`~= zm)y&nWlELeR0{d7F%27z7&wj{VlODuPUUTRZQ^GwIsPT->-vVEwI`2>LzkvoSc=P1 z<uD3_+^*wfw{tk&W4PvRj^O3&jiq>;USVmDYv6R5lGhT6L@f?0xmcAeX-Qcn7bu5m zS(@G=Zq<FP0n^;U!hIizQ;a1<_c(fn2*TfS6-qvJ8^6aAkAP6j;;|`3?pj2`Hfp&& z#APr-+{XcWAY63}KIT!xO+CWKnfy=j0$Ux%NQ#Dsb+|H%xZ8>fN0JkKy+N<Mbo!Q0 zyO@k~60tHShZAke@Jhx9;v;>crr_N|;^I1h24X_ljLfo^a=VSHn7@Fe@Wph@&w}J5 zl@z=9$Ic<~q}hpM?loDIuEf8oM5rRS8I5&fRI93mS&Q;BJhj9nge>L|1)rP=@W3|W zF=*=Ljt-@^0hlvixx3T|<+__vLr9>CEwL9euXB)&)Nw8>Etm|pqRF@mxpCv*uMpni z95D9)OD}U5F&B%1d4(f}ryNISz7?5tx`1sO;(}!255%uT^n!ISqz;fgrPGOsRLZ^l zY)RA@dW1o;3ojhsl|eY`%eV6YQe3(DGSDJq{6`xeCGb>6`>A8W0m?(Mb93`EAnpy$ z7_g_AbgOyzg8C(f4bJM!8dmP&0qS5OsAOLg>MSNuu>zQ40V-_8oJC^H!WmSe?g4WW zi;0;)$|X%E(<5=w27);etGG79<eb5QgiG$FZghSYe{X<q97|zyaeA^OtLAX@L>F$3 zrEgyjps^|~R+A0XddsMC#TC1T0GQ`cFt%I_%i3u=fk3?wVVMWSOFjVw4SZ6w6X9me zLgiyp-9V{OuA_CR3@dQHreJF3hGr(PSb<j^BFw~QCCp1ZmP#GOBQmL%1S&6?a?DF6 zCU9iI<cbtuFn%95GPA3>LHyAf&BY~4V~6>PllzE5rrwy$96`eYF_b*ZI{n1NyzekG zG~8)p@IiMku@;*mlZm1uf?je&81eCz71X>(Qmo&`-xFDb2^PcBKyv>8k|kNad{b9g z?jmfj{xlSdD=lGp;v@++dbw-NL;-US@exGHHE_K3<_k3~2WBdIFJ&qm`i>-(k2YH# zzx`uPQmZ0bR8V;vmiIhZa!)D{^4zDZWD4YO*u)9M8VEHlpQQluSdnHdQ9)%v>skfB zsI^7G*i#vD@au>a7DRw(>7U>K02)GvZEE^Pt4&<pHIkuR2*T2r7{j`N(wNhFM7uEq z9G2kDBe$0{<Zq7<jd{UdD{NqH37px-sZKn-2S@(^1;Xe>)*-R|M9vXL?1OJPHxS2w z-w!c`h<0q8tp#lL7H!JyE-4tgqt$?H8=)?%#Hj*mrPpE<nbckzfU4W4A9?=(?2@1e z--jd}AiB}2>G^;VN}9^)#4Q0~CD>l$nJRE_dBieIwC2mZ)nD}<)C<b~D>CYq5CYjB z^91Y>(_@VJg;qreT2@oM!<H#%>wHECC~HFSTn4SCy^M!-6U|U!B_U?Hq>WXgm3A}| z)I6d4c)bnAPL*k*E8O*WbhaB8REWzpjRC=dcV6lU!l#9(-y}4FKwI#hphydnC}SuK z1(O%tKq(7FyLy?bmvz2(25OmXX17xf(y^QF6t&1;JO2OyM#)ocJ8%3&5gJV~1yx>O z{wgdA*3iu;6g)*CvKE|XzeRBxs-z8d8c;$PtibCDcGvH?K0^QtlUc^$F)pH|SD*g? zC<LU_1HnpD=loRgheBe;0d<=hNCRO7NG}k#TM7wl*aYl=e}hXmrtY`4ZmWt%gnouI zO!r};ine5y4GNc%*7>UO6g1gwOLWp(DmFlE@px$bDhH`&7Fk~{<BrIKRmf<p{`Z-6 zq}>w(qXXLEdsnM_?7cn{9d8Cz*e~smkN?B~DG>nx0s;X90s;d70|5a5000315g{=_ z5K&=qfsvuH!O`#_;qgE)|Jncu0RsU6KM*q0nX@}X$mIQ{OOSBG3>lW!5Z<*^%anyR z>6bcO0YDkQ=9by>GeEku$J78XyqCAZ`^+krZg_y21?lfnjqy+gF}UwWqm^JW6tP-m z26D>zV&zbG+yr?XMB&A17)7S*)D&>Ka^L0(8KM&D)EOfvI^r{vxmedbDp9OgYL8>W z;uaBYB9#MoUE`iFNTLEzH;Q6n-Zn;^20k*yh#oFGq8K$9w~E22F3a~FXfdfoE}7se z6hJVyG0Sk~R)_$3AO?^ZZrHBd?*IjM!iemOmFi*oGo{KJfi>Fa-099!h_g>~0b@Z7 zKtt8sj<Jn>EahGZ^cw&okIYbz?(GEuc~)H!13lp^*s^iU+HaoGq*}V|<|`|Pt0ltF z7(r%b^hr$)W$iTRG1^lOZt`&~y?BXPR>cdY8TpH#-HaeEug0d6L?eVcBGMq%ShNKB zBb3?m0o@Jk?ql+bGNcfu?Z4EZ3)7jB1?K$9I{|kH9Ag;%VDZTSS;pf<gS0Rn^3lyh zCEUGrB&oFJ_XZ)aR4%1C$5Qkj(ENZD?@&krgP`S(C8M*%yJc8JzP4xTgOS_^FsgyU zgYv-%XPm)McLVs3-a`RI)B?6pXzg6Fn+GjqqMjl%UB7+hh%s|SCbPo<VZ(U1m80S} zQs{-*YtJ(GYcba0DoehNx;R6jnp8Zvca$|>2rk7>xAPqZc!ljD1Qf{(Xf~3IXT88` z=w>4%eWL+aQh-yMg`}dCTVw@|^WHEsiRFccVjChTveyx812Ezp9lw|YCAfuDHXdOX zUl5)~vj7ZE{33!ANY5H(yW%6XrDimt?KDJ?3S&lRznDW+E4xf=TW)~ANfo%zfO-r^ zD(#|Eg!%f4O3=)P3|t0%MWd@4IEusf1r%e%6iUBaBZ0+ws6cv{dIreuL%jQyl+iY4 zGawkeN@xM6Sf&Ze1=YU&M#7*sQKf@7Ed<h8Ee8vkxP_X|m?+r8^BLHffZ5tJ9w55M z60Fr?WYe0#-WWQ2CF~0AEFG>jPjS=S(<=tDy{16VxEl27nnA?GwZ{4Z80Hug^QP}i z4zCZUT?FmW#H-90l^6?(wh{q*xt=%y6s$xI#7c@`A;MMRH3llTZlDA#GA788b`QTX z`$3eVmanrbLX&(V$nYQmL5)B!9FWoK0<|pUW8n}PR$?N=<#9>`3wZXtz=yYJU7lF_ zIL-^)0Ib?^o|@n_!x9xp_fIpBJ&dwzJ8KmZwPH8L<|J@y!zUSpG)ic_Kh!Us_lT`s zMV2r|uI36*{$Nz_^D2&R<4nce03IrLe84*teU%k%%GmJ47qlO3Km>VXFH?vrt8&7g zB?0Xclj#|+BL)KcmMVxYV@XdlkhzQ`wiA8eS-Xe4Xq4!zO4)P7r~?<u+rQ!{cqfum zyurA42q80YEGiIPJdn}q1y*=WGU3E-0ee&$**%d&v_z7tu@s$xYcVNexmH`NOceHn zj@9J#2L=aiYF&(Ynu#zsLY?L-TMn>h{{Xm&ZG4YAhJ7WtH4sfudA16gH3o*_(wumV zpsdbVSj*DGpiF}#aWgks()LYM0t!<~f@BNecN{_7G7v=*j6tx%w^s&nB3cAKN$g8N zkTQYwImSO$L1!WYtYfrcP8pA)TpNZZS%hV2$S^cOyiniIF<s>)vk|Q~6y$LW1?w`= zG`4q8k0^k*Xx53qZZ1r3WI>54<}8+gFNllLP<uxz%IFMRHR%fiOTK{{9wnueX#W7& zNz0sn-)OFJ0y5ueT58n#fdIf919&&vFvyuRa*4~dZ00fDHOLIim0oIk%T!VUrR97R zFbl<L^#GPPO~1@LtvMIJX~{GpWg>#^nZ5I)3*rbNJtID20<qL)@4AYo6i~M-t3QSQ zAes}xozW=jUNX!gC8*e^m?JZAN}R_B(ij>5`hcaz2a_UfV)vqbbht`7U|1O#GOXX3 zOh>tAv3Z&2Xi`^b+|<ZSvzdfD-Vd!rMID%WRH&0{iXFY}?!h&}Kg1<UP#K@?iMeWp z;IW{A!Dgt9`-aOLL!q3c00jY}4hYXcPG{lS>&!95rw@n-VTqQ;^YIT@G89(x801B+ z#>?DQ0<!2{pO|n#KyHQ1M{EtiJo$%uovX|d((VvL7agmCZrMP%jje@*+Q5&iWFfer z%*ZDvVh7k3%x^k8c7aJNwMH+Vw}?Vj%RyLW0~vK0nn83*8PEEnt;H=PjfPOY-KZ<u zlu9ERX?u_HF+jRy$j%Rp^f$QBK#?zK1pz=6F8H7w)?o#QyKu>zIfAee<A}`_)3KI< z#?^h`o0jc(tCVlD#O)MIf5Z!f>2|f@ujXA<b+7JLu&f_&v@V+U3JAK6o<<|JW;vq> zN)WlYDb)kwDt|JvLI5r=<_O3V=%@g&xlDC0GP4Bn62t@(%tD3$XAE~!Byj@O7LU~3 zA{y>?i}j6?^O<z|Wx(iPN|;8DNpYL0oF!2~+F(mNlZz|7Lu)ZHT-IqNaKY8j$jdrC zJun}caPPFj{#W(V@<2AUTHeyuD3PH{GKuVAwc0+Mewj+_=_(fB;tA%tU^vVlPz_X- zot3zxR|aJp^ke2F2WBW17B9S7(H{&YTD8A1RP#(qs|p%{*8mq*IF{TN^0oVfL`!cz zXYzy=RNHdy7v3{876fRB!>mQ7a!R((65`CnmV#vLqvINmO%6Rv9z%W}-!La4?4qTz z!cxZ3c;ls#*xY+YUB+Odr(w*vUxa?q2kgADLdkp<1jcxP?2%m_fk)(*m!-$VYmBx1 z^gCFvH_vaGcEEwnsWVaTg$iYO2NLH1<|_ee9+3S``1J*>Tk2jcGiKv%t%@u(1vc<h zt;>p{G96n`fzeGbvdg?y$8iN1)<y44K$mW%#$%j81yTd-oISxy87DIdP$fiwwDG7a zZpfFS;;yZzzSChWH_vDVWe^uYN-qMcw$|<m?to1J@4Ubu<rv>Q5T9hpi}4r~Lk!}m zGVb7Dpw!<%z4@C{rK)6=zYzg!*4LNy0aD1SP~+r}d;>|hH_b|vV6@TZ5MJ;B`M49e zx&qO8VoRneq^K^?w{tBpDg;mN2w@fjd4E##*oiH(?jaM>DUI%UA5ooRFROnr!uF`I zsd+lBne3c0x+1Af8rt}c@Rva~+0JGmfk1B0iC=AwydL5}iBLKQ{{S+PTR_-sYFSBA z@m96DNXr`<BJsL~s}BApN(iCk?2A&&p-Ac&vH$~Ni_e*BfRwe4OiRlND(M@)5Nt2+ zN7c*PQ4=U=g95j~{@^1@4<k%6koGz>Code!*jZ#<TTAf{76w$hU2b+XgI4e229`uB zTgCf;K%CUbpN0L(^$0D7tj;9}jNTiZlOv(hiBJ)Eosn^4iT+P{lCrVje~^KvyF6xo zq20pv(<}oe=raD|j@g1$od@C}ck(Mt1MM~ObkD^>!$^)fJ-F!u^CX{^=V$zqmqQ(q zgNc?efGCdvxEx0Tl7bT2qcp5Mz2T@7D?0-}^xRObkj{_gKNtB=rLw7huf!@Sv&eL! zjIsElP*UM+nZf9QhDtmrKbQkS8FVQjFadCY@L&4o2CHiJG5Skf+M2z~VQGL&1~z@O z)B0daB_R{Z1E4_It2V%uOeF5a_iP}%6JReRXXc@1#2xgf>VK!+B%%KR1Vsx4doX@j z4X{mO{{YmCz@dE1KioEZfWwcS!TOv`p4IF7VVc+<L?41(W-<LI;^L}{l-1<^(xNC{ zjQ;=)!H9DgqWetTl5Lf5;^MXO5@8ju6SQ3bcR%1Lmy%cp<q)VsM`8TKx%vs${laR4 zRm$!9DkFFMBbX^5)wWNzEf$v`E~<!N=r8I0R8pCXVw6+x#8=x2E%uDr0ftUwB%`$E zRq%fh)X{GHv`~AefTbewbpStCQjxqpn1B`m6uQ|jvm*V6@J~TOh^BP+jJXgiAUC32 z?hEB*KN9kNWbN?@VzOH0!}#bx&9S1~BJr$6TQVxwo%VuA(4}Bx7vAF?fe6wj#G=FM z`hCP1fANUWq*X1_Gj}LkQlvZ`ed-eE8ByH}DN&nB7v;h<vX$2864jvG<CW&yg*)w5 zD{HgbEYst~o$<pzJ**UBKf%O0aMFE0yM{)T9EI+xI)aO5rT!o+j>6xd79b^o+~4g8 zs{a7=iw+7`VXwh)Ahu=6tFFj&EBMThujXRV7)?{!c`t!|u`0e{gA4C7R<0!~?=tN@ z!p00bRH(Ry<*hC-s-d&=se_1B>JYL8axN<F5(TS7s}pme)z;!+7Wo$T*%5k}J5u;P zk$>q&{yG(Zg%q#pAy|mC6n@!NOs2~Cy-HO5r=#v8{{VCR7{1TAKm9`K{GtY2F6$6N zoA{WpSIJWYKa2n(jWZ?$Q#pLVb?z-N!R9w?`H2ILRjPxMl<^m(z9SF|WUWLO3z`vC zioM53(Wz-W63)`HoDlGhC9BK>3TfX!J4y~BfHyb<UuHE*a!dhLL|l+fiCV@Z*-Ibz z<bz<a!1Eq)Gcy|ZctAp7954#v8XVNNslf1ggH$O;OvWLsH+85^RxD{364IX1m;+|x zn5UbA%8;h!60-3!ZPIy@&YpDhr81Rin0*c+B9fef@tLp<&+{P-uBx#CtKLp!3xpvm zhEQ%kF$h&F%zFzgs7V@Wkne~9Eu4AG3o5YuMW1Kp2hGo1@{`<^+(sA+)Zhdd>bQ=Q z*sZ}<JDi^9;4nup*j3E%#JNOxmhBK_LP1jCCvh~SC(_galxS%$Y{9!wV|k#s7Q+^6 zuLhZlMKUqmUddbnP-ze)6Cq~t;AOy-TfA(3<5f_@2Y^?*ycRxS&rJe0)TW9^Hx1cA zH%T0mYzY)p6UhFN?esZ5g0Uzt&VZm&e8L^DyJD^aE|ei7c9y~~@exG}I{|L)Sf-+g ztE6p&6<t8Sa7sYR1U@jrjV=R9!B@nj(zLwRv2FAx>&XyggvzBbQ<eUp2TMZTUY}e` zFhoii;~gH<AymL0=2{mG1?(dqw)WkH$EmKPTCb~tVDrM=V|N6Nxut>B7g7Z^kRQy+ z04rBUMh#k6Z$4<ErLj^ffo{p*x!NXWD|8i!uR9PZ6p&(3p`^bulaf}$M6+0rL`!RG zrD)L?qWSoQoB@?h&{idv6j%{-Qg?%8$4EzW6&QAVjeHCwpfyezjJ+{c1?VriT90Zk zh<TzTk_&+Z08jDzl8McgbtPK}9!XztdX{{L&Lx!K=d^G1LI+RCw*1D3tSLt6O~WH_ z3RPD6#voS0eIgi3qx2N$>^b}o_DUc&hA3NqpUkuJ8R=X7Ou*CNQJ>-VPxg6F!hYxZ zJ(!({qg=tBA8}Ygv}~^H#NYmMN>TppiJ)k5(5|~T>OR0ATRr-C<D>olQ*TLTS$n$G zy_1_@^NSyn0dmwLh6S+|<lBFR_M4_9@LH<)saz#Q6dSjixmFN{V#$i}nWy~WKQ#ga zWjl=%>6BuIVE+Ig;yl#0xKm2=m{9=~Ud4Z^3aggvp=0+4m~xS%B#`IQy~zIn3$X$r zL=QE`%&8QXW*palh_8Frb|qNNW{>z*Wn}j_s8wYHMR<j6uHgRSd_+gmTy-dfW(BMU zSfh<Z6`)mkrWX)Yv8!e)m;>X8nz6i!@rA_+vYEq}UzsXUfti+?SlPdmaZ)h=zAuyY z03%4)_DNuvF4E3mc1>uJh3f_&<q<}Jgm>^=UKc771qSKOo=B34D%=jp)T2uPsb$q& z;!(5!rgg*#C3LC>xIW-2#(~VM4YzDFhKS_I1*|yVEldKA$}LD0Kv`vrh#^lJ)!)F! zfdNq-PLoJMPD+keqnLV@tE;(L1tTWY`6E#y0A*=7iG&cLt&xMtu%26>JjUEQ6;v2s zwrp8K#9WWE63qbuSy!IXSu1^?7sfFyY8lE`23mrNK?(*Me&z9H8Z50{$G|4bA`WYH zby2iU8B^(QS*)_kwHD`sr5QjCn&MP05WtyQ_=1C)(wp@i%GnB_v7Yc)2s9HW7XBs6 zwzn$HXm}+?qog_xIvtakrD?uq;uq5pbhyf?5&{!!n4%|5FfafrW~hi;j0|ZO8Ke-? zo91Q^Ck&uvfC~;wi)BHbKT$1x=}$knul7rSy+8EOJ3lC){E@{g^2Vf5(CRg!YqSCZ zR7B4?g3cF62Y@0QeZ#o>l#s?NUSrAVQ4&|rsOGC<hzAxg5UqjC^-o{azSq=zUsKg7 zE|KXmndF{n<YnBl0_W7WhmL`$L)1=VvU4pj^CNy#SuF1^Gob~RP>^~Z<v@<2O3XH4 zVs;V6CQrQK=sB6VF5tB7DoC?=bWiyfLdJ0wT}z4&(;3HnvqlW=Au5W-2ME$Z$bo9w z?J*+CRK%okbE#U{^&ctkJ^E`9WZ&v}q7Gn6h#zG~PhDGij2$dzG1)F>7q^*fm-&9B z%jioQb2JD%CZuD5MC}5iLagsEIaDpXQ>2?cu&YeKI7A)Mgt500>#bZ2IIbd9UG!tQ zU+}T{l#0951v=EVzj2ot*|@EMp+m1x>CIe4W}>*2z9P-<A3eii`U&0oBO}8e+3%kJ z0H{c;2uB@5m=Hc;&?87|rxDy-kcx<^+nMG4vi+y5d}6dq+KO>9aUBG8R2;IUDj?KI zGsGK4T|}jNLm6fR{YJYW3*CxaES9y)XVgb*dV>+^%yd1&H28yXL~?}b01MNa?;54a zuu@kr!R}gFkw9N*M5RPrq@q#(02}N<ID_T`ad0mS7ide^*GI$m0Stxl9X&#G9wE3~ z;_u8K^~a<J40wa>&`KpkIHna4Y9hHv0<Ixh3F6?0IGS0a8>M!<OrjR(ZAvH9OUtE3 zF`eL9Ulhc050Wln1&4N3RM{+jW+pX?xsQ=aZh)ZmFD;ih4C*-6dI46)vTQnlXosk^ z0`3SqC+<AP%ok(k4mDeO61Z=H`Vp#O(9yM=#t^BCnO$_ba^>Gm!*M`EAk24YgD@rK zEcRTa984MEmui5I;OVp1L1PUFQ*(2Ri-|&^+YlIFp(;7txq^#S=2zv5skQEI^)x)< z8?)ODFkAyCX-*|TE_xthRI!TKEr0+_cZGMDs6lWSHBIzEN(GCR;t@>sX33fH0}`{) zB3@=XT)vkHaNNyxE(Ee$H<(utR_z)JjYQ2^G-0#cctp?FL$|0I%&<yYpwxJ$a#oFD zh}1a^1k&Nm{Jw~Nh)*YkVCL}LP*rg)=+ZNNrrB)0C9YJ<VzT9u45Cta{;9?XVWMo4 z^U{jDi!FlpoXw)PXo~SL-~213%bQ773v-x67y@NO68dD+*FgaB(ju_s>Ltfdu9X;+ z7!8#QjyRZ!a2ia(iB&%$1V)cY;k=WV+}ZAAnI`XvoXdgS{lPmm?iT6$i5&C{%FMT; zGl^8fToaN5w50jU@0H!q?36@X{{Wk=wt%XS8Rk+QO6OtFVA>Ig<;>)AL5*q>k(o`h z+zmF=8z{cH1`|2a8l9dd6*+RH9Y<X%ld}S_`-0Bn0kN21rUKgTcP5O(2}E&-GwM*f zB|Aro=raOeL3>M!a=K@?T7jAED>2*(BEiY4i($7-*ibGwmoX|KD42*wW|&vLhV*!g zo`u+2MS*WSsyLv$AW;jt)KRKnR5HcE7~HyHMx&9m^Wn;^%`h+4YV{7@&~b#OjojKg z_eQ24sdAN6#VW34kJN6AT~xZjMl~siLjXf_7pNC55Z6lTlHSt$^tG2VlKLCys~}&2 z64?2b2AG)XQ96SCkcy1v1Uq_HI4zVGB54Ym&G1KX*w+$(yQ6d1mAEp;XtJduX$gUR z#97fSS_EaOMsE=+6wz>9h>Hx~Bh4V)Ap$JR;wxHex8hMa6K`EEiGg~S6Nz%q_<Y4B zO7qib%=U!k60+#T4v$NJ;HQa6F3C|LiBJK_1nx`jAy<H`0m;l=y8De~r`!|C_YUzJ zwKXxV-KO~Ziehv_j19og{OSJyg#o)1V#<Yj2N{m*F#~M}5NF@?m-h@HC^)bCfr!v& z4NFOVlSM<Ka8&exh9FL7c}%EZ{2PQfHN<5eU_{Ia>4Uh%a6kM>M>bMp%(5-r6nu^s zb@_nQggG8_Ebq_=I;dftFz3FY)!N=$Ve=H5sucE39e?$uwi>#4vYUyT0GP@L8nw81 zqeZkXtfL<@FsR%{)q$*Qaat`I6+txZr~-g@T8OA>{o}lPEy^z`-oN}^%>E&BKZ%)} zh2O-@!v6sL1iFZW15BclBUoHSfn}M4n=1LWJ;D+&Fadxk!86(m4-pD2S69vd08j`D zQEQJd8Vk5~g5BdP$NvDcb2QBUW@=~gEx4`AYFjOt%*{;M4buX|pz??xBMxd2Ga?n7 z=%>8DyF>?O)pMOeu@q#L7nPM<#>@bfz))Us9g{CZDzP2`5u>yHcYr3)SDTdXOFPRY zn8}%EcqWto0FfKA3L_|#K!H-2g#ni7U+NNFgILw^8dz0$?f(GQ89_yp>?5cr2(;V^ z3}Yq5+7|k3ixQQTs<>(hCY+0wvStOp$OTXb3^<sHZjBboRYQCJ9{&Ki-Cpa|QdKs( c?*$uk{Z!P`4dDL(c8V3JhiDQ#mHX-c*#ZQ+^#A|> literal 0 HcmV?d00001 From d5f7f917aeb77b104c2638fb0a96ecfc48285c38 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet <pietervdvn@gmail.com> Date: Tue, 21 Jul 2020 15:18:11 +0200 Subject: [PATCH 29/29] Add fallback language --- UI/i18n/Translation.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 911bec4..2ed7484 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -11,7 +11,13 @@ export default class Translation extends UIElement { } const en = this.translations["en"]; console.warn("No translation for language ", Locale.language.data, "for", en); - return en; + if (en !== undefined) { + return en; + } + for (const i in this.translations) { + return this.translations[i]; // Return a random language + } + return "Missing translation" } InnerRender(): string {