</script>
<!-- deal with back-button and other user action -->
-<svelte:window on:popstate={refresh_page} />
+<svelte:window on:popstate={() => refresh_page()} />
<Header/>
{#if view === 'search'}
export let extra_classes = '';
export let feature = null;
- let url_params = '';
+ let url_params = new URLSearchParams();
+ let href = 'details.html';
function formatShortOSMType(sType) {
if (sType === 'node') return 'N';
}
function handleClick() {
- window.history.pushState([], '', 'details.html' + url_params);
- refresh_page();
+ refresh_page('details', url_params);
}
$: {
+ let new_params = new URLSearchParams();
+
if (feature !== null && feature.osm_type) {
- let param = '?osmtype=';
- if (feature.osm_type.length == 1) {
- param += encodeURIComponent(feature.osm_type);
+ if (feature.osm_type.length === 1) {
+ new_params.set('osmtype', feature.osm_type);
} else {
- param += formatShortOSMType(feature.osm_type);
+ new_params.set('osmtype', formatShortOSMType(feature.osm_type));
}
- param += '&osmid=' + encodeURIComponent(feature.osm_id);
+
+ new_params.set('osmid', feature.osm_id);
+
if (feature.class) {
- param += '&class=' + encodeURIComponent(feature.class);
+ new_params.set('class', feature.class);
} else if (feature.category) {
- param += '&class=' + encodeURIComponent(feature.category);
+ new_params.set('class', feature.category);
}
- url_params = param
- } else {
- url_params = '';
}
- }
+
+ url_params = new_params;
+ }
+
+ $: {
+ let param_str = url_params.toString();
+ href = 'details.html' + (param_str ? '?' : '') + param_str;
+ }
</script>
-<a on:click|preventDefault|stopPropagation={handleClick} href="details.html{url_params}" class={extra_classes}><slot></slot></a>
+<a on:click|preventDefault|stopPropagation={handleClick} href={href} class={extra_classes}><slot></slot></a>
export let page;
function handleClick() {
- window.history.pushState([], '', page + '.html');
- refresh_page();
+ refresh_page(page);
}
</script>
import PageLink from '../components/PageLink.svelte';
import { zoomLevels } from '../lib/helpers.js';
- import { map_store } from '../lib/stores.js';
+ import { map_store, refresh_page } from '../lib/stores.js';
- export let api_request_params = {};
+ export let lat = '';
+ export let lon = '';
+ export let zoom = '';
- map_store.subscribe(map => {
- if (!map) { return; }
+ function gotoCoordinates(newlat, newlon, newzoom) {
+ let params = new URLSearchParams();
+ params.set('lat', newlat);
+ params.set('lon', newlon);
+ params.set('zoom', newzoom || zoom);
+ refresh_page('reverse', params);
+ }
- map.on('click', function (e) {
- document.querySelector('input[name=lat]').value = e.latlng.lat.toFixed(5);
- document.querySelector('input[name=lon]').value = e.latlng.wrap().lng.toFixed(5);
- document.querySelector('form').submit();
- });
+ map_store.subscribe(map => {
+ if (map) {
+ map.on('click', (e) => {
+ let coords = e.latlng.wrap();
+ gotoCoordinates(coords.lat.toFixed(5), coords.lng.toFixed(5));
+ });
+ }
});
- function handleSwitchCoords() {
- let lat = document.querySelector('input[name=lat]').value;
- let lon = document.querySelector('input[name=lon]').value;
- document.querySelector('input[name=lat]').value = lon;
- document.querySelector('input[name=lon]').value = lat;
- document.querySelector('form').submit();
- }
-
// common mistake is to copy&paste latitude and longitude into the 'lat' search box
function maybeSplitLatitude(e) {
var coords_split = e.target.value.split(',');
</script>
<div class="top-bar">
- <UrlSubmitForm>
+ <UrlSubmitForm page="reverse">
<div class="form-group">
<input name="format" type="hidden" value="html">
<label for="reverse-lat">lat</label>
type="text"
class="form-control form-control-sm"
placeholder="latitude"
- value="{api_request_params.lat || ''}"
+ bind:value={lat}
on:change={maybeSplitLatitude} />
<a id="switch-coords"
- on:click|preventDefault|stopPropagation={handleSwitchCoords}
+ on:click|preventDefault|stopPropagation={() => gotoCoordinates(lon, lat)}
class="btn btn-outline-secondary btn-sm"
title="switch lat and lon"><></a>
<label for="reverse-lon">lon</label>
type="text"
class="form-control form-control-sm"
placeholder="longitude"
- value="{api_request_params.lon || ''}" />
+ bind:value={lon} />
<label for="reverse-zoom">max zoom</label>
- <select id="reverse-zoom" name="zoom" class="form-control form-control-sm" value="{api_request_params.zoom}">
- <option value="" selected={!api_request_params.zoom}>---</option>
+ <select id="reverse-zoom" name="zoom" class="form-control form-control-sm" bind:value={zoom}>
+ <option value="">---</option>
{#each zoomLevels() as zoomTitle, i}
- <option value="{i}" selected={i === api_request_params.zoom}>{i} - {zoomTitle}</option>
+ <option value="{i}">{i} - {zoomTitle}</option>
{/each}
</select>
<button type="submit" class="btn btn-primary btn-sm mx-1">
export let lon;
export let zoom = null;
-let params = '';
+let params = new URLSearchParams();
+let href = 'reverse.html';
$: {
+ let new_params = new URLSearchParams();
+
if (lat && lon) {
- let new_params = '?lat=' + encodeURIComponent(lat);
- new_params += '&lon=' + encodeURIComponent(lon);
+ new_params.set('lat', lat);
+ new_params.set('lon', lon);
if (zoom) {
- new_params += '&zoom=' + encodeURIComponent(zoom);
+ new_params.set('zoom', zoom);
}
-
- params = new_params;
- } else {
- params = '';
}
+
+ params = new_params;
}
-function handleClick() {
- window.history.pushState([], '', 'reverse.html' + params);
- refresh_page();
+$: {
+ let param_str = params.toString();
+ href = 'reverse.html' + (param_str ? '?' : '') + param_str;
}
</script>
-<a on:click|preventDefault|stopPropagation={handleClick} href="reverse.html{params}"><slot></slot></a>
+<a on:click|preventDefault|stopPropagation={() => refresh_page('reverse', params)} href={href}><slot></slot></a>
</ul>
<div class="tab-content p-2">
<div class="tab-pane" class:active={!bStructuredSearch} id="simple" role="tabpanel">
- <UrlSubmitForm>
+ <UrlSubmitForm page="search">
<input id="q"
name="q"
type="text"
</UrlSubmitForm>
</div>
<div class="tab-pane" class:active={bStructuredSearch} id="structured" role="tabpanel">
- <UrlSubmitForm>
+ <UrlSubmitForm page="search">
<input name="street" type="text" class="form-control form-control-sm mr-1"
placeholder="House number/Street"
value="{api_request_params.street || ''}" />
<script>
- import { serialize_form, clean_up_url_parameters } from '../lib/api_utils.js';
import { refresh_page } from '../lib/stores.js';
- function handleSubmit(event) {
- event.preventDefault();
+ export let page;
- var target_url = serialize_form(event.target);
- target_url = clean_up_url_parameters(target_url);
+ function serialize_form(form) {
+ var params = new URLSearchParams();
- window.history.pushState({}, '', '?' + target_url);
- refresh_page();
+ Array.prototype.slice.call(form.elements).forEach(function (field) {
+ if (!field.name || field.disabled || ['submit', 'button'].indexOf(field.type) > -1) return;
+
+ if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked) return;
+ if (typeof field.value === 'undefined' || field.value === '') return;
+
+ params.set(field.name, field.value);
+ });
+
+ return params;
}
</script>
-<form on:submit={handleSubmit} class="form-inline" role="search" accept-charset="UTF-8" action="">
+<form on:submit|preventDefault={(e) => refresh_page(page, serialize_form(e.target))} class="form-inline" role="search" accept-charset="UTF-8" action="">
<slot></slot>
</form>
}).join('&');
}
-/*!
- * Serialize all form data into a SearchParams string
- * (c) 2020 Chris Ferdinandi, MIT License, https://gomakethings.com
- * @param {Node} form The form to serialize
- * @return {String} The serialized form data
- */
-export function serialize_form(form) {
- var arr = [];
- Array.prototype.slice.call(form.elements).forEach(function (field) {
- if (!field.name || field.disabled || ['submit', 'button'].indexOf(field.type) > -1) return;
- // if (field.type === 'select-multiple') {
- // Array.prototype.slice.call(field.options).forEach(function (option) {
- // if (!option.selected) return;
- // arr.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(option.value));
- // });
- // return;
- // }
- if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked) return;
- if (typeof field.value === 'undefined') return;
- arr.push(encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value));
- });
- return arr.join('&');
-}
-
-
-// remove any URL paramters with empty values
-// '&empty=&filled=value' => 'filled=value'
-export function clean_up_url_parameters(url) {
- var url_params = new URLSearchParams(url);
- var to_delete = []; // deleting inside loop would skip iterations
- url_params.forEach(function (value, key) {
- if (value === '') to_delete.push(key);
- });
- for (var i = 0; i < to_delete.length; i += 1) {
- url_params.delete(to_delete[i]);
- }
- return url_params.toString();
-}
function clean_up_parameters(params) {
// `&a=&b=&c=1` => '&c=1'
export const map_store = writable();
export const results_store = writable();
export const last_api_request_url_store = writable();
-export const page = writable({ count: 0 });
+export const page = writable();
-export function refresh_page() {
- let pagename = window.location.pathname.replace('.html', '').replace(/^.*\//, '');
+/**
+ * Update the global page state.
+ *
+ * When called without a parameter, then the current window.location is
+ * parsed and the page state is set accordingly. Otherwise the page state
+ * is set from the parameters. 'pagename' is the overall subpage (without
+ * .html extension). 'params' must be an URLSearchParams object and contain
+ * the requested query parameters. It may also be omitted completely for a
+ * link without query parameters.
+ */
+export function refresh_page(pagename, params) {
+ if (typeof pagename === 'undefined') {
+ pagename = window.location.pathname.replace('.html', '').replace(/^.*\//, '');
- if (['search', 'reverse', 'details', 'deletable', 'polygons'].indexOf(pagename) === -1) {
- pagename = 'search';
+ if (['search', 'reverse', 'details', 'deletable', 'polygons'].indexOf(pagename) === -1) {
+ pagename = 'search';
+ }
+
+ params = new URLSearchParams(window.location.search);
+ } else {
+ if (typeof params === 'undefined') {
+ params = new URLSearchParams();
+ }
+
+ let param_str = params.toString();
+ if (param_str) {
+ param_str = '?' + param_str;
+ }
+ window.history.pushState([], '', pagename + '.html' + param_str);
}
- // Add a counter here to make sure the store change is triggered
- // everytime we refresh, not just when the page changes.
- page.update(function (v) { return { tab: pagename, count: v.count + 1 }; });
+ page.set({ tab: pagename, params: params });
}
<script>
- import { onMount, onDestroy } from 'svelte';
import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
import { page } from '../lib/stores.js';
let base_url = window.location.search;
let current_result;
- function loaddata() {
- var search_params = new URLSearchParams(window.location.search);
-
+ function loaddata(search_params) {
var api_request_params = {
place_id: search_params.get('place_id'),
osmtype: search_params.get('osmtype'),
}
fetch_from_api('details', api_request_params, function (data) {
+ window.scrollTo(0, 0)
if (data.error) {
errorResponse = data;
current_result = undefined;
}
}
- let page_subscription;
- onMount(() => { page_subscription = page.subscribe(loaddata); });
- onDestroy(() => { page_subscription(); });
-
+ $: {
+ let pageinfo = $page;
+ if (pageinfo.tab === 'details') {
+ loaddata(pageinfo.params);
+ }
+ }
</script>
{#if errorResponse}
<script>
- import { onMount, onDestroy } from 'svelte';
-
import { page, results_store } from '../lib/stores.js';
import { get_config_value } from '../lib/config_reader.js';
import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
let current_result;
let position_marker; // what the user searched for
- function loaddata() {
- let search_params = new URLSearchParams(window.location.search);
-
+ function loaddata(search_params) {
update_html_title();
api_request_params = {
+ api_request_params.lon);
document.querySelector('input[name=lat]').focus();
});
+ } else {
+ results_store.set(undefined);
}
}
- let page_subscription;
- onMount(() => { page_subscription = page.subscribe(loaddata); });
- onDestroy(() => { page_subscription(); });
+ $: {
+ let pageinfo = $page;
+ if (pageinfo.tab === 'reverse') {
+ loaddata(pageinfo.params);
+ }
+ }
</script>
-<ReverseBar api_request_params={api_request_params} />
+<ReverseBar {...api_request_params} />
<div id="content">
<div class="sidebar">
<script>
- import { onMount, onDestroy } from 'svelte';
-
import { page, results_store } from '../lib/stores.js';
import { get_config_value } from '../lib/config_reader.js';
import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
let bStructuredSearch;
let current_result;
- function loaddata() {
- let search_params = new URLSearchParams(window.location.search);
-
+ function loaddata(search_params) {
update_html_title();
api_request_params = {
document.querySelector('input[name=q]').focus();
});
+ } else {
+ results_store.set(undefined);
}
}
- let page_subscription;
- onMount(() => { page_subscription = page.subscribe(loaddata); });
- onDestroy(() => { page_subscription(); });
+ $: {
+ let pageinfo = $page;
+ if (pageinfo.tab === 'search') {
+ loaddata(pageinfo.params);
+ }
+ }
</script>
<SearchBar api_request_params={api_request_params} bStructuredSearch={bStructuredSearch} />