Further work on infobox, styling everything, removing clutter
This commit is contained in:
parent
2acd53d150
commit
0b4016b65d
48 changed files with 1283 additions and 454 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
||||||
dist/*
|
dist/*
|
||||||
node_modules
|
node_modules
|
||||||
|
.cache/*
|
||||||
|
.idea/*
|
||||||
|
|
34
Helpers.ts
34
Helpers.ts
|
@ -1,25 +1,23 @@
|
||||||
import {OsmConnection} from "./Logic/OsmConnection";
|
import {OsmConnection} from "./Logic/OsmConnection";
|
||||||
import {Changes} from "./Logic/Changes";
|
import {Changes} from "./Logic/Changes";
|
||||||
import {UIEventSource} from "./UI/UIEventSource";
|
import {UIEventSource} from "./UI/UIEventSource";
|
||||||
import {PendingChanges} from "./UI/PendingChanges";
|
|
||||||
|
|
||||||
export class Helpers {
|
export class Helpers {
|
||||||
|
|
||||||
|
|
||||||
static SetupAutoSave(changes: Changes, secondsTillChangesAreSaved : UIEventSource<number>) {
|
static SetupAutoSave(changes: Changes, millisTillChangesAreSaved : UIEventSource<number>, saveAfterXMillis : number) {
|
||||||
|
|
||||||
// This little function triggers the actual upload:
|
// This little function triggers the actual upload:
|
||||||
// Either when more then three answers are selected, or when no new answer has been added for the last 20s
|
// Either when more then three answers are selected, or when no new answer has been added for the last 20s
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.decreaseTime = function () {
|
window.decreaseTime = function () {
|
||||||
var time = secondsTillChangesAreSaved.data;
|
var time = millisTillChangesAreSaved.data;
|
||||||
if (time <= 0) {
|
if (time <= 0) {
|
||||||
if (changes._pendingChanges.length > 0) {
|
if (changes.pendingChangesES.data > 0) {
|
||||||
changes.uploadAll(undefined);
|
changes.uploadAll(undefined);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
secondsTillChangesAreSaved.setData(time - 1000);
|
millisTillChangesAreSaved.setData(time - 1000);
|
||||||
|
|
||||||
}
|
}
|
||||||
window.setTimeout('decreaseTime()', 1000);
|
window.setTimeout('decreaseTime()', 1000);
|
||||||
};
|
};
|
||||||
|
@ -27,15 +25,15 @@ export class Helpers {
|
||||||
|
|
||||||
changes.pendingChangesES.addCallback(function () {
|
changes.pendingChangesES.addCallback(function () {
|
||||||
|
|
||||||
var c = changes._pendingChanges.length;
|
var c = changes.pendingChangesES.data;
|
||||||
if (c > 10) {
|
if (c > 10) {
|
||||||
secondsTillChangesAreSaved.setData(0);
|
millisTillChangesAreSaved.setData(0);
|
||||||
changes.uploadAll(undefined);
|
changes.uploadAll(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
secondsTillChangesAreSaved.setData(5000);
|
millisTillChangesAreSaved.setData(saveAfterXMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -44,22 +42,6 @@ export class Helpers {
|
||||||
window.decreaseTime(); // The timer keeps running...
|
window.decreaseTime(); // The timer keeps running...
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate
|
|
||||||
* @param osmConnection
|
|
||||||
*/
|
|
||||||
static registerActivateOsmAUthenticationClass(osmConnection: OsmConnection) {
|
|
||||||
|
|
||||||
const authElements = document.getElementsByClassName("activate-osm-authentication");
|
|
||||||
for (let i = 0; i < authElements.length; i++) {
|
|
||||||
let element = authElements.item(i);
|
|
||||||
// @ts-ignore
|
|
||||||
element.onclick = function () {
|
|
||||||
osmConnection.AttemptLogin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -72,7 +54,7 @@ export class Helpers {
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function (e) {
|
window.addEventListener("beforeunload", function (e) {
|
||||||
// Quickly save everyting!
|
// Quickly save everyting!
|
||||||
if (changes._pendingChanges.length == 0) {
|
if (changes.pendingChangesES.data == 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,53 +29,20 @@ export class LayerDefinition {
|
||||||
|
|
||||||
style: (tags: any) => any;
|
style: (tags: any) => any;
|
||||||
|
|
||||||
removeContainedElements : boolean = false;
|
removeContainedElements: boolean = false;
|
||||||
removeTouchingElements: boolean = false;
|
removeTouchingElements: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
asLayer(basemap: Basemap, allElements: ElementStorage, changes: Changes, userDetails: UserDetails):
|
asLayer(basemap: Basemap, allElements: ElementStorage, changes: Changes, userDetails: UIEventSource<UserDetails>, selectedElement: UIEventSource<any>):
|
||||||
FilteredLayer {
|
FilteredLayer {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
function generateInfoBox(tagsES: UIEventSource<any>) {
|
|
||||||
|
|
||||||
var infoboxes: UIElement[] = [];
|
|
||||||
for (const uiElement of self.elementsToShow) {
|
|
||||||
if (uiElement instanceof QuestionDefinition) {
|
|
||||||
const questionDef = uiElement as QuestionDefinition;
|
|
||||||
const question = new Question(changes, questionDef);
|
|
||||||
infoboxes.push(question.CreateHtml(tagsES));
|
|
||||||
} else if (uiElement instanceof TagMappingOptions) {
|
|
||||||
const tagMappingOpt = uiElement as TagMappingOptions;
|
|
||||||
infoboxes.push(new TagMapping(tagMappingOpt, tagsES))
|
|
||||||
} else {
|
|
||||||
const ui = uiElement as UIElement;
|
|
||||||
infoboxes.push(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
infoboxes.push(new ImageCarousel(tagsES));
|
|
||||||
|
|
||||||
infoboxes.push(new FixedUiElement("<div style='width:750px'></div>"));
|
|
||||||
|
|
||||||
infoboxes.push(new OsmImageUploadHandler(
|
|
||||||
tagsES, userDetails, changes
|
|
||||||
).getUI());
|
|
||||||
|
|
||||||
const qbox = new QuestionPicker(changes.asQuestions(self.questions), tagsES);
|
|
||||||
infoboxes.push(qbox);
|
|
||||||
|
|
||||||
return new VerticalCombine(infoboxes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FilteredLayer(
|
return new FilteredLayer(
|
||||||
this.name,
|
this.name,
|
||||||
basemap, allElements, changes,
|
basemap, allElements, changes,
|
||||||
this.overpassFilter,
|
this.overpassFilter,
|
||||||
this.removeContainedElements, this.removeTouchingElements,
|
this.removeContainedElements, this.removeTouchingElements,
|
||||||
generateInfoBox,
|
this.style,
|
||||||
this.style);
|
selectedElement);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,9 +58,6 @@ export class Bookcases extends LayerDefinition {
|
||||||
new TagMappingOptions({key: "ref", template: "Referentienummer {ref}"}),
|
new TagMappingOptions({key: "ref", template: "Referentienummer {ref}"}),
|
||||||
|
|
||||||
new TagMappingOptions({key: "description", template: "Extra beschrijving: <br /> <p>{description}</p>"}),
|
new TagMappingOptions({key: "description", template: "Extra beschrijving: <br /> <p>{description}</p>"}),
|
||||||
|
|
||||||
CommonTagMappings.osmLink
|
|
||||||
|
|
||||||
]
|
]
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,14 +32,12 @@ export class Bos extends LayerDefinition {
|
||||||
this.elementsToShow = [
|
this.elementsToShow = [
|
||||||
new TagMappingOptions({
|
new TagMappingOptions({
|
||||||
key: "name",
|
key: "name",
|
||||||
template: "<h2>{name}</h2>",
|
template: "{name}",
|
||||||
missing: "<h2>Naamloos bos</h2>"
|
missing: "Naamloos bos"
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CommonTagMappings.access,
|
CommonTagMappings.access,
|
||||||
CommonTagMappings.operator,
|
CommonTagMappings.operator,
|
||||||
CommonTagMappings.osmLink
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {TagMappingOptions} from "../UI/TagMapping";
|
import {TagMappingOptions} from "../UI/TagMapping";
|
||||||
|
import {Img} from "../UI/Img";
|
||||||
|
|
||||||
|
|
||||||
export class CommonTagMappings {
|
export class CommonTagMappings {
|
||||||
|
@ -28,6 +29,8 @@ export class CommonTagMappings {
|
||||||
mapping: {
|
mapping: {
|
||||||
"node/-1": "<span class='osmlink'>Over enkele momenten sturen we je punt naar OpenStreetMap</span>"
|
"node/-1": "<span class='osmlink'>Over enkele momenten sturen we je punt naar OpenStreetMap</span>"
|
||||||
},
|
},
|
||||||
template: "<span class='osmlink'><a href='https://osm.org/{id}'> Op OSM</a></span>"
|
template: "<span class='osmlink'><a href='https://osm.org/{id}' target='_blank'>" +
|
||||||
|
Img.osmAbstractLogo +
|
||||||
|
"</a></span>"
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -49,7 +49,7 @@ export class KnownSet {
|
||||||
|
|
||||||
static groen = new KnownSet("groen",
|
static groen = new KnownSet("groen",
|
||||||
"Buurtnatuur",
|
"Buurtnatuur",
|
||||||
[new NatureReserves(), new Park(), new Bos(), new Playground()],
|
[new NatureReserves(), new Park(), new Bos()],
|
||||||
14,
|
14,
|
||||||
51.2,
|
51.2,
|
||||||
3.2,
|
3.2,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {Quests} from "../Quests";
|
||||||
import {TagMappingOptions} from "../UI/TagMapping";
|
import {TagMappingOptions} from "../UI/TagMapping";
|
||||||
import L from "leaflet"
|
import L from "leaflet"
|
||||||
import {CommonTagMappings} from "./CommonTagMappings";
|
import {CommonTagMappings} from "./CommonTagMappings";
|
||||||
import {Tag} from "../Logic/TagsFilter";
|
import {Or, Tag} from "../Logic/TagsFilter";
|
||||||
|
|
||||||
export class NatureReserves extends LayerDefinition {
|
export class NatureReserves extends LayerDefinition {
|
||||||
|
|
||||||
|
@ -11,7 +11,8 @@ export class NatureReserves extends LayerDefinition {
|
||||||
super();
|
super();
|
||||||
this.name = "natuurgebied";
|
this.name = "natuurgebied";
|
||||||
this.icon = "./assets/tree_white_background.svg";
|
this.icon = "./assets/tree_white_background.svg";
|
||||||
this.overpassFilter = new Tag("leisure", "nature_reserve");
|
this.overpassFilter =
|
||||||
|
new Or([new Tag("leisure", "nature_reserve"), new Tag("boundary","protected_area")]);
|
||||||
this.removeTouchingElements = true;
|
this.removeTouchingElements = true;
|
||||||
|
|
||||||
this.newElementTags = [new Tag("leisure", "nature_reserve"),
|
this.newElementTags = [new Tag("leisure", "nature_reserve"),
|
||||||
|
@ -22,12 +23,11 @@ export class NatureReserves extends LayerDefinition {
|
||||||
this.elementsToShow = [
|
this.elementsToShow = [
|
||||||
new TagMappingOptions({
|
new TagMappingOptions({
|
||||||
key: "name",
|
key: "name",
|
||||||
template: "<h2>{name}</h2>",
|
template: "{name}",
|
||||||
missing: "<h2>Naamloos gebied</h2>"
|
missing: "Naamloos gebied"
|
||||||
}),
|
}),
|
||||||
CommonTagMappings.access,
|
CommonTagMappings.access,
|
||||||
CommonTagMappings.operator,
|
CommonTagMappings.operator,
|
||||||
CommonTagMappings.osmLink
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,12 @@ export class Park extends LayerDefinition {
|
||||||
this.elementsToShow = [
|
this.elementsToShow = [
|
||||||
new TagMappingOptions({
|
new TagMappingOptions({
|
||||||
key: "name",
|
key: "name",
|
||||||
template: "<h2>{name}</h2>",
|
template: "{name}",
|
||||||
missing: "<h2>Naamloos park</h2>"
|
missing: "Naamloos park"
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CommonTagMappings.access,
|
CommonTagMappings.access,
|
||||||
CommonTagMappings.operator,
|
CommonTagMappings.operator,
|
||||||
CommonTagMappings.osmLink,
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,9 +82,6 @@ export class Toilets extends LayerDefinition{
|
||||||
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
CommonTagMappings.osmLink
|
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,21 +58,36 @@ export class Basemap {
|
||||||
|
|
||||||
|
|
||||||
constructor(leafletElementId: string, location: UIEventSource<{ zoom: number, lat: number, lon: number }>) {
|
constructor(leafletElementId: string, location: UIEventSource<{ zoom: number, lat: number, lon: number }>) {
|
||||||
this. map = L.map(leafletElementId, {
|
this.map = L.map(leafletElementId, {
|
||||||
center: [location.data.lat, location.data.lon],
|
center: [location.data.lat, location.data.lon],
|
||||||
zoom: location.data.zoom,
|
zoom: location.data.zoom,
|
||||||
layers: [this.osmLayer]
|
layers: [this.osmLayer],
|
||||||
|
attributionControl: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
L.control.attribution({
|
||||||
|
position: 'bottomleft'
|
||||||
|
}).addTo(this.map);
|
||||||
|
|
||||||
this.Location = location;
|
this.Location = location;
|
||||||
L.control.layers(this.baseLayers).addTo(this.map);
|
const layerControl = L.control.layers(this.baseLayers, null,
|
||||||
|
{
|
||||||
|
position: 'bottomleft',
|
||||||
|
hideSingleBase: true
|
||||||
|
})
|
||||||
|
layerControl.addTo(this.map);
|
||||||
|
|
||||||
this.map.zoomControl.setPosition("bottomleft");
|
this.map.zoomControl.setPosition("bottomleft");
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
this.map.on("moveend", function () {
|
this.map.on("moveend", function () {
|
||||||
location.data.zoom = self.map.getZoom();
|
location.data.zoom = self.map.getZoom();
|
||||||
location.data.lat = self.map.getCenter().lat;
|
location.data.lat = self.map.getCenter().lat;
|
||||||
location.data.lon = self.map.getCenter().lon;
|
location.data.lon = self.map.getCenter().lon;
|
||||||
location.ping();
|
location.ping();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,23 @@ export class Changes {
|
||||||
private readonly login: OsmConnection;
|
private readonly login: OsmConnection;
|
||||||
public readonly _allElements: ElementStorage;
|
public readonly _allElements: ElementStorage;
|
||||||
|
|
||||||
public _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
|
private _pendingChanges: { elementId: string, key: string, value: string }[] = []; // Gets reset on uploadAll
|
||||||
private newElements: OsmObject[] = []; // Gets reset on uploadAll
|
private newElements: OsmObject[] = []; // Gets reset on uploadAll
|
||||||
|
|
||||||
public readonly pendingChangesES = new UIEventSource(this._pendingChanges);
|
public readonly pendingChangesES = new UIEventSource<number>(this._pendingChanges.length);
|
||||||
private readonly centerMessage: UIEventSource<string>;
|
public readonly isSaving = new UIEventSource(false);
|
||||||
|
private readonly _changesetComment: string;
|
||||||
|
private readonly _centerMessage: UIEventSource<string>;
|
||||||
|
|
||||||
constructor(login: OsmConnection, allElements: ElementStorage, centerMessage: UIEventSource<string>) {
|
constructor(
|
||||||
|
changesetComment: string,
|
||||||
|
login: OsmConnection,
|
||||||
|
allElements: ElementStorage,
|
||||||
|
centerMessage: UIEventSource<string>) {
|
||||||
|
this._changesetComment = changesetComment;
|
||||||
this.login = login;
|
this.login = login;
|
||||||
this._allElements = allElements;
|
this._allElements = allElements;
|
||||||
this.centerMessage = centerMessage;
|
this._centerMessage = centerMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,15 +44,15 @@ export class Changes {
|
||||||
addChange(elementId: string, key: string, value: string) {
|
addChange(elementId: string, key: string, value: string) {
|
||||||
|
|
||||||
if (!this.login.userDetails.data.loggedIn) {
|
if (!this.login.userDetails.data.loggedIn) {
|
||||||
this.centerMessage.setData(
|
this._centerMessage.setData(
|
||||||
"<p>Bedankt voor je antwoord!</p>" +
|
"<p>Bedankt voor je antwoord!</p>" +
|
||||||
"<p>Gelieve <span class='activate-osm-authentication'>in te loggen op OpenStreetMap</span> om dit op te slaan.</p>"+
|
"<p>Gelieve <span class='activate-osm-authentication'>in te loggen op OpenStreetMap</span> om dit op te slaan.</p>" +
|
||||||
"<p>Nog geen account? <a href=\'https://www.openstreetmap.org/user/new\' target=\'_blank\'>Registreer hier</a></p>"
|
"<p>Nog geen account? <a href=\'https://www.openstreetmap.org/user/new\' target=\'_blank\'>Registreer hier</a></p>"
|
||||||
);
|
);
|
||||||
const self = this;
|
const self = this;
|
||||||
this.login.userDetails.addCallback(() => {
|
this.login.userDetails.addCallback(() => {
|
||||||
if (self.login.userDetails.data.loggedIn) {
|
if (self.login.userDetails.data.loggedIn) {
|
||||||
self.centerMessage.setData("");
|
self._centerMessage.setData("");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -67,7 +74,7 @@ export class Changes {
|
||||||
eventSource.ping();
|
eventSource.ping();
|
||||||
// We get the id from the event source, as that ID might be rewritten
|
// 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._pendingChanges.push({elementId: eventSource.data.id, key: key, value: value});
|
||||||
this.pendingChangesES.ping();
|
this.pendingChangesES.setData(this._pendingChanges.length);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -114,9 +121,18 @@ export class Changes {
|
||||||
public uploadAll(optionalContinuation: (() => void)) {
|
public uploadAll(optionalContinuation: (() => void)) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
this.isSaving.setData(true);
|
||||||
|
const optionalContinuationWrapped = function () {
|
||||||
|
self.isSaving.setData(false);
|
||||||
|
if (optionalContinuation) {
|
||||||
|
optionalContinuation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const pending: { elementId: string; key: string; value: string }[] = this._pendingChanges;
|
const pending: { elementId: string; key: string; value: string }[] = this._pendingChanges;
|
||||||
this._pendingChanges = [];
|
this._pendingChanges = [];
|
||||||
this.pendingChangesES.setData(this._pendingChanges);
|
this.pendingChangesES.setData(this._pendingChanges.length);
|
||||||
|
|
||||||
const newElements = this.newElements;
|
const newElements = this.newElements;
|
||||||
this.newElements = [];
|
this.newElements = [];
|
||||||
|
@ -203,7 +219,7 @@ export class Changes {
|
||||||
|
|
||||||
console.log("Beginning upload...");
|
console.log("Beginning upload...");
|
||||||
// At last, we build the changeset and upload
|
// At last, we build the changeset and upload
|
||||||
self.login.UploadChangeset("Updaten van metadata met Mapcomplete",
|
self.login.UploadChangeset(self._changesetComment,
|
||||||
function (csId) {
|
function (csId) {
|
||||||
|
|
||||||
let modifications = "";
|
let modifications = "";
|
||||||
|
@ -243,7 +259,7 @@ export class Changes {
|
||||||
return changes;
|
return changes;
|
||||||
},
|
},
|
||||||
handleMapping,
|
handleMapping,
|
||||||
optionalContinuation);
|
optionalContinuationWrapped);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,6 @@ export class FilteredLayer {
|
||||||
private readonly _removeContainedElements;
|
private readonly _removeContainedElements;
|
||||||
private readonly _removeTouchingElements;
|
private readonly _removeTouchingElements;
|
||||||
|
|
||||||
private readonly _popupContent: ((source: UIEventSource<any>) => UIElement);
|
|
||||||
|
|
||||||
private readonly _style: (properties) => any;
|
private readonly _style: (properties) => any;
|
||||||
|
|
||||||
private readonly _storage: ElementStorage;
|
private readonly _storage: ElementStorage;
|
||||||
|
@ -41,6 +39,7 @@ export class FilteredLayer {
|
||||||
* The leaflet layer object which should be removed on rerendering
|
* The leaflet layer object which should be removed on rerendering
|
||||||
*/
|
*/
|
||||||
private _geolayer;
|
private _geolayer;
|
||||||
|
private _selectedElement: UIEventSource<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -49,8 +48,9 @@ export class FilteredLayer {
|
||||||
filters: TagsFilter,
|
filters: TagsFilter,
|
||||||
removeContainedElements: boolean,
|
removeContainedElements: boolean,
|
||||||
removeTouchingElements: boolean,
|
removeTouchingElements: boolean,
|
||||||
popupContent: ((source: UIEventSource<any>) => UIElement),
|
style: ((properties) => any),
|
||||||
style: ((properties) => any)) {
|
selectedElement: UIEventSource<any>) {
|
||||||
|
this._selectedElement = selectedElement;
|
||||||
|
|
||||||
if (style === undefined) {
|
if (style === undefined) {
|
||||||
style = function () {
|
style = function () {
|
||||||
|
@ -60,7 +60,6 @@ export class FilteredLayer {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this._map = map;
|
this._map = map;
|
||||||
this.filters = filters;
|
this.filters = filters;
|
||||||
this._popupContent = popupContent;
|
|
||||||
this._style = style;
|
this._style = style;
|
||||||
this._storage = storage;
|
this._storage = storage;
|
||||||
this._removeContainedElements = removeContainedElements;
|
this._removeContainedElements = removeContainedElements;
|
||||||
|
@ -167,8 +166,6 @@ export class FilteredLayer {
|
||||||
},
|
},
|
||||||
|
|
||||||
pointToLayer: function (feature, latLng) {
|
pointToLayer: function (feature, latLng) {
|
||||||
|
|
||||||
const eventSource = self._storage.addOrGetElement(feature);
|
|
||||||
const style = self._style(feature.properties);
|
const style = self._style(feature.properties);
|
||||||
let marker;
|
let marker;
|
||||||
if (style.icon === undefined) {
|
if (style.icon === undefined) {
|
||||||
|
@ -180,19 +177,6 @@ export class FilteredLayer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
eventSource.addCallback(function () {
|
|
||||||
self.updateStyle();
|
|
||||||
});
|
|
||||||
const content = self._popupContent(eventSource)
|
|
||||||
marker.bindPopup(
|
|
||||||
"<div class='popupcontent'>" +
|
|
||||||
content.Render() +
|
|
||||||
"</div>"
|
|
||||||
).on("popupopen", function () {
|
|
||||||
content.Activate();
|
|
||||||
content.Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
return marker;
|
return marker;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -203,14 +187,9 @@ export class FilteredLayer {
|
||||||
eventSource.addCallback(function () {
|
eventSource.addCallback(function () {
|
||||||
self.updateStyle();
|
self.updateStyle();
|
||||||
});
|
});
|
||||||
const content = self._popupContent(eventSource)
|
layer.on("click", function(){
|
||||||
layer.bindPopup(
|
console.log("Selected ",feature)
|
||||||
"<div class='popupcontent'>" +
|
self._selectedElement.setData(feature.properties);
|
||||||
content.Render() +
|
|
||||||
"</div>"
|
|
||||||
).on("popupopen", function () {
|
|
||||||
content.Activate();
|
|
||||||
content.Update();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,7 +34,9 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
||||||
self.AddImage(wd.image);
|
self.AddImage(wd.image);
|
||||||
Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => {
|
Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => {
|
||||||
for (const image of images.images) {
|
for (const image of images.images) {
|
||||||
self.AddImage(image.filename);
|
if (image.startsWith("File:")) {
|
||||||
|
self.AddImage(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -48,7 +50,10 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
||||||
if (commons.startsWith("Category:")) {
|
if (commons.startsWith("Category:")) {
|
||||||
Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => {
|
Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => {
|
||||||
for (const image of images.images) {
|
for (const image of images.images) {
|
||||||
self.AddImage(image.filename);
|
// @ts-ignore
|
||||||
|
if (image.startsWith("File:")) {
|
||||||
|
self.AddImage(image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else { // @ts-ignore
|
} else { // @ts-ignore
|
||||||
|
@ -125,7 +130,7 @@ export class ImageSearcher extends UIEventSource<string[]> {
|
||||||
const urlSource = new UIEventSource<string>(url);
|
const urlSource = new UIEventSource<string>(url);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (url.startsWith("File:")) {
|
if (url.startsWith("File:")) {
|
||||||
return new WikimediaImage(urlSource);
|
return new WikimediaImage(urlSource.data);
|
||||||
} else {
|
} else {
|
||||||
return new SimpleImageElement(urlSource);
|
return new SimpleImageElement(urlSource);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,13 @@ export class LayerUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleFail(reason: any) {
|
private handleFail(reason: any) {
|
||||||
this.runningQuery.setData(false);
|
|
||||||
console.log("QUERY FAILED", reason);
|
console.log("QUERY FAILED", reason);
|
||||||
// TODO
|
console.log("Retrying in 1s")
|
||||||
|
this.previousBounds = undefined;
|
||||||
|
const self = this;
|
||||||
|
window.setTimeout(
|
||||||
|
function(){self.update()}, 1000
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +93,7 @@ export class LayerUpdater {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildBboxFor(): string {
|
private buildBboxFor(): string {
|
||||||
const b = this._map.map.getBounds();
|
const b = this._map.map.getBounds();
|
||||||
const latDiff = Math.abs(b.getNorth() - b.getSouth());
|
const latDiff = Math.abs(b.getNorth() - b.getSouth());
|
||||||
const lonDiff = Math.abs(b.getEast() - b.getWest());
|
const lonDiff = Math.abs(b.getEast() - b.getWest());
|
||||||
|
@ -101,8 +105,7 @@ export class LayerUpdater {
|
||||||
|
|
||||||
this.previousBounds = {north: n, east: e, south: s, west: w};
|
this.previousBounds = {north: n, east: e, south: s, west: w};
|
||||||
|
|
||||||
const bbox = "[bbox:" + s + "," + w + "," + n + "," + e + "]";
|
return "[bbox:" + s + "," + w + "," + n + "," + e + "]";
|
||||||
return bbox;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IsInBounds(): boolean {
|
private IsInBounds(): boolean {
|
||||||
|
|
|
@ -9,6 +9,9 @@ export class UserDetails {
|
||||||
public csCount = 0;
|
public csCount = 0;
|
||||||
public img: string;
|
public img: string;
|
||||||
public unreadMessages = 0;
|
public unreadMessages = 0;
|
||||||
|
public totalMessages = 0;
|
||||||
|
public osmConnection : OsmConnection;
|
||||||
|
public dryRun : boolean;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +29,9 @@ export class OsmConnection {
|
||||||
|
|
||||||
constructor(dryRun: boolean) {
|
constructor(dryRun: boolean) {
|
||||||
this.userDetails = new UIEventSource<UserDetails>(new UserDetails());
|
this.userDetails = new UIEventSource<UserDetails>(new UserDetails());
|
||||||
|
this.userDetails.data.osmConnection = this;
|
||||||
|
this.userDetails.data.dryRun = dryRun;
|
||||||
this._dryRun = dryRun;
|
this._dryRun = dryRun;
|
||||||
if(dryRun){
|
|
||||||
alert("Opgelet: testmode actief. Wijzigingen worden NIET opgeslaan")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.auth.authenticated()) {
|
if (this.auth.authenticated()) {
|
||||||
this.AttemptLogin(); // Also updates the user badge
|
this.AttemptLogin(); // Also updates the user badge
|
||||||
|
@ -61,23 +63,43 @@ export class OsmConnection {
|
||||||
self.userDetails.ping();
|
self.userDetails.ping();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(details == null){
|
if (details == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// details is an XML DOM of user details
|
// details is an XML DOM of user details
|
||||||
let userInfo = details.getElementsByTagName("user")[0];
|
let userInfo = details.getElementsByTagName("user")[0];
|
||||||
|
|
||||||
|
// let moreDetails = new DOMParser().parseFromString(userInfo.innerHTML, "text/xml");
|
||||||
|
|
||||||
let data = self.userDetails.data;
|
let data = self.userDetails.data;
|
||||||
data.loggedIn = true;
|
data.loggedIn = true;
|
||||||
console.log(userInfo);
|
console.log(userInfo);
|
||||||
data.name = userInfo.getAttribute('display_name');
|
data.name = userInfo.getAttribute('display_name');
|
||||||
data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count");
|
data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count");
|
||||||
data.img = userInfo.getElementsByTagName("img")[0].getAttribute("href");
|
data.img = userInfo.getElementsByTagName("img")[0].getAttribute("href");
|
||||||
data.unreadMessages = userInfo.getElementsByTagName("received")[0].getAttribute("unread");
|
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
|
||||||
|
data.unreadMessages = parseInt(messages.getAttribute("unread"));
|
||||||
|
data.totalMessages = parseInt(messages.getAttribute("count"));
|
||||||
self.userDetails.ping();
|
self.userDetails.ping();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate
|
||||||
|
* @param osmConnection
|
||||||
|
*/
|
||||||
|
registerActivateOsmAUthenticationClass() {
|
||||||
|
|
||||||
|
const authElements = document.getElementsByClassName("activate-osm-authentication");
|
||||||
|
for (let i = 0; i < authElements.length; i++) {
|
||||||
|
let element = authElements.item(i);
|
||||||
|
// @ts-ignore
|
||||||
|
element.onclick = function () {
|
||||||
|
this.AttemptLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static parseUploadChangesetResponse(response: XMLDocument) {
|
private static parseUploadChangesetResponse(response: XMLDocument) {
|
||||||
const nodes = response.getElementsByTagName("node");
|
const nodes = response.getElementsByTagName("node");
|
||||||
const mapping = {};
|
const mapping = {};
|
||||||
|
@ -102,6 +124,7 @@ export class OsmConnection {
|
||||||
console.log("NOT UPLOADING as dryrun is true");
|
console.log("NOT UPLOADING as dryrun is true");
|
||||||
var changesetXML = generateChangeXML("123456");
|
var changesetXML = generateChangeXML("123456");
|
||||||
console.log(changesetXML);
|
console.log(changesetXML);
|
||||||
|
continuation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,29 +9,26 @@ import {UserDetails} from "./OsmConnection";
|
||||||
export class OsmImageUploadHandler {
|
export class OsmImageUploadHandler {
|
||||||
private _tags: UIEventSource<any>;
|
private _tags: UIEventSource<any>;
|
||||||
private _changeHandler: Changes;
|
private _changeHandler: Changes;
|
||||||
private _userdetails: UserDetails;
|
private _userdetails: UIEventSource<UserDetails>;
|
||||||
|
|
||||||
constructor(tags: UIEventSource<any>,
|
constructor(tags: UIEventSource<any>,
|
||||||
userdetails: UserDetails,
|
userdetails: UIEventSource<UserDetails>,
|
||||||
changeHandler: Changes
|
changeHandler: Changes
|
||||||
) {
|
) {
|
||||||
if (tags === undefined || userdetails === undefined || changeHandler === undefined) {
|
if (tags === undefined || userdetails === undefined || changeHandler === undefined) {
|
||||||
throw "Something is undefined"
|
throw "Something is undefined"
|
||||||
}
|
}
|
||||||
console.log(tags, changeHandler, userdetails)
|
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
this._changeHandler = changeHandler;
|
this._changeHandler = changeHandler;
|
||||||
this._userdetails = userdetails;
|
this._userdetails = userdetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateOptions(license: string) {
|
private generateOptions(license: string) {
|
||||||
console.log(this)
|
|
||||||
console.log(this._tags, this._changeHandler, this._userdetails)
|
|
||||||
const tags = this._tags.data;
|
const tags = this._tags.data;
|
||||||
|
|
||||||
const title = tags.name ?? "Unknown area";
|
const title = tags.name ?? "Unknown area";
|
||||||
const description = [
|
const description = [
|
||||||
"author:" + this._userdetails.name,
|
"author:" + this._userdetails.data.name,
|
||||||
"license:" + license,
|
"license:" + license,
|
||||||
"wikidata:" + tags.wikidata,
|
"wikidata:" + tags.wikidata,
|
||||||
"osmid:" + tags.id,
|
"osmid:" + tags.id,
|
||||||
|
@ -60,7 +57,9 @@ export class OsmImageUploadHandler {
|
||||||
|
|
||||||
getUI(): ImageUploadFlow {
|
getUI(): ImageUploadFlow {
|
||||||
const self = this;
|
const self = this;
|
||||||
return new ImageUploadFlow(function (license) {
|
return new ImageUploadFlow(
|
||||||
|
this._userdetails,
|
||||||
|
function (license) {
|
||||||
return self.generateOptions(license)
|
return self.generateOptions(license)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,8 +26,7 @@ export class Overpass {
|
||||||
const query =
|
const query =
|
||||||
'[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;';
|
'[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;';
|
||||||
console.log(query);
|
console.log(query);
|
||||||
const url = "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query);
|
return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query);
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ export class QuestionUI extends UIElement {
|
||||||
const embeddedScriptSave = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", false )';
|
const embeddedScriptSave = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", false )';
|
||||||
const embeddedScriptSkip = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", true )';
|
const embeddedScriptSkip = 'questionAnswered(' + this._qid + ', "' + this._tags.data.id + '", true )';
|
||||||
const saveButton = "<input class='save-button' type='button' onclick='" + embeddedScriptSave + "' value='Opslaan' />";
|
const saveButton = "<input class='save-button' type='button' onclick='" + embeddedScriptSave + "' value='Opslaan' />";
|
||||||
const skip = "<input class='skip-button' type='button' onclick='" + embeddedScriptSkip + "' value='Ik ben het niet zeker (vraag overslaan)' />";
|
const skip = "<input class='skip-button' type='button' onclick='" + embeddedScriptSkip + "' value='Ik ben niet zeker (vraag overslaan)' />";
|
||||||
return q.question + "<br/> " + answers + saveButton + skip;
|
return q.question + "<br/> " + answers + saveButton + skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,7 @@ export class Wikimedia {
|
||||||
|
|
||||||
for (const member of members) {
|
for (const member of members) {
|
||||||
|
|
||||||
imageOverview.images.push(
|
imageOverview.images.push(member.title);
|
||||||
{filename: member.title, fileid: member.pageid});
|
|
||||||
}
|
}
|
||||||
if (response.continue === undefined || alreadyLoaded > 30) {
|
if (response.continue === undefined || alreadyLoaded > 30) {
|
||||||
handleCategory(imageOverview);
|
handleCategory(imageOverview);
|
||||||
|
@ -96,7 +95,10 @@ export class Wikimedia {
|
||||||
wd.commonsWiki = commons?.title;
|
wd.commonsWiki = commons?.title;
|
||||||
|
|
||||||
// P18 is the claim 'depicted in this image'
|
// P18 is the claim 'depicted in this image'
|
||||||
wd.image = "File:" + entity.claims.P18?.[0]?.mainsnak?.datavalue?.value;
|
const image = entity.claims.P18?.[0]?.mainsnak?.datavalue?.value;
|
||||||
|
if (image) {
|
||||||
|
wd.image = "File:" + image;
|
||||||
|
}
|
||||||
handleWikidata(wd);
|
handleWikidata(wd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -114,7 +116,7 @@ export class Wikidata {
|
||||||
|
|
||||||
export class ImagesInCategory {
|
export class ImagesInCategory {
|
||||||
// Filenames of relevant images
|
// Filenames of relevant images
|
||||||
images: { filename: string, fileid: number }[] = [];
|
images: string[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LicenseInfo {
|
export class LicenseInfo {
|
||||||
|
|
|
@ -54,7 +54,8 @@ export class Quests {
|
||||||
).addUnrequiredTag("seamark:type", "restricted_area");
|
).addUnrequiredTag("seamark:type", "restricted_area");
|
||||||
|
|
||||||
static nameOf(name: string) : QuestionDefinition {
|
static nameOf(name: string) : QuestionDefinition {
|
||||||
return QuestionDefinition.noNameOrNameQuestion("Wat is de naam van dit " + name + "?",
|
return QuestionDefinition.noNameOrNameQuestion("<b>Wat is de <i>officiële</i> naam van dit " + name + "?</b><br />" +
|
||||||
|
"Gelieve geen naam uit te vinden",
|
||||||
"Dit " + name + " heeft geen naam", 20);
|
"Dit " + name + " heeft geen naam", 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ export class CenterMessageBox extends UIElement {
|
||||||
if (this._centermessage.data != "") {
|
if (this._centermessage.data != "") {
|
||||||
pstyle.opacity = "1";
|
pstyle.opacity = "1";
|
||||||
pstyle.pointerEvents = "all";
|
pstyle.pointerEvents = "all";
|
||||||
Helpers.registerActivateOsmAUthenticationClass(this._osmConnection);
|
this._osmConnection.registerActivateOsmAUthenticationClass();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pstyle.pointerEvents = "none";
|
pstyle.pointerEvents = "none";
|
||||||
|
|
115
UI/FeatureInfoBox.ts
Normal file
115
UI/FeatureInfoBox.ts
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import {UIElement} from "./UIElement";
|
||||||
|
import {TagMapping, TagMappingOptions} from "./TagMapping";
|
||||||
|
import {Question, QuestionDefinition} from "../Logic/Question";
|
||||||
|
import {UIEventSource} from "./UIEventSource";
|
||||||
|
import {VerticalCombine} from "./VerticalCombine";
|
||||||
|
import {QuestionPicker} from "./QuestionPicker";
|
||||||
|
import {OsmImageUploadHandler} from "../Logic/OsmImageUploadHandler";
|
||||||
|
import {ImageCarousel} from "./Image/ImageCarousel";
|
||||||
|
import {Changes} from "../Logic/Changes";
|
||||||
|
import {UserDetails} from "../Logic/OsmConnection";
|
||||||
|
import {Img} from "./Img";
|
||||||
|
import {CommonTagMappings} from "../Layers/CommonTagMappings";
|
||||||
|
import {Tag} from "../Logic/TagsFilter";
|
||||||
|
|
||||||
|
export class FeatureInfoBox extends UIElement {
|
||||||
|
|
||||||
|
private _tagsES: UIEventSource<any>;
|
||||||
|
|
||||||
|
|
||||||
|
private _title: UIElement;
|
||||||
|
private _osmLink: UIElement;
|
||||||
|
private _infoElements: UIElement[]
|
||||||
|
|
||||||
|
|
||||||
|
private _questions: QuestionPicker;
|
||||||
|
|
||||||
|
private _changes: Changes;
|
||||||
|
private _userDetails: UIEventSource<UserDetails>;
|
||||||
|
private _imageElement: ImageCarousel;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
tagsES: UIEventSource<any>,
|
||||||
|
elementsToShow: (TagMappingOptions | QuestionDefinition | UIElement)[],
|
||||||
|
questions: QuestionDefinition[],
|
||||||
|
changes: Changes,
|
||||||
|
userDetails: UIEventSource<UserDetails>,
|
||||||
|
) {
|
||||||
|
super(tagsES);
|
||||||
|
this._tagsES = tagsES;
|
||||||
|
this._changes = changes;
|
||||||
|
this._userDetails = userDetails;
|
||||||
|
|
||||||
|
this._imageElement = new ImageCarousel(this._tagsES);
|
||||||
|
|
||||||
|
this._questions = new QuestionPicker(
|
||||||
|
this._changes.asQuestions(questions), this._tagsES);
|
||||||
|
|
||||||
|
var infoboxes: UIElement[] = [];
|
||||||
|
for (const uiElement of elementsToShow) {
|
||||||
|
if (uiElement instanceof QuestionDefinition) {
|
||||||
|
const questionDef = uiElement as QuestionDefinition;
|
||||||
|
const question = new Question(this._changes, questionDef);
|
||||||
|
infoboxes.push(question.CreateHtml(this._tagsES));
|
||||||
|
} else if (uiElement instanceof TagMappingOptions) {
|
||||||
|
const tagMappingOpt = uiElement as TagMappingOptions;
|
||||||
|
infoboxes.push(new TagMapping(tagMappingOpt, this._tagsES))
|
||||||
|
} else {
|
||||||
|
const ui = uiElement as UIElement;
|
||||||
|
infoboxes.push(ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this._title = infoboxes.shift();
|
||||||
|
this._infoElements = infoboxes;
|
||||||
|
|
||||||
|
this._osmLink = new TagMapping(CommonTagMappings.osmLink, this._tagsES);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
InnerRender(): string {
|
||||||
|
|
||||||
|
|
||||||
|
return "<div class='featureinfobox'>" +
|
||||||
|
"<div class='featureinfoboxtitle'>" +
|
||||||
|
"<span>" + this._title.Render() + "</span>" +
|
||||||
|
this._osmLink.Render() +
|
||||||
|
"</div>" +
|
||||||
|
|
||||||
|
"<div class='infoboxcontents'>" +
|
||||||
|
|
||||||
|
this._imageElement.Render() +
|
||||||
|
|
||||||
|
new VerticalCombine(this._infoElements).Render() +
|
||||||
|
" <span class='infobox-questions'>" +
|
||||||
|
this._questions.Render() +
|
||||||
|
" </span>" +
|
||||||
|
"</div>" +
|
||||||
|
"" +
|
||||||
|
"</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
Activate() {
|
||||||
|
super.Activate();
|
||||||
|
this._imageElement.Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Update() {
|
||||||
|
super.Update();
|
||||||
|
this._imageElement.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateInfoBox() {
|
||||||
|
var infoboxes: UIElement[] = [];
|
||||||
|
|
||||||
|
infoboxes.push(new OsmImageUploadHandler(
|
||||||
|
this._tagsES, this._userDetails, this._changes
|
||||||
|
).getUI());
|
||||||
|
|
||||||
|
|
||||||
|
return new VerticalCombine(infoboxes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ export class ImageCarousel extends UIElement {
|
||||||
this.slideshow = new SlideShow(
|
this.slideshow = new SlideShow(
|
||||||
new FixedUiElement("<b>Afbeeldingen</b>"),
|
new FixedUiElement("<b>Afbeeldingen</b>"),
|
||||||
uiElements,
|
uiElements,
|
||||||
new FixedUiElement("<i>Geen afbeeldingen gevonden</i>"));
|
new FixedUiElement("")).HideOnEmpty(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerRender(): string {
|
InnerRender(): string {
|
||||||
|
|
|
@ -1,36 +1,52 @@
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
import {UIElement} from "../UIElement";
|
import {UIElement} from "../UIElement";
|
||||||
import {SimpleImageElement} from "./SimpleImageElement";
|
|
||||||
import {LicenseInfo, Wikimedia} from "../../Logic/Wikimedia";
|
import {LicenseInfo, Wikimedia} from "../../Logic/Wikimedia";
|
||||||
|
|
||||||
|
|
||||||
export class WikimediaImage extends UIElement {
|
export class WikimediaImage extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
|
static allLicenseInfos: any = {};
|
||||||
private _imageMeta: UIEventSource<LicenseInfo>;
|
private _imageMeta: UIEventSource<LicenseInfo>;
|
||||||
|
private _imageLocation : string;
|
||||||
|
|
||||||
|
constructor(source: string) {
|
||||||
|
super(undefined)
|
||||||
|
this._imageLocation = source;
|
||||||
|
if (WikimediaImage.allLicenseInfos[source] !== undefined) {
|
||||||
|
this._imageMeta = WikimediaImage.allLicenseInfos[source];
|
||||||
|
} else {
|
||||||
|
this._imageMeta = new UIEventSource<LicenseInfo>(new LicenseInfo());
|
||||||
|
WikimediaImage.allLicenseInfos[source] = this._imageMeta;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(source: UIEventSource<string>) {
|
this.ListenTo(this._imageMeta);
|
||||||
super(source)
|
|
||||||
const meta = new UIEventSource<LicenseInfo>(new LicenseInfo());
|
const self = this;
|
||||||
this.ListenTo(meta);
|
Wikimedia.LicenseData(source, (info) => {
|
||||||
this._imageMeta = meta;
|
self._imageMeta.setData(info);
|
||||||
this._source.addCallback(() => {
|
|
||||||
Wikimedia.LicenseData(source.data, (info) => {
|
|
||||||
meta.setData(info);
|
|
||||||
})
|
})
|
||||||
});
|
|
||||||
this._source.ping();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InnerRender(): string {
|
protected InnerRender(): string {
|
||||||
let url = Wikimedia.ImageNameToUrl(this._source.data);
|
let url = Wikimedia.ImageNameToUrl(this._imageLocation, 500, 400);
|
||||||
url = url.replace(/'/g, '%27');
|
url = url.replace(/'/g, '%27');
|
||||||
return "<div class='imgWithAttr'><img class='attributedImage' src='" + url + "' " +
|
|
||||||
"alt='" + this._imageMeta.data.description + "' >" +
|
const wikimediaLink =
|
||||||
"<br /><span class='attribution'>" +
|
"<a href='https://commons.wikimedia.org/wiki/" + this._imageLocation + "' target='_blank'>" +
|
||||||
"<a href='https://commons.wikimedia.org/wiki/"+this._source.data+"' target='_blank'><b>" + (this._source.data) + "</b></a> <br />" +
|
"<img class='wikimedia-link' src='./assets/wikimedia-commons-white.svg' alt='Wikimedia Commons Logo'/>" +
|
||||||
(this._imageMeta.data.artist ?? "Unknown artist") + " " + (this._imageMeta.data.licenseShortName ?? "") +
|
"</a> ";
|
||||||
"</span>" +
|
|
||||||
|
const attribution =
|
||||||
|
"<span class='attribution-author'>" + (this._imageMeta.data.artist ?? "") + "</span>" + " <span class='license'>" + (this._imageMeta.data.licenseShortName ?? "") + "</span>";
|
||||||
|
const image = "<img src='" + url + "' " + "alt='" + this._imageMeta.data.description + "' >";
|
||||||
|
|
||||||
|
return "<div class='imgWithAttr'>" +
|
||||||
|
image +
|
||||||
|
"<div class='attribution'>" +
|
||||||
|
wikimediaLink +
|
||||||
|
attribution +
|
||||||
|
"</div>" +
|
||||||
"</div>";
|
"</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,18 @@ import {UIRadioButton} from "./UIRadioButton";
|
||||||
import {VariableUiElement} from "./VariableUIElement";
|
import {VariableUiElement} from "./VariableUIElement";
|
||||||
import $ from "jquery"
|
import $ from "jquery"
|
||||||
import {Imgur} from "../Logic/Imgur";
|
import {Imgur} from "../Logic/Imgur";
|
||||||
|
import {UserDetails} from "../Logic/OsmConnection";
|
||||||
|
|
||||||
export class ImageUploadFlow extends UIElement {
|
export class ImageUploadFlow extends UIElement {
|
||||||
private _licensePicker: UIRadioButton;
|
private _licensePicker: UIRadioButton;
|
||||||
private _licenseExplanation: UIElement;
|
private _licenseExplanation: UIElement;
|
||||||
private _isUploading: UIEventSource<number> = new UIEventSource<number>(0)
|
private _isUploading: UIEventSource<number> = new UIEventSource<number>(0)
|
||||||
private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) };
|
private _uploadOptions: (license: string) => { title: string; description: string; handleURL: (url: string) => void; allDone: (() => void) };
|
||||||
|
private _userdetails: UIEventSource<UserDetails>;
|
||||||
|
|
||||||
constructor(uploadOptions: ((license: string) =>
|
constructor(
|
||||||
|
userInfo: UIEventSource<UserDetails>,
|
||||||
|
uploadOptions: ((license: string) =>
|
||||||
{
|
{
|
||||||
title: string,
|
title: string,
|
||||||
description: string,
|
description: string,
|
||||||
|
@ -20,6 +24,8 @@ export class ImageUploadFlow extends UIElement {
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
super(undefined);
|
super(undefined);
|
||||||
|
this._userdetails = userInfo;
|
||||||
|
this.ListenTo(userInfo);
|
||||||
this._uploadOptions = uploadOptions;
|
this._uploadOptions = uploadOptions;
|
||||||
this.ListenTo(this._isUploading);
|
this.ListenTo(this._isUploading);
|
||||||
this._licensePicker = UIRadioButton.FromStrings(
|
this._licensePicker = UIRadioButton.FromStrings(
|
||||||
|
@ -49,6 +55,10 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
protected InnerRender(): string {
|
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</div>";
|
||||||
|
}
|
||||||
|
|
||||||
if (this._isUploading.data > 0) {
|
if (this._isUploading.data > 0) {
|
||||||
return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>"
|
return "<b>Bezig met uploaden, nog " + this._isUploading.data + " foto's te gaan...</b>"
|
||||||
}
|
}
|
||||||
|
@ -63,6 +73,13 @@ export class ImageUploadFlow extends UIElement {
|
||||||
|
|
||||||
InnerUpdate(htmlElement: HTMLElement) {
|
InnerUpdate(htmlElement: HTMLElement) {
|
||||||
super.InnerUpdate(htmlElement);
|
super.InnerUpdate(htmlElement);
|
||||||
|
const user = this._userdetails.data;
|
||||||
|
if(!user.loggedIn){
|
||||||
|
htmlElement.onclick = function(){
|
||||||
|
user.osmConnection.AttemptLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._licensePicker.Update();
|
this._licensePicker.Update();
|
||||||
const selector = document.getElementById('fileselector-' + this.id);
|
const selector = document.getElementById('fileselector-' + this.id);
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
14
UI/Img.ts
Normal file
14
UI/Img.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export class Img {
|
||||||
|
|
||||||
|
static osmAbstractLogo: string =
|
||||||
|
"<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>";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import {UIElement} from "./UIElement";
|
|
||||||
import {UserDetails} from "../Logic/OsmConnection";
|
|
||||||
import {UIEventSource} from "./UIEventSource";
|
|
||||||
|
|
||||||
export class LoginDependendMessage extends UIElement {
|
|
||||||
private _noLoginMsg: string;
|
|
||||||
private _loginMsg: string;
|
|
||||||
private _userDetails: UserDetails;
|
|
||||||
|
|
||||||
constructor(loginData: UIEventSource<UserDetails>,
|
|
||||||
noLoginMsg: string,
|
|
||||||
loginMsg: string) {
|
|
||||||
super(loginData);
|
|
||||||
this._userDetails = loginData.data;
|
|
||||||
this._noLoginMsg = noLoginMsg;
|
|
||||||
this._loginMsg = loginMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InnerRender(): string {
|
|
||||||
if (this._userDetails.loggedIn) {
|
|
||||||
return this._loginMsg;
|
|
||||||
} else {
|
|
||||||
return this._noLoginMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InnerUpdate(htmlElement: HTMLElement) {
|
|
||||||
// pass
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
58
UI/MessageBoxHandler.ts
Normal file
58
UI/MessageBoxHandler.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Keeps 'messagebox' and 'messageboxmobile' in sync, shows a 'close' button on the latter one
|
||||||
|
*/
|
||||||
|
import {UIEventSource} from "./UIEventSource";
|
||||||
|
import {UIElement} from "./UIElement";
|
||||||
|
import {FixedUiElement} from "./FixedUiElement";
|
||||||
|
import {VariableUiElement} from "./VariableUIElement";
|
||||||
|
|
||||||
|
export class MessageBoxHandler {
|
||||||
|
private _uielement: UIEventSource<() => UIElement>;
|
||||||
|
|
||||||
|
constructor(uielement: UIEventSource<() => UIElement>,
|
||||||
|
onClear: (() => void)) {
|
||||||
|
this._uielement = uielement;
|
||||||
|
this.listenTo(uielement);
|
||||||
|
this.update();
|
||||||
|
|
||||||
|
new VariableUiElement(new UIEventSource<string>("<h2>Naar de kaart > </h2>"),
|
||||||
|
(htmlElement) => {
|
||||||
|
htmlElement.onclick = function () {
|
||||||
|
uielement.setData(undefined);
|
||||||
|
onClear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).AttachTo("to-the-map");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
listenTo(uiEventSource: UIEventSource<any>) {
|
||||||
|
const self = this;
|
||||||
|
uiEventSource.addCallback(function () {
|
||||||
|
self.update();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
const wrapper = document.getElementById("messagesboxmobilewrapper");
|
||||||
|
const gen = this._uielement.data;
|
||||||
|
console.log("Generator: ", gen);
|
||||||
|
if (gen === undefined) {
|
||||||
|
wrapper.classList.add("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wrapper.classList.remove("hidden");
|
||||||
|
gen()
|
||||||
|
?.HideOnEmpty(true)
|
||||||
|
?.AttachTo("messagesbox")
|
||||||
|
?.Activate();
|
||||||
|
|
||||||
|
gen()
|
||||||
|
?.HideOnEmpty(true)
|
||||||
|
?.AttachTo("messagesboxmobile")
|
||||||
|
?.Activate();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,65 +1,37 @@
|
||||||
import {UIElement} from "./UIElement";
|
import {UIElement} from "./UIElement";
|
||||||
import {UIEventSource} from "./UIEventSource";
|
import {UIEventSource} from "./UIEventSource";
|
||||||
import {Changes} from "../Logic/Changes";
|
|
||||||
|
|
||||||
export class PendingChanges extends UIElement{
|
export class PendingChanges extends UIElement {
|
||||||
|
private _pendingChangesCount: UIEventSource<number>;
|
||||||
|
private _countdown: UIEventSource<number>;
|
||||||
|
private _isSaving: UIEventSource<boolean>;
|
||||||
|
|
||||||
private readonly changes;
|
constructor(pendingChangesCount: UIEventSource<number>,
|
||||||
|
countdown: UIEventSource<number>,
|
||||||
constructor(changes: Changes, countdown: UIEventSource<number>) {
|
isSaving: UIEventSource<boolean>) {
|
||||||
super(undefined); // We do everything manually here!
|
super(pendingChangesCount);
|
||||||
this.changes = changes;
|
this.ListenTo(isSaving);
|
||||||
|
this.ListenTo(countdown);
|
||||||
|
this._pendingChangesCount = pendingChangesCount;
|
||||||
countdown.addCallback(function () {
|
this._countdown = countdown;
|
||||||
|
this._isSaving = isSaving;
|
||||||
const percentage = Math.max(0, 100 * countdown.data / 20000);
|
|
||||||
|
|
||||||
let bar = document.getElementById("pending-bar");
|
|
||||||
if (bar === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const style = bar.style;
|
|
||||||
style.width = percentage + "%";
|
|
||||||
style["margin-left"] = (50 - (percentage / 2)) + "%";
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
changes.pendingChangesES.addCallback(function () {
|
|
||||||
const c = changes._pendingChanges.length;
|
|
||||||
const text = document.getElementById("pending-text");
|
|
||||||
if (c == 0) {
|
|
||||||
text.style.opacity = "0";
|
|
||||||
text.innerText = "Saving...";
|
|
||||||
} else {
|
|
||||||
text.innerText = c + " pending";
|
|
||||||
text.style.opacity = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const bar = document.getElementById("pending-bar");
|
|
||||||
|
|
||||||
if (bar === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (c == 0) {
|
|
||||||
bar.style.opacity = "0";
|
|
||||||
} else {
|
|
||||||
bar.style.opacity = "0.5";
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InnerRender(): string {
|
protected InnerRender(): string {
|
||||||
return "<div id='pending-bar' style='width:100%; margin-left:0%'></div>" +
|
if (this._isSaving.data) {
|
||||||
"<div id='pending-text'></div>";
|
return "<span class='alert'>Saving</span>";
|
||||||
|
}
|
||||||
|
if (this._pendingChangesCount.data == 0) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerUpdate(htmlElement: HTMLElement) {
|
var restingSeconds = this._countdown.data / 1000;
|
||||||
|
var dots = "";
|
||||||
|
while (restingSeconds > 0) {
|
||||||
|
dots += ".";
|
||||||
|
restingSeconds = restingSeconds - 1;
|
||||||
|
}
|
||||||
|
return "Saving "+this._pendingChangesCount.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@ export class QuestionPicker extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
if (highestQ === undefined) {
|
if (highestQ === undefined) {
|
||||||
return "De vragen zijn op!";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return highestQ.CreateHtml(this.source).Render();
|
return highestQ.CreateHtml(this.source).Render();
|
||||||
|
|
|
@ -6,7 +6,6 @@ export class SlideShow extends UIElement {
|
||||||
private readonly _embeddedElements: UIEventSource<UIElement[]>
|
private readonly _embeddedElements: UIEventSource<UIElement[]>
|
||||||
|
|
||||||
private readonly _currentSlide: UIEventSource<number> = new UIEventSource<number>(0);
|
private readonly _currentSlide: UIEventSource<number> = new UIEventSource<number>(0);
|
||||||
private readonly _title: UIElement;
|
|
||||||
private readonly _noimages: UIElement;
|
private readonly _noimages: UIElement;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -14,7 +13,6 @@ export class SlideShow extends UIElement {
|
||||||
embeddedElements: UIEventSource<UIElement[]>,
|
embeddedElements: UIEventSource<UIElement[]>,
|
||||||
noImages: UIElement) {
|
noImages: UIElement) {
|
||||||
super(embeddedElements);
|
super(embeddedElements);
|
||||||
this._title = title;
|
|
||||||
this._embeddedElements = embeddedElements;
|
this._embeddedElements = embeddedElements;
|
||||||
this.ListenTo(this._currentSlide);
|
this.ListenTo(this._currentSlide);
|
||||||
this._noimages = noImages;
|
this._noimages = noImages;
|
||||||
|
@ -24,30 +22,37 @@ export class SlideShow extends UIElement {
|
||||||
if (this._embeddedElements.data.length == 0) {
|
if (this._embeddedElements.data.length == 0) {
|
||||||
return this._noimages.Render();
|
return this._noimages.Render();
|
||||||
}
|
}
|
||||||
const prevBtn = "<input class='prev-button' type='button' onclick='console.log(\"prev\")' value='<' />"
|
|
||||||
const nextBtn = "<input class='next-button' type='button' onclick='console.log(\"nxt\")' value='>' />"
|
if (this._embeddedElements.data.length == 1) {
|
||||||
let header = this._title.Render();
|
return "<div class='image-slideshow'>"+this._embeddedElements.data[0].Render()+"</div>";
|
||||||
if (this._embeddedElements.data.length > 1) {
|
|
||||||
header = header + prevBtn + (this._currentSlide.data + 1) + "/" + this._embeddedElements.data.length + nextBtn;
|
|
||||||
}
|
}
|
||||||
let body = ""
|
|
||||||
|
const prevBtn = "<div class='prev-button' id='prevbtn-"+this.id+"'></div>"
|
||||||
|
const nextBtn = "<div class='next-button' id='nextbtn-"+this.id+"'></div>"
|
||||||
|
|
||||||
|
let slides = ""
|
||||||
for (let i = 0; i < this._embeddedElements.data.length; i++) {
|
for (let i = 0; i < this._embeddedElements.data.length; i++) {
|
||||||
let embeddedElement = this._embeddedElements.data[i];
|
let embeddedElement = this._embeddedElements.data[i];
|
||||||
let state = "hidden"
|
let state = "hidden"
|
||||||
if (this._currentSlide.data === i) {
|
if (this._currentSlide.data === i) {
|
||||||
state = "active-slide";
|
state = "active-slide";
|
||||||
}
|
}
|
||||||
body += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n";
|
slides += " <div class=\"slide " + state + "\">" + embeddedElement.Render() + "</div>\n";
|
||||||
}
|
}
|
||||||
return "<span class='image-slideshow'>" + header + body + "</span>";
|
return "<div class='image-slideshow'>"
|
||||||
|
+ prevBtn
|
||||||
|
+ "<div class='slides'>" + slides + "</div>"
|
||||||
|
+ nextBtn
|
||||||
|
+ "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerUpdate(htmlElement) {
|
InnerUpdate(htmlElement) {
|
||||||
const nextButton = htmlElement.getElementsByClassName('next-button')[0];
|
const nextButton = document.getElementById("nextbtn-"+this.id);
|
||||||
if(nextButton === undefined){
|
if(nextButton === undefined || nextButton === null){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const prevButton = htmlElement.getElementsByClassName('prev-button')[0];
|
|
||||||
|
const prevButton = document.getElementById("prevbtn-"+this.id);
|
||||||
const self = this;
|
const self = this;
|
||||||
nextButton.onclick = () => {
|
nextButton.onclick = () => {
|
||||||
const current = self._currentSlide.data;
|
const current = self._currentSlide.data;
|
||||||
|
|
|
@ -7,6 +7,8 @@ export abstract class UIElement {
|
||||||
public readonly id: string;
|
public readonly id: string;
|
||||||
public readonly _source: UIEventSource<any>;
|
public readonly _source: UIEventSource<any>;
|
||||||
|
|
||||||
|
private _hideIfEmpty = false;
|
||||||
|
|
||||||
protected constructor(source: UIEventSource<any>) {
|
protected constructor(source: UIEventSource<any>) {
|
||||||
this.id = "ui-element-" + UIElement.nextId;
|
this.id = "ui-element-" + UIElement.nextId;
|
||||||
this._source = source;
|
this._source = source;
|
||||||
|
@ -33,9 +35,23 @@ export abstract class UIElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
element.innerHTML = this.InnerRender();
|
element.innerHTML = this.InnerRender();
|
||||||
|
if(this._hideIfEmpty){
|
||||||
|
if(element.innerHTML === ""){
|
||||||
|
element.parentElement.style.display = "none";
|
||||||
|
}else{
|
||||||
|
element.parentElement.style.display = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.InnerUpdate(element);
|
this.InnerUpdate(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HideOnEmpty(hide : boolean){
|
||||||
|
this._hideIfEmpty = hide;
|
||||||
|
this.Update();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// Called after the HTML has been replaced. Can be used for css tricks
|
// Called after the HTML has been replaced. Can be used for css tricks
|
||||||
InnerUpdate(htmlElement : HTMLElement){}
|
InnerUpdate(htmlElement : HTMLElement){}
|
||||||
|
|
||||||
|
@ -45,6 +61,10 @@ export abstract class UIElement {
|
||||||
|
|
||||||
AttachTo(divId: string) {
|
AttachTo(divId: string) {
|
||||||
let element = document.getElementById(divId);
|
let element = document.getElementById(divId);
|
||||||
|
if(element === null){
|
||||||
|
console.log("SEVERE: could not attach UIElement to ", divId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
element.innerHTML = this.Render();
|
element.innerHTML = this.Render();
|
||||||
this.Update();
|
this.Update();
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -8,7 +8,7 @@ export class UIEventSource<T>{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public addCallback(callback: (() => void)) {
|
public addCallback(callback: ((latestData) => void)) {
|
||||||
this._callbacks.push(callback);
|
this._callbacks.push(callback);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,19 @@ export class UIEventSource<T>{
|
||||||
|
|
||||||
public ping(): void {
|
public ping(): void {
|
||||||
for (let i in this._callbacks) {
|
for (let i in this._callbacks) {
|
||||||
this._callbacks[i]();
|
this._callbacks[i](this.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public map<J>(f: ((T) => J)): UIEventSource<J> {
|
public map<J>(f: ((T) => J)): UIEventSource<J> {
|
||||||
const newSource = new UIEventSource<J>(
|
|
||||||
f(this.data)
|
|
||||||
);
|
|
||||||
const self = this;
|
const self = this;
|
||||||
this.addCallback(function () {
|
this.addCallback(function () {
|
||||||
newSource.setData(f(self.data));
|
newSource.setData(f(self.data));
|
||||||
|
newSource.ping();
|
||||||
});
|
});
|
||||||
|
const newSource = new UIEventSource<J>(
|
||||||
|
f(this.data)
|
||||||
|
);
|
||||||
|
|
||||||
return newSource;
|
return newSource;
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,14 @@ import {UIEventSource} from "./UIEventSource";
|
||||||
*/
|
*/
|
||||||
export class UserBadge extends UIElement {
|
export class UserBadge extends UIElement {
|
||||||
private _userDetails: UIEventSource<UserDetails>;
|
private _userDetails: UIEventSource<UserDetails>;
|
||||||
|
private _pendingChanges: UIElement;
|
||||||
|
|
||||||
|
|
||||||
constructor(userDetails: UIEventSource<UserDetails>) {
|
constructor(userDetails: UIEventSource<UserDetails>,
|
||||||
|
pendingChanges : UIElement) {
|
||||||
super(userDetails);
|
super(userDetails);
|
||||||
this._userDetails = userDetails;
|
this._userDetails = userDetails;
|
||||||
|
this._pendingChanges = pendingChanges;
|
||||||
|
|
||||||
userDetails.addCallback(function () {
|
userDetails.addCallback(function () {
|
||||||
const profilePic = document.getElementById("profile-pic");
|
const profilePic = document.getElementById("profile-pic");
|
||||||
|
@ -28,16 +31,49 @@ export class UserBadge extends UIElement {
|
||||||
return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>";
|
return "<div class='activate-osm-authentication'>Klik hier om aan te melden bij OSM</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let messageSpan = "<span id='messages'>" +
|
||||||
|
" <a href='https://www.openstreetmap.org/messages/inbox' target='_blank'><img class='envelope' src='./assets/envelope.svg'/>" +
|
||||||
|
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='envelope' src='./assets/envelope.svg'/>" +
|
||||||
|
" " +
|
||||||
|
"" +
|
||||||
|
user.unreadMessages.toString() +
|
||||||
|
"</a></span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
let dryrun = "";
|
||||||
|
if (user.dryRun) {
|
||||||
|
dryrun = " <span class='alert'>TESTING</span>";
|
||||||
|
}
|
||||||
|
|
||||||
return "<img id='profile-pic' src='" + user.img + "'/> " +
|
return "<img id='profile-pic' src='" + user.img + "'/> " +
|
||||||
"<div id='usertext'>"+
|
"<div id='usertext'>" +
|
||||||
"<div id='username'>" +
|
"<p id='username'>" +
|
||||||
"<a href='https://www.openstreetmap.org/user/"+user.name+"' target='_blank'>" + user.name + "</a></div> <br />" +
|
"<a href='https://www.openstreetmap.org/user/" + user.name + "' target='_blank'>" + user.name + "</a>" +
|
||||||
"<div id='csCount'> " +
|
dryrun +
|
||||||
" <a href='https://www.openstreetmap.org/user/"+user.name+"/history' target='_blank'><img class='star' src='./assets/star.svg'/>" + user.csCount + "</div></a>" +
|
"</p> " +
|
||||||
|
"<p id='userstats'>" +
|
||||||
|
messageSpan +
|
||||||
|
"<span id='csCount'> " +
|
||||||
|
" <a href='https://www.openstreetmap.org/user/" + user.name + "/history' target='_blank'><img class='star' src='./assets/star.svg'/> " + user.csCount +
|
||||||
|
"</a></span> " +
|
||||||
|
this._pendingChanges.Render() +
|
||||||
|
"</p>" +
|
||||||
|
|
||||||
"</div>";
|
"</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
InnerUpdate(htmlElement: HTMLElement) {
|
InnerUpdate(htmlElement: HTMLElement) {
|
||||||
|
this._pendingChanges.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Activate() {
|
||||||
|
this._pendingChanges.Activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,15 +3,25 @@ import {UIEventSource} from "./UIEventSource";
|
||||||
|
|
||||||
export class VariableUiElement extends UIElement {
|
export class VariableUiElement extends UIElement {
|
||||||
private _html: UIEventSource<string>;
|
private _html: UIEventSource<string>;
|
||||||
|
private _innerUpdate: (htmlElement: HTMLElement) => void;
|
||||||
|
|
||||||
constructor(html: UIEventSource<string>) {
|
constructor(html: UIEventSource<string>, innerUpdate : ((htmlElement : HTMLElement) => void) = undefined) {
|
||||||
super(html);
|
super(html);
|
||||||
this._html = html;
|
this._html = html;
|
||||||
|
this._innerUpdate = innerUpdate;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InnerRender(): string {
|
protected InnerRender(): string {
|
||||||
return this._html.data;
|
return this._html.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InnerUpdate(htmlElement: HTMLElement) {
|
||||||
|
super.InnerUpdate(htmlElement);
|
||||||
|
if(this._innerUpdate !== undefined){
|
||||||
|
this._innerUpdate(htmlElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
76
assets/arrow-left-smooth.svg
Normal file
76
assets/arrow-left-smooth.svg
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
viewBox="0 0 26.458333 26.458334"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="arrow-left-smooth.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="19.262262"
|
||||||
|
inkscape:cy="36.323203"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="13.229167,23.859748"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide815"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="14.944824,13.229167"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide817"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-270.54165)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 20.139011,294.16029 c 0,0 -13.7995299,-7.53922 -13.8484369,-10.36091 -0.04891,-2.82169 13.8484369,-10.38607 13.8484369,-10.38607"
|
||||||
|
id="path821"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
82
assets/arrow-right-both.svg
Normal file
82
assets/arrow-right-both.svg
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
viewBox="0 0 26.458333 26.458334"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
sodipodi:docname="arrow-right-both.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="19.262262"
|
||||||
|
inkscape:cy="36.323203"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="13.229167,23.859748"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide815"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="14.944824,13.229167"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide817"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-270.54165)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.89327955;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 6.741713,274.0738 9.596044,9.69249 -9.5719331,9.66812"
|
||||||
|
id="path819"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:3.96875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 31.419271,269.58152 c 0,0 13.197107,9.60301 13.243879,13.19711 0.04677,3.5941 -13.243879,13.22916 -13.243879,13.22916"
|
||||||
|
id="path821"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
77
assets/arrow-right-sharp.svg
Normal file
77
assets/arrow-right-sharp.svg
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
viewBox="0 0 26.458333 26.458334"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
sodipodi:docname="arrow-right-sharp.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="19.262262"
|
||||||
|
inkscape:cy="36.323203"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="13.229167,23.859748"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide815"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="14.944824,13.229167"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide817"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-270.54165)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2.89327955;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 6.741713,274.0738 9.596044,9.69249 -9.5719331,9.66812"
|
||||||
|
id="path819"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
76
assets/arrow-right-smooth.svg
Normal file
76
assets/arrow-right-smooth.svg
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="100"
|
||||||
|
height="100"
|
||||||
|
viewBox="0 0 26.458333 26.458334"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="arrow-right-smooth.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="-22.237738"
|
||||||
|
inkscape:cy="36.323203"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1001"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="13.229167,23.859748"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide815"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="14.944824,13.229167"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide817"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-270.54165)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:3.59588718;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 6.3128214,273.41335 c 0,0 13.7995296,7.53922 13.8484366,10.36091 0.04891,2.82169 -13.8484366,10.38607 -13.8484366,10.38607"
|
||||||
|
id="path821"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -577,7 +577,6 @@
|
||||||
],
|
],
|
||||||
"tags": {
|
"tags": {
|
||||||
"leisure": "park",
|
"leisure": "park",
|
||||||
"name": "Koning Albert I-park",
|
|
||||||
"name:etymology:wikidata": "Q12974",
|
"name:etymology:wikidata": "Q12974",
|
||||||
"surface": "wood",
|
"surface": "wood",
|
||||||
"wikidata": "Q54817470",
|
"wikidata": "Q54817470",
|
||||||
|
@ -634,7 +633,6 @@
|
||||||
],
|
],
|
||||||
"tags": {
|
"tags": {
|
||||||
"leisure": "park",
|
"leisure": "park",
|
||||||
"name": "Begijnvest",
|
|
||||||
"wikidata": "Q4499623",
|
"wikidata": "Q4499623",
|
||||||
"wikipedia": "nl:Begijnenvest (Brugge)"
|
"wikipedia": "nl:Begijnenvest (Brugge)"
|
||||||
}
|
}
|
||||||
|
@ -17215,7 +17213,6 @@
|
||||||
],
|
],
|
||||||
"tags": {
|
"tags": {
|
||||||
"leisure": "park",
|
"leisure": "park",
|
||||||
"name": "Begijnvest",
|
|
||||||
"type": "multipolygon",
|
"type": "multipolygon",
|
||||||
"wikidata": "Q4499623",
|
"wikidata": "Q4499623",
|
||||||
"wikipedia": "nl:Begijnenvest (Brugge)"
|
"wikipedia": "nl:Begijnenvest (Brugge)"
|
||||||
|
|
22
assets/wikimedia-commons-white.svg
Normal file
22
assets/wikimedia-commons-white.svg
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1024" height="1376" viewBox="-305 -516 610 820">
|
||||||
|
<title>Wikimedia Commons Logo</title>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="c"><circle r="298"/></clipPath>
|
||||||
|
</defs>
|
||||||
|
<circle r="100" fill="#fff"/>
|
||||||
|
<g fill="#fff">
|
||||||
|
<g id="arrow" clip-path="url(#c)">
|
||||||
|
<path d="m-11 180v118h22v-118"/>
|
||||||
|
<path d="m-43 185l43-75 43 75"/>
|
||||||
|
</g>
|
||||||
|
<g id="arrows3">
|
||||||
|
<use xlink:href="#arrow" transform="rotate(45)"/>
|
||||||
|
<use xlink:href="#arrow" transform="rotate(90)"/>
|
||||||
|
<use xlink:href="#arrow" transform="rotate(135)"/>
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#arrows3" transform="scale(-1 1)"/>
|
||||||
|
<path id="blue_path" transform="rotate(-45)" stroke="#fff" stroke-width="84" fill="none" d="M 0,-256 A 256 256 0 1 0 256,0 C 256,-100 155,-150 250,-275"/>
|
||||||
|
<path id="arrow_top" d="m-23-515s-36 135-80 185 116-62 170-5-90-180-90-180z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 933 B |
22
assets/wikimedia-commons.svg
Normal file
22
assets/wikimedia-commons.svg
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1024" height="1376" viewBox="-305 -516 610 820">
|
||||||
|
<title>Wikimedia Commons Logo</title>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="c"><circle r="298"/></clipPath>
|
||||||
|
</defs>
|
||||||
|
<circle r="100" fill="#900"/>
|
||||||
|
<g fill="#069">
|
||||||
|
<g id="arrow" clip-path="url(#c)">
|
||||||
|
<path d="m-11 180v118h22v-118"/>
|
||||||
|
<path d="m-43 185l43-75 43 75"/>
|
||||||
|
</g>
|
||||||
|
<g id="arrows3">
|
||||||
|
<use xlink:href="#arrow" transform="rotate(45)"/>
|
||||||
|
<use xlink:href="#arrow" transform="rotate(90)"/>
|
||||||
|
<use xlink:href="#arrow" transform="rotate(135)"/>
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#arrows3" transform="scale(-1 1)"/>
|
||||||
|
<path id="blue_path" transform="rotate(-45)" stroke="#069" stroke-width="84" fill="none" d="M 0,-256 A 256 256 0 1 0 256,0 C 256,-100 155,-150 250,-275"/>
|
||||||
|
<path id="arrow_top" d="m-23-515s-36 135-80 185 116-62 170-5-90-180-90-180z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 932 B |
335
index.css
335
index.css
|
@ -12,18 +12,59 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************** GENERIC ****************/
|
||||||
|
|
||||||
|
.uielement {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
background-color: #fee4d1;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 1em;
|
||||||
|
padding: 0.3em;
|
||||||
|
margin: 0.25em;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 0.15em;
|
||||||
|
padding-bottom: 0.15em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.activate-osm-authentication {
|
||||||
|
cursor: pointer;
|
||||||
|
color: blue;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************** USER BADGE ****************/
|
||||||
|
|
||||||
|
|
||||||
.star {
|
.star {
|
||||||
fill: black;
|
fill: black;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.envelope {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#profile-pic {
|
#profile-pic {
|
||||||
float: left;
|
float: left;
|
||||||
width: 4em;
|
width: 4em;
|
||||||
height: 4em;
|
height: 4em;
|
||||||
padding:0;
|
padding: 0;
|
||||||
margin:0;
|
margin: 0;
|
||||||
-webkit-border-radius: 50%;
|
-webkit-border-radius: 50%;
|
||||||
-moz-border-radius: 50%;
|
-moz-border-radius: 50%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
@ -31,11 +72,12 @@ body {
|
||||||
transition: opacity 500ms linear;
|
transition: opacity 500ms linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
#usertext a {
|
#username {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#usertext {
|
#usertext {
|
||||||
width: auto;
|
width: auto;
|
||||||
margin:0;
|
margin:0;
|
||||||
|
@ -52,79 +94,74 @@ body {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
line-height: 0.75em;
|
line-height: 0.75em;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#username {
|
#usertext a {
|
||||||
display: block ruby;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imgWithAttr {
|
#userbadge {
|
||||||
max-height: 20em;
|
display: inline-block;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#userbadge{
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
-webkit-border-radius: 2em;
|
-webkit-border-radius: 2em;
|
||||||
-moz-border-radius: 2em;
|
-moz-border-radius: 2em;
|
||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
transition: all 500ms linear;
|
transition: all 500ms linear;
|
||||||
|
margin: 1em;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
min-width: 20em;
|
||||||
|
pointer-events: all;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#authbox {
|
|
||||||
|
#userbadge p {
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#topleft-tools {
|
||||||
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding:0;
|
padding: 0;
|
||||||
z-index: 5000;
|
z-index: 5000;
|
||||||
transition: all 500ms linear;
|
transition: all 500ms linear;
|
||||||
max-width: 25%;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
}
|
|
||||||
|
|
||||||
#pendingchangesbox {
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pending-bar {
|
|
||||||
height: 1.2em;
|
|
||||||
margin: 0;
|
|
||||||
z-index: 5005;
|
|
||||||
background-color: white;
|
|
||||||
border-radius:1.2em;
|
|
||||||
padding: 0;
|
|
||||||
transition: all 1s linear;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#pending-text {
|
|
||||||
margin: 0;
|
|
||||||
margin-top: -1.2em;
|
|
||||||
z-index: 5000;
|
|
||||||
border-radius:1.2em;
|
|
||||||
height: 1.2em;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 1s linear;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#welcomeMessage {
|
||||||
|
display: inline-block;
|
||||||
|
width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
#zoomin {
|
|
||||||
|
|
||||||
|
#messagesboxmobilewrapper {
|
||||||
|
display: none; /*Only shown on small screens*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#messagesboxmobile {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 1.5em;
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 0;
|
||||||
|
height: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#messagesbox {
|
#messagesbox {
|
||||||
|
/*Only shown on big screens*/
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
padding-top: 1em;
|
padding-top: 1em;
|
||||||
padding-bottom: 1em;
|
padding-bottom: 1em;
|
||||||
|
@ -132,6 +169,33 @@ body {
|
||||||
transition: all 500ms linear;
|
transition: all 500ms linear;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
#messagesbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
#messagesboxmobilewrapper {
|
||||||
|
position: absolute;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
z-index: 5050;
|
||||||
|
transition: all 500ms linear;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
|
@ -202,41 +266,170 @@ body {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.uielement{
|
|
||||||
width:100%;
|
/************ Slideshow *****************/
|
||||||
height:100%;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
.image-slideshow {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activate-osm-authentication {
|
.slides {
|
||||||
cursor: pointer;
|
|
||||||
color: blue;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popupImg {
|
.prev-button {
|
||||||
max-height: 150px;
|
background-color: black;
|
||||||
max-width: 300px;
|
opacity: 30%;
|
||||||
|
width: 3.0em;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
|
||||||
|
z-index: 5060;
|
||||||
|
|
||||||
|
content:url(assets/arrow-left-smooth.svg);
|
||||||
|
|
||||||
|
border-bottom-left-radius: 1em;
|
||||||
|
border-top-left-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-slidehow{
|
|
||||||
|
.next-button {
|
||||||
|
background-color: black;
|
||||||
|
opacity: 30%;
|
||||||
|
width: 3.0em;
|
||||||
|
height: 100%;
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
border-bottom-right-radius: 1em;
|
||||||
|
border-top-right-radius: 1em;
|
||||||
|
|
||||||
|
|
||||||
|
z-index: 5060;
|
||||||
|
|
||||||
|
content:url(assets/arrow-right-smooth.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.slide > span > img {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
height: 250px;
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide {
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide img{
|
|
||||||
max-height: 200px;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
/* This is used by the slideshow, to hide non-active slides*/
|
||||||
|
display: none !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.osmlink{
|
|
||||||
font-size: xx-small;
|
.imgWithAttr {
|
||||||
|
max-height: 20em;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.wikimedia-link {
|
||||||
|
width: 1.5em;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribution {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: smaller;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 5em; /* Offset for the go left button*/
|
||||||
|
padding: 0.25em;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribution-author {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.license {
|
||||||
|
font-size: small;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attribution a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************** Info box (box containing features and questions ******************/
|
||||||
|
|
||||||
|
.featureinfobox {
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureinfoboxtitle {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.osmlink {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.osm-logo path {
|
||||||
|
fill: #80cf36;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.featureinfoboxtitle span {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featureinfoboxtitle a {
|
||||||
|
float: right;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.infoboxcontents {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.infboxcontents-left {
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infboxcontents-right {
|
||||||
|
padding: 0.1em;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infobox-questions {
|
||||||
|
max-width: 25em;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
24
index.html
24
index.html
|
@ -12,42 +12,34 @@
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="messagesboxmobilewrapper">
|
||||||
|
<div id="to-the-map">Return to map</div>
|
||||||
|
<div id="messagesboxmobile"> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="authbox">
|
<div id="topleft-tools">
|
||||||
<div id="userbadge">
|
<div id="userbadge">
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<div id="pendingchangesbox"></div>
|
|
||||||
<br/>
|
|
||||||
<div id="messagesbox">
|
<div id="messagesbox">
|
||||||
<div id="welcomeMessage"></div>
|
|
||||||
|
|
||||||
<div id="gettingStartedBox"></div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="centermessage"></div>
|
<div id="centermessage"></div>
|
||||||
<div id="bottomRight">ADD</div>
|
<div id="bottomRight" style="display: none">ADD</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="leafletDiv"></div>
|
<div id="leafletDiv"></div>
|
||||||
|
|
||||||
<script src="./index.ts"></script>
|
<script src="./index.ts"></script>
|
||||||
|
|
||||||
<!-- TODO -->
|
|
||||||
|
|
||||||
<!-- Fotos -->
|
|
||||||
<!-- KLeuren/icoontjes -->
|
|
||||||
<!-- Aanpassingen van tagmapping/tagmapping verwijderen -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 3 dagen eerste protoype -->
|
<!-- 3 dagen eerste protoype -->
|
||||||
<!-- 19 juni: eerste feedbackronde, foto's -->
|
<!-- 19 juni: eerste feedbackronde, foto's -->
|
||||||
<!-- 23 juni: wikimedia foto's laden -->
|
<!-- 23 juni: wikimedia foto's laden -->
|
||||||
<!-- 24 juni: foto's via imgur -->
|
<!-- 24 juni: foto's via imgur -->
|
||||||
|
|
||||||
|
<!-- 26 restylen infobox -->
|
||||||
|
|
||||||
<script data-goatcounter="https://pietervdvn.goatcounter.com/count"
|
<script data-goatcounter="https://pietervdvn.goatcounter.com/count"
|
||||||
async src="//gc.zgo.at/count.js"></script>
|
async src="//gc.zgo.at/count.js"></script>
|
||||||
|
|
95
index.ts
95
index.ts
|
@ -5,30 +5,34 @@ import {UIEventSource} from "./UI/UIEventSource";
|
||||||
import {UserBadge} from "./UI/UserBadge";
|
import {UserBadge} from "./UI/UserBadge";
|
||||||
import {Basemap} from "./Logic/Basemap";
|
import {Basemap} from "./Logic/Basemap";
|
||||||
import {PendingChanges} from "./UI/PendingChanges";
|
import {PendingChanges} from "./UI/PendingChanges";
|
||||||
import {FixedUiElement} from "./UI/FixedUiElement";
|
|
||||||
import {CenterMessageBox} from "./UI/CenterMessageBox";
|
import {CenterMessageBox} from "./UI/CenterMessageBox";
|
||||||
import {Helpers} from "./Helpers";
|
import {Helpers} from "./Helpers";
|
||||||
import {KnownSet} from "./Layers/KnownSet";
|
import {KnownSet} from "./Layers/KnownSet";
|
||||||
import {AddButton} from "./UI/AddButton";
|
import {Tag, TagsFilter, TagUtils} from "./Logic/TagsFilter";
|
||||||
import {Tag} from "./Logic/TagsFilter";
|
|
||||||
import {FilteredLayer} from "./Logic/FilteredLayer";
|
import {FilteredLayer} from "./Logic/FilteredLayer";
|
||||||
import {LayerUpdater} from "./Logic/LayerUpdater";
|
import {LayerUpdater} from "./Logic/LayerUpdater";
|
||||||
import {LoginDependendMessage} from "./UI/LoginDependendMessage";
|
import {VariableUiElement} from "./UI/VariableUIElement";
|
||||||
|
import {UIElement} from "./UI/UIElement";
|
||||||
|
import {MessageBoxHandler} from "./UI/MessageBoxHandler";
|
||||||
|
import {Overpass} from "./Logic/Overpass";
|
||||||
|
import {FixedUiElement} from "./UI/FixedUiElement";
|
||||||
|
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
||||||
|
|
||||||
let dryRun = false;
|
let dryRun = false;
|
||||||
|
|
||||||
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
|
||||||
|
|
||||||
// Set to true if testing and changes should NOT be saved
|
// Set to true if testing and changes should NOT be saved
|
||||||
// dryRun = true;
|
dryRun = true;
|
||||||
// If you have a testfile somewhere, enable this to spoof overpass
|
// 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
|
// 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/test.json";
|
Overpass.testUrl = "http://127.0.0.1:8080/test.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------- SELECT THE RIGHT QUESTSET -----------------
|
// ----------------- SELECT THE RIGHT QUESTSET -----------------
|
||||||
|
|
||||||
|
|
||||||
let questSetToRender = KnownSet.groen;
|
let questSetToRender = KnownSet.groen;
|
||||||
if (window.location.search) {
|
if (window.location.search) {
|
||||||
const params = window.location.search.substr(1).split("&");
|
const params = window.location.search.substr(1).split("&");
|
||||||
|
@ -56,7 +60,12 @@ 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
|
// 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 secondsTillChangesAreSaved = new UIEventSource<number>(0);
|
||||||
|
|
||||||
var locationControl = new UIEventSource({
|
const leftMessage = new UIEventSource<() => UIElement>(undefined);
|
||||||
|
|
||||||
|
const selectedElement = new UIEventSource<any>(undefined);
|
||||||
|
|
||||||
|
|
||||||
|
const locationControl = new UIEventSource({
|
||||||
zoom: questSetToRender.startzoom,
|
zoom: questSetToRender.startzoom,
|
||||||
lat: questSetToRender.startLat,
|
lat: questSetToRender.startLat,
|
||||||
lon: questSetToRender.startLon
|
lon: questSetToRender.startLon
|
||||||
|
@ -65,10 +74,12 @@ var locationControl = new UIEventSource({
|
||||||
|
|
||||||
// ----------------- Prepare the important objects -----------------
|
// ----------------- Prepare the important objects -----------------
|
||||||
|
|
||||||
|
const saveTimeout = 5000; // After this many milliseconds without changes, saves are sent of to OSM
|
||||||
const allElements = new ElementStorage();
|
const allElements = new ElementStorage();
|
||||||
const osmConnection = new OsmConnection(dryRun);
|
const osmConnection = new OsmConnection(dryRun);
|
||||||
const changes = new Changes(osmConnection, allElements, centerMessage);
|
const changes = new Changes(
|
||||||
|
"Beantwoorden van vragen met MapComplete voor vragenset #" + questSetToRender.name,
|
||||||
|
osmConnection, allElements, centerMessage);
|
||||||
const bm = new Basemap("leafletDiv", locationControl);
|
const bm = new Basemap("leafletDiv", locationControl);
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +97,7 @@ const flayers: FilteredLayer[] = []
|
||||||
|
|
||||||
for (const layer of questSetToRender.layers) {
|
for (const layer of questSetToRender.layers) {
|
||||||
|
|
||||||
const flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails.data);
|
const flayer = layer.asLayer(bm, allElements, changes, osmConnection.userDetails, selectedElement);
|
||||||
|
|
||||||
const addButton = {
|
const addButton = {
|
||||||
name: layer.name,
|
name: layer.name,
|
||||||
|
@ -103,23 +114,64 @@ const layerUpdater = new LayerUpdater(bm, questSetToRender.startzoom, flayers);
|
||||||
|
|
||||||
// ------------------ Setup various UI elements ------------
|
// ------------------ Setup various UI elements ------------
|
||||||
|
|
||||||
|
/*
|
||||||
const addButton = new AddButton(bm, changes, addButtons);
|
const addButton = new AddButton(bm, changes, addButtons);
|
||||||
addButton.AttachTo("bottomRight");
|
addButton.AttachTo("bottomRight");
|
||||||
addButton.Update();
|
addButton.Update();
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the questions and information for the selected element on the leftMessage
|
||||||
|
*/
|
||||||
|
selectedElement.addCallback((data) => {
|
||||||
|
console.log("Got selection");
|
||||||
|
// Which is the applicable set?
|
||||||
|
for (const layer of questSetToRender.layers) {
|
||||||
|
|
||||||
|
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.elementsToShow,
|
||||||
|
layer.questions,
|
||||||
|
changes,
|
||||||
|
osmConnection.userDetails
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
new UserBadge(osmConnection.userDetails)
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const pendingChanges = new PendingChanges(
|
||||||
|
changes.pendingChangesES, secondsTillChangesAreSaved, changes.isSaving);
|
||||||
|
|
||||||
|
new UserBadge(osmConnection.userDetails, pendingChanges)
|
||||||
.AttachTo('userbadge');
|
.AttachTo('userbadge');
|
||||||
|
|
||||||
new FixedUiElement(questSetToRender.welcomeMessage)
|
var welcomeMessage = () => {
|
||||||
.AttachTo("welcomeMessage");
|
return new VariableUiElement(
|
||||||
|
osmConnection.userDetails.map((userdetails) => {
|
||||||
|
var login = questSetToRender.gettingStartedPlzLogin;
|
||||||
|
if (userdetails.loggedIn) {
|
||||||
|
login = questSetToRender.welcomeBackMessage;
|
||||||
|
}
|
||||||
|
return "<div id='welcomeMessage'>" +
|
||||||
|
questSetToRender.welcomeMessage + login +
|
||||||
|
"</div>";
|
||||||
|
}),
|
||||||
|
function (html) {
|
||||||
|
osmConnection.registerActivateOsmAUthenticationClass()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
leftMessage.setData(welcomeMessage);
|
||||||
|
|
||||||
new LoginDependendMessage(osmConnection.userDetails, questSetToRender.gettingStartedPlzLogin, questSetToRender.welcomeBackMessage)
|
|
||||||
.AttachTo("gettingStartedBox");
|
|
||||||
|
|
||||||
new PendingChanges(changes, secondsTillChangesAreSaved)
|
var messageBox = new MessageBoxHandler(leftMessage, () => {selectedElement.setData(undefined)});
|
||||||
.AttachTo("pendingchangesbox");
|
|
||||||
|
|
||||||
new CenterMessageBox(
|
new CenterMessageBox(
|
||||||
questSetToRender.startzoom,
|
questSetToRender.startzoom,
|
||||||
|
@ -130,11 +182,14 @@ new CenterMessageBox(
|
||||||
.AttachTo("centermessage");
|
.AttachTo("centermessage");
|
||||||
|
|
||||||
|
|
||||||
Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved);
|
Helpers.SetupAutoSave(changes, secondsTillChangesAreSaved, saveTimeout);
|
||||||
Helpers.LastEffortSave(changes);
|
Helpers.LastEffortSave(changes);
|
||||||
Helpers.registerActivateOsmAUthenticationClass(osmConnection);
|
|
||||||
|
|
||||||
|
osmConnection.registerActivateOsmAUthenticationClass();
|
||||||
|
|
||||||
|
|
||||||
// --------------- Send a ping to start various action --------
|
// --------------- Send a ping to start various action --------
|
||||||
|
|
||||||
locationControl.ping();
|
locationControl.ping();
|
||||||
|
messageBox.update();
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
<link href="index.css" rel="stylesheet"/>
|
<link href="index.css" rel="stylesheet"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="maindiv">Hello World</div>
|
<div id="maindiv" style="border: 5px dashed red">Hello World</div>
|
||||||
|
|
||||||
<script src="./test.ts"></script>
|
<script src="./test.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
102
test.ts
102
test.ts
|
@ -1,50 +1,70 @@
|
||||||
import {FixedUiElement} from "./UI/FixedUiElement";
|
// The message that should be shown at the center of the screen
|
||||||
import $ from "jquery"
|
|
||||||
import {Imgur} from "./Logic/Imgur";
|
|
||||||
import {ImageUploadFlow} from "./UI/ImageUploadFlow";
|
|
||||||
import {UserDetails} from "./Logic/OsmConnection";
|
|
||||||
import {UIEventSource} from "./UI/UIEventSource";
|
import {UIEventSource} from "./UI/UIEventSource";
|
||||||
import {UIRadioButton} from "./UI/UIRadioButton";
|
|
||||||
import {UIElement} from "./UI/UIElement";
|
import {UIElement} from "./UI/UIElement";
|
||||||
|
import {ElementStorage} from "./Logic/ElementStorage";
|
||||||
|
import {OsmConnection} from "./Logic/OsmConnection";
|
||||||
|
import {Changes} from "./Logic/Changes";
|
||||||
|
import {Basemap} from "./Logic/Basemap";
|
||||||
|
import {KnownSet} from "./Layers/KnownSet";
|
||||||
|
import {Overpass} from "./Logic/Overpass";
|
||||||
|
import {FeatureInfoBox} from "./UI/FeatureInfoBox";
|
||||||
|
import {TagMapping, TagMappingOptions} from "./UI/TagMapping";
|
||||||
|
import {CommonTagMappings} from "./Layers/CommonTagMappings";
|
||||||
|
import {ImageCarousel} from "./UI/Image/ImageCarousel";
|
||||||
|
import {WikimediaImage} from "./UI/Image/WikimediaImage";
|
||||||
|
|
||||||
|
const centerMessage = new UIEventSource<string>("");
|
||||||
|
|
||||||
var tags = {
|
const dryRun = true;
|
||||||
"name": "Astridpark Brugge",
|
// If you have a testfile somewhere, enable this to spoof overpass
|
||||||
"wikidata":"Q1234",
|
// This should be hosted independantly, e.g. with `cd assets; webfsd -p 8080` + a CORS plugin to disable cors rules
|
||||||
"leisure":"park"
|
Overpass.testUrl = "http://127.0.0.1:8080/test.json";
|
||||||
}
|
// 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);
|
||||||
|
|
||||||
var userdetails = new UserDetails()
|
const leftMessage = new UIEventSource<() => UIElement>(undefined);
|
||||||
userdetails.loggedIn = true;
|
|
||||||
userdetails.name = "Pietervdvn";
|
|
||||||
|
|
||||||
|
const selectedElement = new UIEventSource<any>(undefined);
|
||||||
|
|
||||||
new ImageUploadFlow(
|
const questSetToRender = KnownSet.groen;
|
||||||
).AttachTo("maindiv") //*/
|
|
||||||
|
|
||||||
|
const locationControl = new UIEventSource({
|
||||||
|
zoom: questSetToRender.startzoom,
|
||||||
/*
|
lat: questSetToRender.startLat,
|
||||||
$('document').ready(function () {
|
lon: questSetToRender.startLon
|
||||||
$('input[type=file]').on('change', function () {
|
|
||||||
var $files = $(this).get(0).files;
|
|
||||||
|
|
||||||
if ($files.length) {
|
|
||||||
// Reject big files
|
|
||||||
if ($files[0].size > $(this).data('max-size') * 1024) {
|
|
||||||
console.log('Please select a smaller file');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin file upload
|
|
||||||
console.log('Uploading file to Imgur..');
|
|
||||||
|
|
||||||
const imgur = new Imgur();
|
|
||||||
imgur.uploadImage("KorenBloem", "Een korenbloem, ergens", $files[0],
|
|
||||||
(url) => {
|
|
||||||
console.log("URL: ", url);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
|
// ----------------- Prepare the important objects -----------------
|
||||||
|
|
||||||
|
const saveTimeout = 5000; // 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 #" + questSetToRender.name,
|
||||||
|
osmConnection, allElements, centerMessage);
|
||||||
|
|
||||||
|
|
||||||
|
const layer = questSetToRender.layers[0];
|
||||||
|
const tags ={
|
||||||
|
id: "way/123",
|
||||||
|
// access: "yes",
|
||||||
|
barrier: "fence",
|
||||||
|
curator: "Arnout Zwaenepoel",
|
||||||
|
description: "Heide en heischraal landschap met landduin en grote soortenverscheidenheid",
|
||||||
|
dog: "no",
|
||||||
|
email: "arnoutenregine@skynet.be",
|
||||||
|
image: "https://natuurpuntbrugge.be/wp-content/uploads/2017/05/Schobbejakshoogte-schapen-PDG-1024x768.jpg",
|
||||||
|
leisure: "nature_reserve",
|
||||||
|
name: "Schobbejakshoogte",
|
||||||
|
operator: "Natuurpunt Brugge",
|
||||||
|
phone: "+32 50 82 26 97",
|
||||||
|
website: "https://natuurpuntbrugge.be/schobbejakshoogte/",
|
||||||
|
wikidata: "Q4499623",
|
||||||
|
wikipedia: "nl:Schobbejakshoogte"
|
||||||
|
};
|
||||||
|
const tagsES = allElements.addElement({properties: tags});
|
||||||
|
|
||||||
|
new ImageCarousel(tagsES).AttachTo("maindiv").Activate();
|
||||||
|
|
||||||
|
// new WikimediaImage(new UIEventSource<string>("File:Brugge_cobblestone_path.jpg")).AttachTo("maindiv");
|
||||||
|
|
Loading…
Reference in a new issue