diff --git a/Logic/Osm/ChangesetHandler.ts b/Logic/Osm/ChangesetHandler.ts index 31563dc..998d326 100644 --- a/Logic/Osm/ChangesetHandler.ts +++ b/Logic/Osm/ChangesetHandler.ts @@ -88,13 +88,13 @@ export class ChangesetHandler { layout : Layout, continuation: (changesetId: string) => void) { - const commentExtra = layout.changesetMessage !== undefined? " - "+layout.changesetMessage : ""; - + const commentExtra = layout.changesetMessage !== undefined ? " - " + layout.changesetMessage : ""; + let surveySource = ""; - if(State.state.currentGPSLocation.data !== undefined){ + if (State.state.currentGPSLocation.data !== undefined) { surveySource = '' } - + this.auth.xhr({ method: 'PUT', path: '/api/0.6/changeset/create', diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index baee083..86a64eb 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -1,4 +1,5 @@ import * as $ from "jquery" +import {Utils} from "../../Utils"; export abstract class OsmObject { @@ -40,16 +41,6 @@ export abstract class OsmObject { abstract SaveExtraData(element); - /** - * Replaces all '"' (double quotes) by '"' - * Bugfix where names containing '"' were not uploaded, such as '"Het Zwin" nature reserve' - * @param string - * @constructor - */ - private static Escape(string: string) { - return string.replace(/"/g, '"') - .replace(/&/g, "&"); - } /** * Generates the changeset-XML for tags @@ -60,7 +51,7 @@ export abstract class OsmObject { for (const key in this.tags) { const v = this.tags[key]; if (v !== "") { - tags += ' \n' + tags += ' \n' } } return tags; diff --git a/UI/Input/OpeningHours.ts b/UI/Input/OpeningHours.ts new file mode 100644 index 0000000..7934404 --- /dev/null +++ b/UI/Input/OpeningHours.ts @@ -0,0 +1,209 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {Utils} from "../../Utils"; + +export interface OpeningHour { + weekdayStart: number, // 0 is monday, 1 is tuesday, ... + weekdayEnd: number, + startHour: number, + startMinutes: number, + endHour: number, + endMinutes: number +} + +export default class OpeningHours extends InputElement { + public readonly IsSelected: UIEventSource; + + public static readonly days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; + + private readonly source: UIEventSource; + + constructor(source: UIEventSource = undefined) { + super(); + this.source = source ?? new UIEventSource([]); + this.IsSelected = new UIEventSource(false); + } + + InnerRender(): string { + let rows = ""; + for (let h = 0; h < 24; h++) { + let hs = "" + h; + if (hs.length == 1) { + hs = "0" + hs; + } + for (let m = 0; m < 60; m += 60) { + let min = "" + m; + const style = "width:0.5em;font-size:small;"; + if (m === 0) { + min = "00"; + } + rows += `${hs}:${min}` + + Utils.Times('', 7) + + '' + + Utils.Times('', 7) + + '' + + Utils.Times('', 7) + + '' + + Utils.Times('', 7) + + ''; + } + } + let days = OpeningHours.days.join(""); + return `${rows}
${days}
`; + } + + protected InnerUpdate() { + const self = this; + const table = (document.getElementById(`oh-table-${this.id}`) as HTMLTableElement); + if (table === undefined || table === null) { + return; + } + + let mouseIsDown = false; + let selectionStart: [number, number] = undefined; + let selectionEnd: [number, number] = undefined; + + function h(timeSegment: number) { + return Math.floor(timeSegment / 4); + } + + function m(timeSegment: number) { + return (timeSegment % 4) * 15; + } + + function hhmm(timeSegment: number) { + return h(timeSegment) + ":" + m(timeSegment) + } + + + function startSelection(i: number, j: number, cell: HTMLElement) { + mouseIsDown = true; + selectionStart = [i, j]; + selectionEnd = [i, j]; + cell.classList.add("oh-timecell-selected") + } + + function endSelection() { + if (selectionStart === undefined) { + return; + } + mouseIsDown = false + const dStart = Math.min(selectionStart[1], selectionEnd[1]); + const dEnd = Math.max(selectionStart[1], selectionEnd[1]); + const timeStart = Math.min(selectionStart[0], selectionEnd[0]) - 1; + const timeEnd = Math.max(selectionStart[0], selectionEnd[0]) - 1; + console.log("Selected from day", OpeningHours.days[dStart], "at", + hhmm(timeStart), "till", OpeningHours.days[dEnd], "at", hhmm(timeEnd + 1) + ) + const oh: OpeningHour = { + weekdayStart: dStart, + weekdayEnd: dEnd, + startHour: h(timeStart), + startMinutes: m(timeStart), + endHour: h(timeEnd + 1), + endMinutes: m(timeEnd + 1) + } + self.source.data.push(oh); + self.source.ping(); + } + + table.onmouseup = () => { + endSelection(); + }; + table.onmouseleave = () => { + endSelection(); + }; + + function selectAllBetween(iEnd, jEnd) { + let iStart = selectionStart[0]; + let jStart = selectionStart[1]; + + if (iStart > iEnd) { + const h = iStart; + iStart = iEnd; + iEnd = h; + } + if (jStart > jEnd) { + const h = jStart; + jStart = jEnd; + jEnd = h; + } + + for (let i = 1; i < table.rows.length; i++) { + let row = table.rows[i] + for (let j = 0; j < row.cells.length; j++) { + let cell = row.cells[j] + let offset = 0; + if (i % 4 == 1) { + if (j == 0) { + continue; + } + offset = -1; + } + if (iStart <= i && i <= iEnd && + jStart <= j + offset && j + offset <= jEnd) { + cell.classList.add("oh-timecell-selected") + } else { + cell.classList.remove("oh-timecell-selected") + } + + } + + } + } + + for (let i = 1; i < table.rows.length; i++) { + let row = table.rows[i] + for (let j = 0; j < row.cells.length; j++) { + let cell = row.cells[j] + let offset = 0; + if (i % 4 == 1) { + if (j == 0) { + continue; + } + offset = -1; + } + + + cell.onmousedown = (ev) => { + ev.preventDefault(); + startSelection(i, j + offset, cell) + } + cell.ontouchstart = (ev) => { + ev.preventDefault(); + startSelection(i, j + offset, cell) + } + cell.onmouseenter = () => { + if (mouseIsDown) { + selectionEnd = [i, j + offset]; + selectAllBetween(i, j + offset) + } + } + + cell.ontouchmove = (ev) => { + ev.preventDefault(); + selectionEnd = [i, j + offset]; + selectAllBetween(i, j + offset) + } + + cell.ontouchend = (ev) => { + ev.preventDefault(); + selectionEnd = [i, j + offset]; + selectAllBetween(i, j + offset) + } + } + + } + + + } + + IsValid(t: OpeningHour[]): boolean { + return true; + } + + GetValue(): UIEventSource { + return this.source; + } + +} \ No newline at end of file diff --git a/Utils.ts b/Utils.ts index 01e7d41..13335ec 100644 --- a/Utils.ts +++ b/Utils.ts @@ -2,6 +2,15 @@ import {UIElement} from "./UI/UIElement"; export class Utils { + + static EncodeXmlValue(str) { + return str.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + } + /** * Gives a clean float, or undefined if parsing fails * @param str @@ -16,9 +25,17 @@ export class Utils { } return undefined; } - - public static Upper(str : string){ - return str.substr(0,1).toUpperCase() + str.substr(1); + + public static Upper(str: string) { + return str.substr(0, 1).toUpperCase() + str.substr(1); + } + + public static Times(str: string, count: number): string { + let res = ""; + for (let i = 0; i < count; i++) { + res += str; + } + return res; } static DoEvery(millis: number, f: (() => void)) { diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index bc7a64f..330f599 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -13,11 +13,11 @@ "nl": "Een Witte Fiets of Spookfiets is een aandenken aan een fietser die bij een verkeersongeval om het leven kwam. Het gaat om een fiets die volledig wit is geschilderd en in de buurt van het ongeval werd geinstalleerd.

Op deze kaart zie je alle witte fietsen die door OpenStreetMap gekend zijn. Ontbreekt er een Witte Fiets of wens je informatie aan te passen? Meld je dan aan met een (gratis) OpenStreetMap account.", "de": "Ein Geisterrad ist ein Denkmal für einen Radfahrer, der bei einem Verkehrsunfall ums Leben kam, in Form eines weißen Fahrrades, das dauerhaft in der Nähe des Unfallortes aufgestellt ist.

Auf dieser Karte kann man alle Geisterräder sehen, die OpenStreetMap kennt. Fehlt ein Geisterrad? Jeder kann hier Informationen hinzufügen oder aktualisieren - Sie benötigen lediglich einen (kostenlosen) OpenStreetMap-Account." }, - "icon": "./assets/layers/ghost_bike/ghost_bike.svg", + "icon": "./assets/themes/ghost_bike/logo.svg", "startZoom": 1, "startLat": 0, "startLon": 0, "widenFactor": 0.1, "layers": ["ghost_bike"], "defaultBackgroundId": "CartoDB.Positron" -} \ No newline at end of file +} diff --git a/assets/themes/ghostbikes/logo.svg b/assets/themes/ghostbikes/logo.svg new file mode 100644 index 0000000..6751efa --- /dev/null +++ b/assets/themes/ghostbikes/logo.svg @@ -0,0 +1,102 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/css/openinghourstable.css b/css/openinghourstable.css new file mode 100644 index 0000000..549dbf1 --- /dev/null +++ b/css/openinghourstable.css @@ -0,0 +1,43 @@ + +.oh-table { + width: 15em; + border-collapse: collapse; +} + + +.oh-table td { + vertical-align: top; +} + +.oh-timecell:hover { + background-color: lightsalmon !important; +} + +.oh-timecell { + background-color: white; +} + +.oh-timecell-selected { + background-color: red; +} + +.oh-timecell-half { + border-top: 0.5px solid #eee +} + +.oh-timecell-half.oh-timecell-selected { + border-top: 0.5px solid lightsalmon; +} + +.oh-timecell-full { + border-top: 1px solid #aaa +} + +.oh-timecell-full.oh-timecell-selected { + border-top: 1px solid lightsalmon; +} + +.oh-left-col { + /*border-top: 1px solid #aaa;*/ +} + diff --git a/index.css b/index.css index c6eb9c3..36a5aa4 100644 --- a/index.css +++ b/index.css @@ -71,7 +71,6 @@ body { .invalid { box-shadow: 0 0 10px #ff5353; - display: block; height: min-content; } diff --git a/index.html b/index.html index f125b55..d353b31 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ + @@ -63,8 +64,7 @@ - + diff --git a/test.html b/test.html index a84c24f..4d66097 100644 --- a/test.html +++ b/test.html @@ -5,6 +5,7 @@ +