diff --git a/js/id/id.js b/js/id/id.js
index 31ff26836..9d7fb38bf 100644
--- a/js/id/id.js
+++ b/js/id/id.js
@@ -184,26 +184,18 @@ window.iD = function(container) {
map.size(m.size());
});
- map.keybinding()
- .on('a', function(evt, mods) {
- if (mods) return;
- controller.enter(iD.modes.AddArea());
- })
- .on('⌫.prevent_navigation', function(evt, mods) {
- evt.preventDefault();
- })
- .on('p', function(evt, mods) {
- if (mods) return;
- controller.enter(iD.modes.AddPoint());
- })
- .on('l', function(evt, mods) {
- if (mods) return;
- controller.enter(iD.modes.AddLine());
- })
- .on('z', function(evt, mods) {
- if (mods === '⇧⌘' || mods === '⌃⇧') history.redo();
- if (mods === '⌘' || mods === '⌃') history.undo();
- });
+ var keybinding = d3.keybinding('main')
+ .on('P', function() { controller.enter(iD.modes.AddPoint()); })
+ .on('L', function() { controller.enter(iD.modes.AddLine()); })
+ .on('A', function() { controller.enter(iD.modes.AddArea()); })
+ .on('⌘+Z', function() { history.undo(); })
+ .on('⌃+Z', function() { history.undo(); })
+ .on('⌘+⇧+Z', function() { history.redo(); })
+ .on('⌃+⇧+Z', function() { history.redo(); })
+ .on('⌫', function(e) { e.preventDefault(); });
+
+ d3.select(document)
+ .call(keybinding);
var hash = iD.Hash().controller(controller).map(map);
diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js
index a9fcb830e..d3936229e 100644
--- a/js/id/modes/add_area.js
+++ b/js/id/modes/add_area.js
@@ -6,6 +6,8 @@ iD.modes.AddArea = function() {
description: 'Add parks, buildings, lakes, or other areas to the map.'
};
+ var keybinding = d3.keybinding('add-area');
+
mode.enter = function() {
var map = mode.map,
history = mode.history,
@@ -38,9 +40,12 @@ iD.modes.AddArea = function() {
controller.enter(iD.modes.DrawArea(way.id));
});
- map.keybinding().on('⎋.addarea', function() {
+ keybinding.on('⎋', function() {
controller.exit();
});
+
+ d3.select(document)
+ .call(keybinding);
};
mode.exit = function() {
@@ -49,7 +54,7 @@ iD.modes.AddArea = function() {
}, 1000);
mode.map.tail(false);
mode.map.surface.on('click.addarea', null);
- mode.map.keybinding().on('⎋.addarea', null);
+ keybinding.off();
};
return mode;
diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js
index b83202097..db4e88e18 100644
--- a/js/id/modes/add_line.js
+++ b/js/id/modes/add_line.js
@@ -6,6 +6,8 @@ iD.modes.AddLine = function() {
description: 'Lines can be highways, streets, pedestrian paths, or even canals.'
};
+ var keybinding = d3.keybinding('add-line');
+
mode.enter = function() {
var map = mode.map,
node,
@@ -60,16 +62,19 @@ iD.modes.AddLine = function() {
controller.enter(iD.modes.DrawLine(way.id, direction));
});
- map.keybinding().on('⎋.addline', function() {
+ keybinding.on('⎋', function() {
controller.exit();
});
+
+ d3.select(document)
+ .call(keybinding);
};
mode.exit = function() {
mode.map.dblclickEnable(true);
mode.map.tail(false);
mode.map.surface.on('click.addline', null);
- mode.map.keybinding().on('⎋.addline', null);
+ keybinding.off();
};
return mode;
diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js
index 82ad283c2..70275d050 100644
--- a/js/id/modes/add_point.js
+++ b/js/id/modes/add_point.js
@@ -5,6 +5,8 @@ iD.modes.AddPoint = function() {
description: 'Restaurants, monuments, and postal boxes are points.'
};
+ var keybinding = d3.keybinding('add-point');
+
mode.enter = function() {
var map = mode.map,
history = mode.history,
@@ -22,15 +24,18 @@ iD.modes.AddPoint = function() {
controller.enter(iD.modes.Select(node, true));
});
- map.keybinding().on('⎋.addpoint', function() {
+ keybinding.on('⎋', function() {
controller.exit();
});
+
+ d3.select(document)
+ .call(keybinding);
};
mode.exit = function() {
mode.map.tail(false);
mode.map.surface.on('click.addpoint', null);
- mode.map.keybinding().on('⎋.addpoint', null);
+ keybinding.off();
};
return mode;
diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js
index 2f1fa6304..2fe02f37c 100644
--- a/js/id/modes/draw_area.js
+++ b/js/id/modes/draw_area.js
@@ -4,6 +4,8 @@ iD.modes.DrawArea = function(wayId) {
id: 'draw-area'
};
+ var keybinding = d3.keybinding('draw-area');
+
mode.enter = function() {
var map = mode.map,
surface = map.surface,
@@ -102,11 +104,14 @@ iD.modes.DrawArea = function(wayId) {
.on('mouseover.drawarea', mouseover)
.on('click.drawarea', click);
- map.keybinding()
- .on('⌫.drawarea', backspace)
- .on('⌦.drawarea', del)
- .on('⎋.drawarea', ret)
- .on('↩.drawarea', ret);
+ keybinding
+ .on('⌫', backspace)
+ .on('⌦', del)
+ .on('⎋', ret)
+ .on('↩', ret);
+
+ d3.select(document)
+ .call(keybinding);
};
mode.exit = function() {
@@ -122,11 +127,7 @@ iD.modes.DrawArea = function(wayId) {
.on('mousemove.drawarea', null)
.on('click.drawarea', null);
- mode.map.keybinding()
- .on('⎋.drawarea', null)
- .on('⌫.drawarea', null)
- .on('⌦.drawarea', null)
- .on('↩.drawarea', null);
+ keybinding.off();
window.setTimeout(function() {
mode.map.dblclickEnable(true);
diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js
index 8442fbf80..e857bafe4 100644
--- a/js/id/modes/draw_line.js
+++ b/js/id/modes/draw_line.js
@@ -4,6 +4,8 @@ iD.modes.DrawLine = function(wayId, direction) {
id: 'draw-line'
};
+ var keybinding = d3.keybinding('draw-line');
+
mode.enter = function() {
var map = mode.map,
surface = map.surface,
@@ -133,16 +135,19 @@ iD.modes.DrawLine = function(wayId, direction) {
.on('mousemove.drawline', mousemove)
.on('click.drawline', click);
- map.keybinding()
- .on('⌫.drawline', backspace)
- .on('⌦.drawline', del)
- .on('⎋.drawline', ret)
- .on('↩.drawline', ret)
- .on('z.drawline', function(evt, mods) {
- if (mods === '⌘' || mods === '⌃') undo();
- });
+ keybinding
+ .on('⌫', backspace)
+ .on('⌦', del)
+ .on('⎋', ret)
+ .on('↩', ret)
+ .on('⌘-Z', undo)
+ .on('⌃-Z', undo);
- d3.select('#undo').on('click.drawline', undo);
+ d3.select(document)
+ .call(keybinding);
+
+ d3.select('#undo')
+ .on('click.drawline', undo);
};
mode.exit = function() {
@@ -159,12 +164,7 @@ iD.modes.DrawLine = function(wayId, direction) {
.on('mousemove.drawline', null)
.on('click.drawline', null);
- mode.map.keybinding()
- .on('⌫.drawline', null)
- .on('⌦.drawline', null)
- .on('⎋.drawline', null)
- .on('↩.drawline', null)
- .on('z.drawline', null);
+ keybinding.off();
d3.select('#undo').on('click.drawline', null);
diff --git a/js/id/modes/select.js b/js/id/modes/select.js
index 0cfd725f7..b735352aa 100644
--- a/js/id/modes/select.js
+++ b/js/id/modes/select.js
@@ -6,6 +6,7 @@ iD.modes.Select = function(entity, initial) {
};
var inspector = iD.ui.inspector().initial(!!initial),
+ keybinding = d3.keybinding('select'),
behaviors;
function remove() {
@@ -132,10 +133,10 @@ iD.modes.Select = function(entity, initial) {
surface.on('click.select', click)
.on('dblclick.browse', dblclick);
- mode.map.keybinding().on('⌫.select', function(e) {
- remove();
- e.preventDefault();
- });
+ keybinding.on('⌫', remove);
+
+ d3.select(document)
+ .call(keybinding);
surface.selectAll("*")
.filter(function (d) {
@@ -166,8 +167,9 @@ iD.modes.Select = function(entity, initial) {
var q = iD.util.stringQs(location.hash.substring(1));
location.hash = '#' + iD.util.qsString(_.omit(q, 'id'), true);
+ keybinding.off();
+
surface.on("click.select", null);
- mode.map.keybinding().on('⌫.select', null);
mode.history.on('change.entity-undone', null);
surface.selectAll(".selected")
diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js
index ee145103f..7e74d7b66 100644
--- a/js/id/renderer/map.js
+++ b/js/id/renderer/map.js
@@ -2,7 +2,6 @@ iD.Map = function() {
var connection, history,
dimensions = [],
dispatch = d3.dispatch('move', 'drawn'),
- keybinding = d3.keybinding(),
projection = d3.geo.mercator().scale(1024),
roundedProjection = iD.svg.RoundProjection(projection),
zoom = d3.behavior.zoom()
@@ -49,8 +48,6 @@ iD.Map = function() {
supersurface
.call(tail);
-
- d3.select(document).call(keybinding);
}
function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
@@ -345,12 +342,6 @@ iD.Map = function() {
return map;
};
- map.keybinding = function (_) {
- if (!arguments.length) return keybinding;
- keybinding = _;
- return map;
- };
-
map.background = background;
map.projection = projection;
map.redraw = redraw;
diff --git a/js/lib/d3.keybinding.js b/js/lib/d3.keybinding.js
index 01256aa20..e16ade725 100644
--- a/js/lib/d3.keybinding.js
+++ b/js/lib/d3.keybinding.js
@@ -1,120 +1,197 @@
-d3.keybinding = function() {
- // via https://github.com/keithamus/jwerty/
- // and https://github.com/madrobby/keymaster
- var _keys = {
- // MOD aka toggleable keys
- mods: {
- // Shift key, ⇧
- '⇧': 16,
- // CTRL key, on Mac: ⌃
- '⌃': 17,
- // ALT key, on Mac: ⌥ (Alt)
- '⌥': 18,
- // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
- '⌘': 91
- },
- // Normal keys
- keys: {
- // Backspace key, on Mac: ⌫ (Backspace)
- '⌫': 8, backspace: 8,
- // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
- '⇥': 9, '⇆': 9, tab: 9,
- // Return key, ↩
- '↩': 13, 'return': 13, enter: 13, '⌅': 13,
- // Pause/Break key
- 'pause': 19, 'pause-break': 19,
- // Caps Lock key, ⇪
- '⇪': 20, caps: 20, 'caps-lock': 20,
- // Escape key, on Mac: ⎋, on Windows: Esc
- '⎋': 27, escape: 27, esc: 27,
- // Space key
- space: 32,
- // Page-Up key, or pgup, on Mac: ↖
- '↖': 33, pgup: 33, 'page-up': 33,
- // Page-Down key, or pgdown, on Mac: ↘
- '↘': 34, pgdown: 34, 'page-down': 34,
- // END key, on Mac: ⇟
- '⇟': 35, end: 35,
- // HOME key, on Mac: ⇞
- '⇞': 36, home: 36,
- // Insert key, or ins
- ins: 45, insert: 45,
- // Delete key, on Mac: ⌦ (Delete)
- '⌦': 46, del: 46, 'delete': 46,
- // Left Arrow Key, or ←
- '←': 37, left: 37, 'arrow-left': 37,
- // Up Arrow Key, or ↑
- '↑': 38, up: 38, 'arrow-up': 38,
- // Right Arrow Key, or →
- '→': 39, right: 39, 'arrow-right': 39,
- // Up Arrow Key, or ↓
- '↓': 40, down: 40, 'arrow-down': 40,
- // odities, printing characters that come out wrong:
- // Num-Multiply, or *
- '*': 106, star: 106, asterisk: 106, multiply: 106,
- // Num-Plus or +
- '+': 107, 'plus': 107,
- // Num-Subtract, or -
- '-': 109, subtract: 109,
- // Semicolon
- ';': 186, semicolon:186,
- // = or equals
- '=': 187, 'equals': 187,
- // Comma, or ,
- ',': 188, comma: 188,
- //'-': 189, //???
- // Period, or ., or full-stop
- '.': 190, period: 190, 'full-stop': 190,
- // Slash, or /, or forward-slash
- '/': 191, slash: 191, 'forward-slash': 191,
- // Tick, or `, or back-quote
- '`': 192, tick: 192, 'back-quote': 192,
- // Open bracket, or [
- '[': 219, 'open-bracket': 219,
- // Back slash, or \
- '\\': 220, 'back-slash': 220,
- // Close backet, or ]
- ']': 221, 'close-bracket': 221,
- // Apostraphe, or Quote, or '
- '\'': 222, quote: 222, apostraphe: 222
+/*
+ * This code is licensed under the MIT license.
+ *
+ * Copyright © 2013, iD authors.
+ *
+ * Portions copyright © 2011, Keith Cirkel
+ * See https://github.com/keithamus/jwerty
+ *
+ */
+d3.keybinding = function(namespace) {
+ var bindings = [];
+
+ function matches(binding, event) {
+ for (var p in binding.event) {
+ if (event[p] != binding.event[p])
+ return false;
}
- };
- // To minimise code bloat, add all of the NUMPAD 0-9 keys in a loop
- var i = 95, n = 0;
- while (++i < 106) _keys.keys['num-' + n] = i; ++n;
- // To minimise code bloat, add all of the top row 0-9 keys in a loop
- i = 47, n = 0;
- while (++i < 58) _keys.keys[n] = i; ++n;
- // To minimise code bloat, add all of the F1-F25 keys in a loop
- i = 111, n = 1;
- while (++i < 136) _keys.keys['f' + n] = i; ++n;
- // To minimise code bloat, add all of the letters of the alphabet in a loop
- i = 64;
- while(++i < 91) _keys.keys[String.fromCharCode(i).toLowerCase()] = i;
- var pairs = d3.entries(_keys.keys),
- event = d3.dispatch.apply(d3, d3.keys(_keys.keys));
-
- function keys(selection) {
- selection.on('keydown', function () {
- var tagName = d3.select(d3.event.target).node().tagName;
- if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
- return;
- }
-
- var modifiers = '';
- if (d3.event.shiftKey) modifiers += '⇧';
- if (d3.event.ctrlKey) modifiers += '⌃';
- if (d3.event.altKey) modifiers += '⌥';
- if (d3.event.metaKey) modifiers += '⌘';
-
- pairs.filter(function(d) {
- return d.value === d3.event.keyCode;
- }).forEach(function(d) {
- event[d.key](d3.event, modifiers);
- });
- });
+ return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
}
- return d3.rebind(keys, event, 'on');
+ function capture() {
+ for (var i = 0; i < bindings.length; i++) {
+ var binding = bindings[i];
+ if (matches(binding, d3.event)) {
+ binding.callback();
+ }
+ }
+ }
+
+ function bubble() {
+ var tagName = d3.select(d3.event.target).node().tagName;
+ if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
+ return;
+ }
+ capture();
+ }
+
+ function keybinding(selection) {
+ selection = selection || d3.select(document);
+ selection.on('keydown.capture' + namespace, capture, true);
+ selection.on('keydown.bubble' + namespace, bubble, false);
+ return keybinding;
+ }
+
+ keybinding.off = function(selection) {
+ selection = selection || d3.select(document);
+ selection.on('keydown.capture' + namespace, null);
+ selection.on('keydown.bubble' + namespace, null);
+ return keybinding;
+ };
+
+ keybinding.on = function(code, callback, capture) {
+ var binding = {
+ event: {
+ keyCode: 0,
+ shiftKey: false,
+ ctrlKey: false,
+ altKey: false,
+ metaKey: false
+ },
+ capture: capture,
+ callback: callback
+ };
+
+ code = code.toLowerCase().match(/(?:(?:[^+])+|\+\+|^\+$)/g);
+
+ for (var i = 0; i < code.length; i++) {
+ // Normalise matching errors
+ if (code[i] === '++') code[i] = '+';
+
+ if (code[i] in d3.keybinding.modifierCodes) {
+ binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
+ } else if (code[i] in d3.keybinding.keyCodes) {
+ binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
+ }
+ }
+
+ bindings.push(binding);
+
+ return keybinding;
+ };
+
+ return keybinding;
};
+
+(function () {
+ d3.keybinding.modifierCodes = {
+ // Shift key, ⇧
+ '⇧': 16, shift: 16,
+ // CTRL key, on Mac: ⌃
+ '⌃': 17, ctrl: 17,
+ // ALT key, on Mac: ⌥ (Alt)
+ '⌥': 18, alt: 18, option: 18,
+ // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
+ '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
+ };
+
+ d3.keybinding.modifierProperties = {
+ 16: 'shiftKey',
+ 17: 'ctrlKey',
+ 18: 'altKey',
+ 91: 'metaKey'
+ };
+
+ d3.keybinding.keyCodes = {
+ // Backspace key, on Mac: ⌫ (Backspace)
+ '⌫': 8, backspace: 8,
+ // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
+ '⇥': 9, '⇆': 9, tab: 9,
+ // Return key, ↩
+ '↩': 13, 'return': 13, enter: 13, '⌅': 13,
+ // Pause/Break key
+ 'pause': 19, 'pause-break': 19,
+ // Caps Lock key, ⇪
+ '⇪': 20, caps: 20, 'caps-lock': 20,
+ // Escape key, on Mac: ⎋, on Windows: Esc
+ '⎋': 27, escape: 27, esc: 27,
+ // Space key
+ space: 32,
+ // Page-Up key, or pgup, on Mac: ↖
+ '↖': 33, pgup: 33, 'page-up': 33,
+ // Page-Down key, or pgdown, on Mac: ↘
+ '↘': 34, pgdown: 34, 'page-down': 34,
+ // END key, on Mac: ⇟
+ '⇟': 35, end: 35,
+ // HOME key, on Mac: ⇞
+ '⇞': 36, home: 36,
+ // Insert key, or ins
+ ins: 45, insert: 45,
+ // Delete key, on Mac: ⌦ (Delete)
+ '⌦': 46, del: 46, 'delete': 46,
+ // Left Arrow Key, or ←
+ '←': 37, left: 37, 'arrow-left': 37,
+ // Up Arrow Key, or ↑
+ '↑': 38, up: 38, 'arrow-up': 38,
+ // Right Arrow Key, or →
+ '→': 39, right: 39, 'arrow-right': 39,
+ // Up Arrow Key, or ↓
+ '↓': 40, down: 40, 'arrow-down': 40,
+ // odities, printing characters that come out wrong:
+ // Num-Multiply, or *
+ '*': 106, star: 106, asterisk: 106, multiply: 106,
+ // Num-Plus or +
+ '+': 107, 'plus': 107,
+ // Num-Subtract, or -
+ '-': 109, subtract: 109,
+ // Semicolon
+ ';': 186, semicolon:186,
+ // = or equals
+ '=': 187, 'equals': 187,
+ // Comma, or ,
+ ',': 188, comma: 188,
+ //'-': 189, //???
+ // Period, or ., or full-stop
+ '.': 190, period: 190, 'full-stop': 190,
+ // Slash, or /, or forward-slash
+ '/': 191, slash: 191, 'forward-slash': 191,
+ // Tick, or `, or back-quote
+ '`': 192, tick: 192, 'back-quote': 192,
+ // Open bracket, or [
+ '[': 219, 'open-bracket': 219,
+ // Back slash, or \
+ '\\': 220, 'back-slash': 220,
+ // Close backet, or ]
+ ']': 221, 'close-bracket': 221,
+ // Apostrophe, or Quote, or '
+ '\'': 222, quote: 222, apostrophe: 222
+ };
+
+ // NUMPAD 0-9
+ var i = 95, n = 0;
+ while (++i < 106) {
+ d3.keybinding.keyCodes['num-' + n] = i;
+ ++n;
+ }
+
+ // 0-9
+ i = 47; n = 0;
+ while (++i < 58) {
+ d3.keybinding.keyCodes[n] = i;
+ ++n;
+ }
+
+ // F1-F25
+ i = 111; n = 1;
+ while (++i < 136) {
+ d3.keybinding.keyCodes['f' + n] = i;
+ ++n;
+ }
+
+ // a-z
+ i = 64;
+ while (++i < 91) {
+ d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
+ }
+})();
diff --git a/test/index.html b/test/index.html
index 425258d20..4099f2786 100644
--- a/test/index.html
+++ b/test/index.html
@@ -124,6 +124,8 @@
+
+
diff --git a/test/lib/happen.js b/test/lib/happen.js
index 7c46d238d..8777d883a 100644
--- a/test/lib/happen.js
+++ b/test/lib/happen.js
@@ -19,10 +19,10 @@
evt = new Event(o.type);
evt.keyCode = o.keyCode || 0;
evt.charCode = o.charCode || 0;
- evt.shift = o.shift || false;
- evt.meta = o.meta || false;
- evt.ctrl = o.ctrl || false;
- evt.alt = o.alt || false;
+ evt.shiftKey = o.shiftKey || false;
+ evt.metaKey = o.metaKey || false;
+ evt.ctrlKey = o.ctrlKey || false;
+ evt.altKey = o.altKey || false;
} else {
evt = document.createEvent('KeyboardEvent');
// https://developer.mozilla.org/en/DOM/event.initKeyEvent
@@ -33,10 +33,10 @@
true, // in boolean canBubbleArg,
true, // in boolean cancelableArg,
null, // in nsIDOMAbstractView viewArg, Specifies UIEvent.view. This value may be null.
- o.ctrl || false, // in boolean ctrlKeyArg,
- o.alt || false, // in boolean altKeyArg,
- o.shift || false, // in boolean shiftKeyArg,
- o.meta || false, // in boolean metaKeyArg,
+ o.ctrlKey || false, // in boolean ctrlKeyArg,
+ o.altKey || false, // in boolean altKeyArg,
+ o.shiftKey || false, // in boolean shiftKeyArg,
+ o.metaKey || false, // in boolean metaKeyArg,
o.keyCode || 0, // in unsigned long keyCodeArg,
o.charCode || 0 // in unsigned long charCodeArg);
);
@@ -53,10 +53,10 @@
o.screenY || 0, // screenY
o.clientX || 0, // clientX
o.clientY || 0, // clientY
- o.ctrl || 0, // ctrl
- o.alt || false, // alt
- o.shift || false, // shift
- o.meta || false, // meta
+ o.ctrlKey || 0, // ctrl
+ o.altKey || false, // alt
+ o.shiftKey || false, // shift
+ o.metaKey || false, // meta
o.button || false, // mouse button
null // relatedTarget
);
@@ -65,7 +65,8 @@
x.dispatchEvent(evt);
};
- var shortcuts = ['click', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keyup', 'keypress'],
+ var shortcuts = ['click', 'mousedown', 'mouseup', 'mousemove',
+ 'mouseover', 'mouseout', 'keydown', 'keyup', 'keypress'],
s, i = 0;
while (s = shortcuts[i++]) {
diff --git a/test/spec/lib/d3.keybinding.js b/test/spec/lib/d3.keybinding.js
new file mode 100644
index 000000000..23e1a0e0c
--- /dev/null
+++ b/test/spec/lib/d3.keybinding.js
@@ -0,0 +1,55 @@
+describe("d3.keybinding", function() {
+ var keybinding, spy, input;
+
+ beforeEach(function () {
+ keybinding = d3.keybinding('keybinding-test');
+ spy = sinon.spy();
+ input = d3.select('body')
+ .append('input');
+ });
+
+ afterEach(function () {
+ keybinding.off(d3.select(document));
+ input.remove();
+ });
+
+ describe("#on", function () {
+ it("returns self", function () {
+ expect(keybinding.on('a', spy)).to.equal(keybinding);
+ });
+
+ it("adds a binding for the specified bare key", function () {
+ d3.select(document).call(keybinding.on('A', spy));
+
+ happen.keydown(document, {keyCode: 65, metaKey: true});
+ expect(spy).not.to.have.been.called;
+
+ happen.keydown(document, {keyCode: 65});
+ expect(spy).to.have.been.called;
+ });
+
+ it("adds a binding for the specified key combination", function () {
+ d3.select(document).call(keybinding.on('⌘+A', spy));
+
+ happen.keydown(document, {keyCode: 65});
+ expect(spy).not.to.have.been.called;
+
+ happen.keydown(document, {keyCode: 65, metaKey: true});
+ expect(spy).to.have.been.called;
+ });
+
+ it("does not dispatch when focus is in input elements by default", function () {
+ d3.select(document).call(keybinding.on('A', spy));
+
+ happen.keydown(input.node(), {keyCode: 65});
+ expect(spy).not.to.have.been.called;
+ });
+
+ it("dispatches when focus is in input elements when the capture flag was passed", function () {
+ d3.select(document).call(keybinding.on('A', spy, true));
+
+ happen.keydown(input.node(), {keyCode: 65});
+ expect(spy).to.have.been.called;
+ });
+ });
+});