From 765e07d69c6f1ba791ed54d5269067c437d3ba75 Mon Sep 17 00:00:00 2001 From: emartinez Date: Thu, 3 May 2012 11:00:30 -0600 Subject: [PATCH 1/8] Created keyboard handler for map panning/zooming. --- build/deps.js | 5 ++ src/map/handler/Map.Keyboard.js | 95 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/map/handler/Map.Keyboard.js diff --git a/build/deps.js b/build/deps.js index f89d27669..fd546d694 100644 --- a/build/deps.js +++ b/build/deps.js @@ -180,6 +180,11 @@ var deps = { desc: 'Enables zooming to bounding box by shift-dragging the map.' }, + Keyboard: { + src: ['map/handler/Map.Keyboard.js'], + desc: 'Enables keyboard pan/zoom when map is active.' + }, + MarkerDrag: { src: ['layer/marker/Marker.Drag.js'], deps: ['Marker'], diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js new file mode 100644 index 000000000..9367498bf --- /dev/null +++ b/src/map/handler/Map.Keyboard.js @@ -0,0 +1,95 @@ +L.Map.mergeOptions({ + keyboard: true +}); + +L.Map.Keyboard = L.Handler.extend({ + // Cross browser list of e.keyCode values for particular keys. + // This list currently covers: + // + // Mac OSX 10.6.8 + // Safari 5.1.1 + // Firefox 11 + // Chrome 18 + // + // Windows 7 + // IE 8 + // IE 9 + // Firefox 4 + // Chrome 18 + _upArrow: {38: true}, + _rightArrow: {39: true}, + _downArrow: {40: true}, + _leftArrow: {37: true}, + _plusKey: {187: true, 61: true, 107: true}, + _minusKey: {189: true, 109: true, 0: true}, + + options: { + panDistance: 50 // Pixels + }, + + initialize: function (map) { + this._map = map; + this._container = map._container; + }, + + addHooks: function () { + L.DomEvent.addListener(this._container, 'click', this._onClick, this); + }, + + removeHooks: function () { + L.DomEvent.removeListener(this._container, 'click', this._onClick, this); + }, + + _onClick: function (e) { + if (this._checkInMap(e.target || e.srcElement)) { + this._addHooks(); + } else { + this._removeHooks(); + } + }, + + _addHooks: function () { + L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); + }, + + _removeHooks: function () { + L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); + }, + + _onKeyDown: function (e) { + var key = e.keyCode, + dist = this.options.panDistance; + + if (key in this._leftArrow) { + this._map.panBy(new L.Point(-1 * dist, 0)); + } else if (key in this._rightArrow) { + this._map.panBy(new L.Point(dist, 0)); + } else if (key in this._upArrow) { + this._map.panBy(new L.Point(0, -1 * dist)); + } else if (key in this._downArrow) { + this._map.panBy(new L.Point(0, dist)); + } else if (key in this._plusKey) { + this._map.zoomIn(); + } else if (key in this._minusKey) { + this._map.zoomOut(); + } else { + return; + } + L.DomEvent.stop(e); + }, + + _checkInMap: function (element) { + try { + if (element === this._container) { + return true; + } else if (!element.parentNode) { + return false; + } else { + return this._checkInMap(element.parentNode); + } + } catch (e) { + return false; + } + } +}); +L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard); From 909fac4be7d7c933a66db00eacc6fb9dd741637c Mon Sep 17 00:00:00 2001 From: emartinez Date: Thu, 3 May 2012 12:18:33 -0600 Subject: [PATCH 2/8] Corrected click handler for click-out events properly. --- src/map/handler/Map.Keyboard.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 9367498bf..8bf2fcb7b 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -41,19 +41,26 @@ L.Map.Keyboard = L.Handler.extend({ }, _onClick: function (e) { - if (this._checkInMap(e.target || e.srcElement)) { - this._addHooks(); - } else { + this._addHooks(); + }, + + _onClickOut: function (e) { + if (!this._checkInMap(e.target || e.srcElement)) { this._removeHooks(); } }, _addHooks: function () { - L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent + .addListener(document, 'keydown', this._onKeyDown, this) + .addListener(document, 'click', this._onClickOut, this); + }, _removeHooks: function () { - L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); + L.DomEvent + .removeListener(document, 'keydown', this._onKeyDown, this) + .removeListener(document, 'click', this._onClickOut, this); }, _onKeyDown: function (e) { From 4fa5918956da6523024073cb7187b2a5283971ac Mon Sep 17 00:00:00 2001 From: emartinez Date: Thu, 3 May 2012 13:01:26 -0600 Subject: [PATCH 3/8] Updated keyboard handler to be more flexible/extensible. Calls to map.keyboard.setZoomOffset and map.keyboard.setPanOffset can change the behavior of this handler. --- src/map/handler/Map.Keyboard.js | 107 +++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 8bf2fcb7b..721c548ba 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -16,20 +16,83 @@ L.Map.Keyboard = L.Handler.extend({ // IE 9 // Firefox 4 // Chrome 18 - _upArrow: {38: true}, - _rightArrow: {39: true}, - _downArrow: {40: true}, - _leftArrow: {37: true}, - _plusKey: {187: true, 61: true, 107: true}, - _minusKey: {189: true, 109: true, 0: true}, + _leftKeys: [37], + _rightKeys: [39], + _downKeys: [40], + _upKeys: [38], - options: { - panDistance: 50 // Pixels - }, - - initialize: function (map) { + _inKeys: [187, 61, 107], + _outKeys: [189, 109, 0], + + panKeys: {}, + zoomKeys: {}, + + initialize: function (map, pan, zoom) { this._map = map; this._container = map._container; + + this.setPanOffset(pan); + this.setZoomOffset(zoom); + }, + + setPanOffset: function (pan) { + var panKeys = {}, + keyCode = null, + i = 0; + + if (typeof pan !== 'number') { + pan = L.Map.Keyboard.DEFAULT_PAN; + } + + // Left + for (i = 0; i < this._leftKeys.length; i++) { + keyCode = this._leftKeys[i]; + panKeys[keyCode] = new L.Point(-1 * pan, 0); + } + + // Right + for (i = 0; i < this._rightKeys.length; i++) { + keyCode = this._rightKeys[i]; + panKeys[keyCode] = new L.Point(pan, 0); + } + + // Down + for (i = 0; i < this._downKeys.length; i++) { + keyCode = this._downKeys[i]; + panKeys[keyCode] = new L.Point(0, -1 * pan); + } + + // Up + for (i = 0; i < this._upKeys.length; i++) { + keyCode = this._upKeys[i]; + panKeys[keyCode] = new L.Point(0, pan); + } + + this.panKeys = panKeys; + }, + + setZoomOffset: function (zoom) { + var zoomKeys = {}, + keyCode = null, + i = 0; + + if (typeof zoom !== 'number') { + zoom = L.Map.Keyboard.DEFAULT_ZOOM; + } + + // In + for (i = 0; i < this._inKeys.length; i++) { + keyCode = this._inKeys[i]; + zoomKeys[keyCode] = zoom; + } + + // Out + for (i = 0; i < this._outKeys.length; i++) { + keyCode = this._outKeys[i]; + zoomKeys[keyCode] = -1 * zoom; + } + + this.zoomKeys = zoomKeys; }, addHooks: function () { @@ -65,20 +128,12 @@ L.Map.Keyboard = L.Handler.extend({ _onKeyDown: function (e) { var key = e.keyCode, - dist = this.options.panDistance; + map = this._map; - if (key in this._leftArrow) { - this._map.panBy(new L.Point(-1 * dist, 0)); - } else if (key in this._rightArrow) { - this._map.panBy(new L.Point(dist, 0)); - } else if (key in this._upArrow) { - this._map.panBy(new L.Point(0, -1 * dist)); - } else if (key in this._downArrow) { - this._map.panBy(new L.Point(0, dist)); - } else if (key in this._plusKey) { - this._map.zoomIn(); - } else if (key in this._minusKey) { - this._map.zoomOut(); + if (this.panKeys.hasOwnProperty(key)) { + map.panBy(this.panKeys[key]); + } else if (this.zoomKeys.hasOwnProperty(key)) { + map.setZoom(map.getZoom() + this.zoomKeys[key]); } else { return; } @@ -99,4 +154,8 @@ L.Map.Keyboard = L.Handler.extend({ } } }); + +L.Map.Keyboard.DEFAULT_PAN = 50; // Pixels +L.Map.Keyboard.DEFAULT_ZOOM = 1; // Zoom levels + L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard); From aaa0721073c8caf3ef53c918e0d9d83e6e6e51ab Mon Sep 17 00:00:00 2001 From: Eric Martinez Date: Fri, 4 May 2012 22:19:48 -0600 Subject: [PATCH 4/8] Reversed up/down arrow pan direction. Some noted the inverted style is not expected. --- src/map/handler/Map.Keyboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 721c548ba..8c2c1928d 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -59,13 +59,13 @@ L.Map.Keyboard = L.Handler.extend({ // Down for (i = 0; i < this._downKeys.length; i++) { keyCode = this._downKeys[i]; - panKeys[keyCode] = new L.Point(0, -1 * pan); + panKeys[keyCode] = new L.Point(0, pan); } // Up for (i = 0; i < this._upKeys.length; i++) { keyCode = this._upKeys[i]; - panKeys[keyCode] = new L.Point(0, pan); + panKeys[keyCode] = new L.Point(0, -1 * pan); } this.panKeys = panKeys; From 20bec613ec15ef3997800c60eb75abb972427e75 Mon Sep 17 00:00:00 2001 From: Eric Martinez Date: Fri, 4 May 2012 22:31:59 -0600 Subject: [PATCH 5/8] Support for map.options.keyboardPanOffset and map.options.keyboardZoomOffset. Use these to configure how far the keyboard controls will pan/zoom the map (respectively). --- src/map/handler/Map.Keyboard.js | 41 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 8c2c1928d..5a4d974b6 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -1,5 +1,7 @@ L.Map.mergeOptions({ - keyboard: true + keyboard: true, + keyboardPanOffset: 50, + keyboardZoomOffset: 1 }); L.Map.Keyboard = L.Handler.extend({ @@ -27,15 +29,32 @@ L.Map.Keyboard = L.Handler.extend({ panKeys: {}, zoomKeys: {}, - initialize: function (map, pan, zoom) { + initialize: function (map) { this._map = map; this._container = map._container; - - this.setPanOffset(pan); - this.setZoomOffset(zoom); + var panOffset = map.options.keyboardPanOffset; + var zoomOffset = map.options.keyboardZoomOffset; + + if (typeof panOffset !== 'number') { + panOffset = 50; + } + if (typeof zoomOffset !== 'number') { + zoomOffset = 1; + } + + this._setPanOffset(panOffset); + this._setZoomOffset(zoomOffset); }, - setPanOffset: function (pan) { + addHooks: function () { + L.DomEvent.addListener(this._container, 'click', this._onClick, this); + }, + + removeHooks: function () { + L.DomEvent.removeListener(this._container, 'click', this._onClick, this); + }, + + _setPanOffset: function (pan) { var panKeys = {}, keyCode = null, i = 0; @@ -71,7 +90,7 @@ L.Map.Keyboard = L.Handler.extend({ this.panKeys = panKeys; }, - setZoomOffset: function (zoom) { + _setZoomOffset: function (zoom) { var zoomKeys = {}, keyCode = null, i = 0; @@ -95,14 +114,6 @@ L.Map.Keyboard = L.Handler.extend({ this.zoomKeys = zoomKeys; }, - addHooks: function () { - L.DomEvent.addListener(this._container, 'click', this._onClick, this); - }, - - removeHooks: function () { - L.DomEvent.removeListener(this._container, 'click', this._onClick, this); - }, - _onClick: function (e) { this._addHooks(); }, From a69477abcf2a82b2597355b79c4c9bf71886422c Mon Sep 17 00:00:00 2001 From: Eric Martinez Date: Fri, 4 May 2012 23:46:15 -0600 Subject: [PATCH 6/8] Created a focus handler for the map. Converted keyboard handler to make use of new focus events. --- build/deps.js | 8 +++- dist/leaflet.css | 3 ++ src/map/handler/Map.Focus.js | 69 +++++++++++++++++++++++++++++++++ src/map/handler/Map.Keyboard.js | 32 +++------------ 4 files changed, 85 insertions(+), 27 deletions(-) create mode 100644 src/map/handler/Map.Focus.js diff --git a/build/deps.js b/build/deps.js index fd546d694..a0f217716 100644 --- a/build/deps.js +++ b/build/deps.js @@ -180,9 +180,15 @@ var deps = { desc: 'Enables zooming to bounding box by shift-dragging the map.' }, + Focus: { + src: ['map/handler/Map.Focus.js'], + desc: 'Enables map to gain focus.' + }, + Keyboard: { src: ['map/handler/Map.Keyboard.js'], - desc: 'Enables keyboard pan/zoom when map is active.' + deps: ['Focus'], + desc: 'Enables keyboard pan/zoom when map is focused.' }, MarkerDrag: { diff --git a/dist/leaflet.css b/dist/leaflet.css index b089b330e..140a76eaf 100644 --- a/dist/leaflet.css +++ b/dist/leaflet.css @@ -338,6 +338,9 @@ a.leaflet-active { .leaflet-container { background: #ddd; } +.leaflet-container-nofocus { + outline:0; +} .leaflet-container a { color: #0078A8; } diff --git a/src/map/handler/Map.Focus.js b/src/map/handler/Map.Focus.js new file mode 100644 index 000000000..3e05cf285 --- /dev/null +++ b/src/map/handler/Map.Focus.js @@ -0,0 +1,69 @@ +/* + * L.Handler.Focus is used internally by L.Map to make the map focusable. + */ + +L.Map.mergeOptions({ + focus: true +}); + +L.Map.Focus = L.Handler.extend({ + _focused: false, + + initialize: function (map) { + this._map = map; + this._container = map._container; + + this._makeFocusable(); + this._focused = false; + }, + + addHooks: function () { + var container = this._container; + L.DomEvent + .addListener(container, 'focus', this.onFocus, this) + .addListener(container, 'blur', this.onBlur, this) + .addListener(container, 'click', this.onClick, this); + }, + + removeHooks: function () { + var container = this._container; + L.DomEvent + .removeListener(container, 'focus', this.onFocus, this) + .removeListener(container, 'blur', this.onBlur, this) + .removeListener(container, 'click', this.onClick, this); + }, + + onClick: function (e) { + if (!this._focused) { + this._container.focus(); + } + }, + + onFocus: function (e) { + this._focused = true; + this._map.fire('focus'); + }, + + onBlur: function (e) { + this._focused = false; + this._map.fire('blur'); + }, + + _makeFocusable: function () { + var map = this._map, container = this._container; + + + // While we want the map to be "focusable", we don't want the map to + // appear focused (i.e. no outline etc...) + L.DomUtil.addClass(container, 'leaflet-container-nofocus'); + + // Allows user to tab to the container. + // -1 => User can focus container by clicks, but not tabs + // 0 => User can focus container by clicks or tabs. Order is based on + // DOM source order. + // N => User can focus container by clicks or tabs. N = tab order. + container.tabIndex = "0"; + } +}); + +L.Map.addInitHook('addHandler', 'focus', L.Map.Focus); diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index 5a4d974b6..d5b91d23e 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -47,11 +47,15 @@ L.Map.Keyboard = L.Handler.extend({ }, addHooks: function () { - L.DomEvent.addListener(this._container, 'click', this._onClick, this); + this._map.on('focus', this._addHooks, this) + .on('blur', this._removeHooks, this); }, removeHooks: function () { - L.DomEvent.removeListener(this._container, 'click', this._onClick, this); + this._removeHooks(); + + this._map.off('focus', this._addHooks, this) + .off('blur', this._addHooks, this); }, _setPanOffset: function (pan) { @@ -114,16 +118,6 @@ L.Map.Keyboard = L.Handler.extend({ this.zoomKeys = zoomKeys; }, - _onClick: function (e) { - this._addHooks(); - }, - - _onClickOut: function (e) { - if (!this._checkInMap(e.target || e.srcElement)) { - this._removeHooks(); - } - }, - _addHooks: function () { L.DomEvent .addListener(document, 'keydown', this._onKeyDown, this) @@ -149,20 +143,6 @@ L.Map.Keyboard = L.Handler.extend({ return; } L.DomEvent.stop(e); - }, - - _checkInMap: function (element) { - try { - if (element === this._container) { - return true; - } else if (!element.parentNode) { - return false; - } else { - return this._checkInMap(element.parentNode); - } - } catch (e) { - return false; - } } }); From b937d993c160950a9ed4f6931caf63f11b26371c Mon Sep 17 00:00:00 2001 From: Eric Martinez Date: Sat, 5 May 2012 00:30:45 -0600 Subject: [PATCH 7/8] Removed legacy code that caused console errors. --- src/map/handler/Map.Keyboard.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index d5b91d23e..bfce3db0b 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -119,16 +119,11 @@ L.Map.Keyboard = L.Handler.extend({ }, _addHooks: function () { - L.DomEvent - .addListener(document, 'keydown', this._onKeyDown, this) - .addListener(document, 'click', this._onClickOut, this); - + L.DomEvent.addListener(document, 'keydown', this._onKeyDown, this); }, _removeHooks: function () { - L.DomEvent - .removeListener(document, 'keydown', this._onKeyDown, this) - .removeListener(document, 'click', this._onClickOut, this); + L.DomEvent.removeListener(document, 'keydown', this._onKeyDown, this); }, _onKeyDown: function (e) { From 3d78a8feef35b1e079d92816052556561d815779 Mon Sep 17 00:00:00 2001 From: Eric Martinez Date: Sat, 5 May 2012 00:36:07 -0600 Subject: [PATCH 8/8] Cleaned up use of defaults. --- src/map/handler/Map.Keyboard.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/map/handler/Map.Keyboard.js b/src/map/handler/Map.Keyboard.js index bfce3db0b..e1415dd5f 100644 --- a/src/map/handler/Map.Keyboard.js +++ b/src/map/handler/Map.Keyboard.js @@ -32,18 +32,9 @@ L.Map.Keyboard = L.Handler.extend({ initialize: function (map) { this._map = map; this._container = map._container; - var panOffset = map.options.keyboardPanOffset; - var zoomOffset = map.options.keyboardZoomOffset; - if (typeof panOffset !== 'number') { - panOffset = 50; - } - if (typeof zoomOffset !== 'number') { - zoomOffset = 1; - } - - this._setPanOffset(panOffset); - this._setZoomOffset(zoomOffset); + this._setPanOffset(map.options.keyboardPanOffset); + this._setZoomOffset(map.options.keyboardZoomOffset); }, addHooks: function () {