ui/vendor/ember-shortcuts.js

211 lines
5.3 KiB
JavaScript

;(function(Ember, $) {
var objectKeys = Object.keys || Ember.keys;
var MODIFIERS = {
'⇧': 16, shift: 16,
'⌥': 18, alt: 18, option: 18,
'⌃': 17, ctrl: 17, control: 17,
'⌘': 91, command: 91
};
var DEFINITIONS = {
backspace: 8, tab: 9, clear: 12,
enter: 13, 'return': 13,
esc: 27, escape: 27, space: 32,
left: 37, up: 38,
right: 39, down: 40,
del: 46, 'delete': 46,
home: 36, end: 35,
pageup: 33, pagedown: 34,
',': 188, '.': 190, '/': 191,
'`': 192, '-': 189, '=': 187,
';': 186, '\'': 222,
'[': 219, ']': 221, '\\': 220
};
for (var n = 1; n < 20; n++) DEFINITIONS['f'+n] = 111 + n;
function code(c) {
return DEFINITIONS[c] || c.toUpperCase().charCodeAt(0);
}
var ENABLED = true;
var PRESSED = {};
var PRESSED_MODS = {};
var SHORTCUTS = {};
function normalize(kc) {
switch (kc) {
case 93: case 224: return 91; // Firefox does ⌘ weird
case 61: return 187; // and `=`
case 173: return 189; // and `-`
default: return kc;
}
}
function isMod(kc) {
return kc === 16 || kc === 17 || kc === 18 || kc === 91;
}
function updatePressedMods(event, kc) {
if (event.shiftKey) PRESSED_MODS[16] = true;
if (event.ctrlKey) PRESSED_MODS[17] = true;
if (event.altKey) PRESSED_MODS[18] = true;
if (event.metaKey) PRESSED_MODS[91] = true;
}
function forEach(array, fn) {
for (var i = 0, len = array.length; i < len; i++) {
fn(array[i]);
}
}
function makeDispatch(router, filters) {
function triggerShortcut(def, event) {
var i, action, handler, infos;
if (!(infos = router.currentHandlerInfos)) return;
for (i = infos.length - 1; i >= 0; i--) {
handler = infos[i].handler;
if (handler.shortcuts && (action = handler.shortcuts[def.raw])) {
handler.send(action, event);
return;
}
}
}
function filter(event) {
for (var i = 0; i < filters.length; i++) {
if (!filters[i](event)) return false;
}
return true;
}
return function dispatchShortcut(event) {
var kc = normalize(event.keyCode);
PRESSED[kc] = true;
if (isMod(kc)) {
PRESSED_MODS[kc] = true;
return;
}
updatePressedMods(event, kc);
if (!ENABLED) return;
if (!filter(event)) return;
if (!(kc in SHORTCUTS)) return;
forEach(SHORTCUTS[kc], function(def) {
if (!modsMatch(def)) return;
Ember.run(function() { triggerShortcut(def, event); });
});
};
}
function clear(event) {
var kc = normalize(event.keyCode);
if (PRESSED[kc]) PRESSED[kc] = undefined;
if (PRESSED_MODS[kc]) PRESSED_MODS[kc] = undefined;
}
function reset() {
PRESSED = {};
PRESSED_MODS = {};
}
function modsMatch(def) {
var mods = def.mods;
return mods[16] === PRESSED_MODS[16] && mods[17] === PRESSED_MODS[17] &&
mods[18] === PRESSED_MODS[18] && mods[91] === PRESSED_MODS[91];
}
function parse(spec) {
var parts = spec.replace(/\s+/g, '').split('+');
var kc = code(parts.pop());
var m, mods = {};
forEach(parts, function(part) {
if ((m = MODIFIERS[part])) mods[m] = true;
});
return { mods: mods, kc: kc, raw: spec };
}
function register(shortcuts) {
forEach(shortcuts, function(spec) {
var def = parse(spec);
if (!(def.kc in SHORTCUTS)) SHORTCUTS[def.kc] = [];
SHORTCUTS[def.kc].push(def);
});
}
var $doc = $(document);
var $win = $(window);
function targetIsNotInput(event) {
var tagName = event.target.tagName;
return (tagName !== 'INPUT') && (tagName !== 'SELECT') && (tagName !== 'TEXTAREA');
}
Ember.Shortcuts = Ember.Object.extend({
concatenatedProperties: ['filters'],
enable: function() { ENABLED = true; },
disable: function() { ENABLED = false; },
filters: [targetIsNotInput],
init: function() {
var router = this.get('router');
var filters = this.get('filters');
var dispatch = makeDispatch(router, filters);
$doc.on('keydown.ember-shortcuts', dispatch);
$doc.on('keyup.ember-shortcuts', clear);
$win.on('focus.ember-shortcuts', reset);
this.enable();
},
router: Ember.computed(function() {
var path = 'router:main';
return Ember.getOwner
? Ember.getOwner(this).lookup(path)._routerMicrolib
: this.container.lookup(path)._routerMicrolib;
}),
unbind: function() {
$doc.off('keydown.ember-shortcuts');
$doc.off('keyup.ember-shortcuts');
$win.off('focus.ember-shortcuts');
},
destroy: function() {
SHORTCUTS = {};
this.unbind();
}
});
Ember.Route.reopen({
mergedProperties: ['shortcuts'],
registerShortcuts: function() {
if (this.shortcuts) register(objectKeys(this.shortcuts));
}.on('init')
});
Ember.onLoad('Ember.Application', function(Application) {
Application.initializer({
name: 'Ember Shortcuts',
initialize: function() {
var application = arguments[1] || arguments[0];
application.register('shortcuts:main', Ember.Shortcuts);
application.inject('route', 'shortcuts', 'shortcuts:main');
application.inject('controller', 'shortcuts', 'shortcuts:main');
}
});
});
}(Ember, Ember.$));