commit 38587038d3416557bd7dd3e030fe042cdd69c45e
parent 7afc05a2bd3da5b14f9a3b5dbce8f3d281a4c664
Author: Sergej Orlov <wladimirych@gmail.com>
Date: Tue, 8 Nov 2016 00:15:25 +0300
added hash state for map position and layers
Diffstat:
5 files changed, 229 insertions(+), 21 deletions(-)
diff --git a/src/App.js b/src/App.js
@@ -7,38 +7,35 @@ import './lib/control.caption/caption'
import config from './config'
import './lib/control.coordinates/coordinates';
import './lib/control.layers.hotkeys/control.Layers-hotkeys';
+import './lib/hashState/Leaflet.Map';
+import './lib/hashState/Leaflet.Control.Layers';
function setUp() {
const map = L.map('map', {
- zoomControl: false,
- fadeAnimation: false,
- center:[55.75185, 37.61856],
- zoom: 10
- });
+ zoomControl: false,
+ fadeAnimation: false,
+ zoom: 10
+ }
+ );
+ map.enableHashState('m', [10, 55.75185, 37.61856]);
+
new L.Control.Caption(`<a href=mailto:${config.email}">nakarte@nakarte.tk</a>`, {
- position: 'topleft'
- }).addTo(map);
+ position: 'topleft'
+ }
+ ).addTo(map);
L.control.zoom().addTo(map);
- {
- let baseLayers = layers.getBaseMaps();
- const layersControl = L.control.layers(baseLayers, layers.getOverlays(), {collapsed: false}).addTo(map);
- map.addLayer(baseLayers['OpenStreetMap']);
- }
+
+ let baseLayers = layers.getBaseMaps();
+ const layersControl = L.control.layers(baseLayers, layers.getOverlays(), {collapsed: false}).addTo(map);
+ map.addLayer(baseLayers['OpenStreetMap']);
+ layersControl.enableHashState('l');
+
new L.Control.PrintPages().addTo(map);
new L.Control.Coordinates().addTo(map);
- // const hashState = L.HashState();
- // hashState.bind(map);
- // hashState.bind(layersControl);
-
- // let p = L.polyline([[0, 0], [20, 20]]);
- // p.addTo(map);
- // map.on('contextmenu', () => {
- // map.flyTo([10, 20], 13, {duration: 1});
- // });
}
export default {setUp};
diff --git a/src/lib/hashState/Leaflet.Control.Layers.js b/src/lib/hashState/Leaflet.Control.Layers.js
@@ -0,0 +1,39 @@
+import L from 'leaflet';
+import './leaflet.hashState';
+
+L.Control.Layers.include(L.Mixin.HashState);
+
+L.Control.Layers.include({
+ stateChangeEvents: ['baselayerchange', 'overlayadd', 'overlayremove'],
+ stateChangeEventsSource: '_map',
+
+ serializeState: function(e) {
+ const state = [];
+
+ this._map.eachLayer((layer) => {
+ let key = layer.options.code;
+ if (key && key.length === 1) {
+ state.push(key);
+ }
+ }
+ );
+ return state;
+ },
+
+ unserializeState: function(values) {
+ if (!values || values.length === 0) {
+ return false;
+ }
+
+ for (let layer of this._layers) {
+ if (values.includes(layer.layer.options.code)) {
+ this._map.addLayer(layer.layer);
+ } else {
+ this._map.removeLayer(layer.layer);
+ }
+ }
+ return true;
+ }
+ }
+);
+
diff --git a/src/lib/hashState/Leaflet.Map.js b/src/lib/hashState/Leaflet.Map.js
@@ -0,0 +1,39 @@
+import L from 'leaflet';
+import './leaflet.hashState'
+
+L.Map.include(L.Mixin.HashState);
+
+L.Map.include({
+ stateChangeEvents: ['moveend'],
+
+ serializeState: function() {
+ var center = this.getCenter();
+ var zoom = this.getZoom();
+ var precision = 5;
+ var state = [
+ zoom,
+ center.lat.toFixed(precision),
+ center.lng.toFixed(precision),
+ ];
+ return state;
+ },
+
+ unserializeState: function(values) {
+ if (!values || values.length !== 3) {
+ return false;
+ }
+ var zoom = parseInt(values[0], 10),
+ lat = parseFloat(values[1]),
+ lng = parseFloat(values[2]);
+ if (isNaN(zoom) || isNaN(lat) || isNaN(lng) || zoom < 0 || zoom > 32 || lat < -90 || lat > 90 || lng < -180 ||
+ lng > 180) {
+ return false;
+ }
+ // this._updating_state = true;
+ this.setView([lat, lng], zoom);
+ // this._updating_state = false;
+ return true;
+ }
+ }
+);
+
diff --git a/src/lib/hashState/hashState.js b/src/lib/hashState/hashState.js
@@ -0,0 +1,101 @@
+function arrayItemsEqual(l1, l2) {
+ l1 = l1 || [];
+ l2 = l2 || [];
+ if (l1.length !== l2.length)
+ return false;
+ for (var i = 0; i < l1.length; i++) {
+ if (l1[i] !== l2[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+const hashState = {
+ _listeners: [],
+ _state: {},
+
+ addEventListener: function(key, callback) {
+ for (let [k, c] of this._listeners) {
+ if (k === key && c === callback) {
+ return;
+ }
+ }
+ this._listeners.push([key, callback]);
+ },
+
+ removeEventListener: function(key, callback) {
+ this._listeners.forEach(([k, c], i) => {
+ if (k === key && c === callback) {
+ this._listeners.splice(i, 1);
+ return;
+ }
+ }
+ );
+ },
+
+ updateState: function(key, values) {
+ this._state[key] = values;
+ this._saveStateToHash();
+ },
+
+ getState: function(key) {
+ return this._state[key];
+ },
+
+ _parseHash: function() {
+ let hash = location.hash;
+ const args = {},
+ i = hash.indexOf('#');
+ if (i >= 0) {
+ hash = hash.substr(i + 1).trim();
+ let m, key, value;
+ for (let pair of hash.split('&')) {
+ m = /(.+?)=(.+)/.exec(pair);
+ if (m) {
+ [, key, value] = m;
+ value = value.split('/');
+ value = value.map(decodeURIComponent);
+ args[key] = value;
+ }
+ }
+ }
+ return args;
+ },
+
+ _saveStateToHash: function() {
+ var stateItems = [];
+ for (var key in this._state) {
+ if (this._state[key].length) {
+ var values = this._state[key].join('/');
+ stateItems.push(key + '=' + values);
+ }
+ }
+ var hashString = '#' + stateItems.join('&');
+ location.replace(hashString);
+ },
+
+
+ onHashChanged: function() {
+ const newState = this._parseHash();
+ const changedKeys = {};
+ for (let key in newState) {
+ if (!arrayItemsEqual(newState[key], this._state[key])) {
+ changedKeys[key] = 1;
+ }
+ }
+ for (let [key, callback] of this._listeners) {
+ if (key in changedKeys) {
+ // setTimeout(callback.bind(null, state[key]), 0);
+ callback(newState[key]);
+ }
+ }
+ this._state = newState;
+ }
+};
+
+window.addEventListener('hashchange', hashState.onHashChanged.bind(hashState));
+hashState.onHashChanged();
+
+export default hashState;
+
diff --git a/src/lib/hashState/leaflet.hashState.js b/src/lib/hashState/leaflet.hashState.js
@@ -0,0 +1,31 @@
+import L from 'leaflet';
+import hashState from './hashState';
+
+L.Mixin.HashState = {
+ enableHashState: function(key, defaultInitialState = null) {
+ this._hashStateKey = key;
+ const eventSource = this.stateChangeEventsSource ? this[this.stateChangeEventsSource] : this;
+ this.stateChangeEvents.forEach((event) => {
+ eventSource.on(event, this.updateHashState, this);
+ }
+ );
+
+ hashState.addEventListener(key, (state) => {
+ this.unserializeState(state)
+ }
+ );
+
+ const state = hashState.getState(key) || defaultInitialState;
+ if (state) {
+ if (!this.unserializeState(state)) { // state from hash is invalid, update hash from component state
+ hashState.updateState(key, this.serializeState());
+ }
+ }
+ },
+
+ updateHashState: function(state) {
+ hashState.updateState(this._hashStateKey, this.serializeState());
+ }
+
+ // TODO: disableHashState
+};
+\ No newline at end of file