Git Product home page Git Product logo

keymaster's Introduction

keymaster.js

Keymaster is a simple micro-library for defining and dispatching keyboard shortcuts in web applications.

It has no dependencies.

It’s a work in progress (e.g. beta), so spare me your nerdrage and instead contribute! Patches are welcome, but they are not guaranteed to make it in.

Usage

Include keymaster.js in your web app*, by loading it as usual:

<script src="keymaster.js"></script>

Keymaster has no dependencies and can be used completely standalone. It should not interfere with any JavaScript libraries or frameworks.

*Preferably use a minified version that fits your workflow. You can run make to have UglifyJS (if you have it installed) create a keymaster.min.js file for you.

Defining shortcuts

One global method is exposed, key which defines shortcuts when called directly.

// define short of 'a'
key('a', function(){ alert('you pressed a!') });

// returning false stops the event and prevents default browser events
key('ctrl+r', function(){ alert('stopped reload!'); return false });

// multiple shortcuts that do the same thing
key('⌘+r, ctrl+r', function(){ });

The handler method is called with two arguments set, the keydown event fired, and an object containing, among others, the following two properties:

shortcut: a string that contains the shortcut used, e.g. ctrl+r scope: a string describing the scope (or all)

key('⌘+r, ctrl+r', function(event, handler){
  console.log(handler.shortcut, handler.scope);
});

// "ctrl+r", "all"

Supported keys

Keymaster understands the following modifiers: , shift, option, , alt, ctrl, control, command, and .

The following special keys can be used for shortcuts: backspace, tab, clear, enter, return, esc, escape, space, up, down, left, right, home, end, pageup, pagedown, del, delete and f1 through f19.

Modifier key queries

At any point in time (even in code other than key shortcut handlers), you can query the key object for the state of any keys. This allows easy implementation of things like shift+click handlers. For example, key.shift is true if the shift key is currently pressed.

if(key.shift) alert('shift is pressed, OMGZ!');

Other key queries

At any point in time (even in code other than key shortcut handlers), you can query the key object for the state of any key. This is very helpful for game development using a game loop. For example, key.isPressed(77) is true if the M key is currently pressed.

if(key.isPressed("M")) alert('M key is pressed, can ya believe it!?');
if(key.isPressed(77)) alert('M key is pressed, can ya believe it!?');

You can also get these as an array using...

key.getPressedKeyCodes() // returns an array of key codes currently pressed

Scopes

If you want to reuse the same shortcut for separate areas in your single page app, Keymaster supports switching between scopes. Use the key.setScope method to set scope.

// define shortcuts with a scope
key('o, enter', 'issues', function(){ /* do something */ });
key('o, enter', 'files', function(){ /* do something else */ });

// set the scope (only 'all' and 'issues' shortcuts will be honored)
key.setScope('issues'); // default scope is 'all'

// remove all events that are set in 'issues' scope
key.deleteScope('issues');

Filter key presses

By default, when an INPUT, SELECT or TEXTAREA element is focused, Keymaster doesn't process any shortcuts.

You can change this by overwriting key.filter with a new function. This function is called before Keymaster processes shortcuts, with the keydown event as argument.

If your function returns false, then the no shortcuts will be processed.

Here's the default implementation for reference:

function filter(event){
  var tagName = (event.target || event.srcElement).tagName;
  return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
}

If you only want some shortcuts to work while in an input element, you can change the scope in the key.filter function. Here's an example implementation, setting the scope to either 'input' or 'other'. Don't forget to return true so the any shortcuts get processed.

key.filter = function(event){
  var tagName = (event.target || event.srcElement).tagName;
  key.setScope(/^(INPUT|TEXTAREA|SELECT)$/.test(tagName) ? 'input' : 'other');
  return true;
}

However a more robust way to handle this is to use proper focus and blur event handlers on your input element, and change scopes there as you see fit.

noConflict mode

You can call key.noConflict to remove the key function from global scope and restore whatever key was defined to before Keymaster was loaded. Calling key.noConflict will return the Keymaster key function.

var k = key.noConflict();
k('a', function() { /* ... */ });

key()
// --> TypeError: 'undefined' is not a function

Unbinding shortcuts

Similar to defining shortcuts, they can be unbound using key.unbind.

// unbind 'a' handler
key.unbind('a');

// unbind a key only for a single scope
// when no scope is specified it defaults to the current scope (key.getScope())
key.unbind('o, enter', 'issues');
key.unbind('o, enter', 'files');

Notes

Keymaster should work with any browser that fires keyup and keydown events, and is tested with IE (6+), Safari, Firefox and Chrome.

See http://madrobby.github.com/keymaster/ for a live demo.

CoffeeScript

If you're using CoffeeScript, configuring key shortcuts couldn't be simpler:

key 'a', -> alert('you pressed a!')

key '⌘+r, ctrl+r', ->
  alert 'stopped reload!'
  off

key 'o, enter', 'issues', ->
  whatevs()

alert 'shift is pressed, OMGZ!' if key.shift

Contributing

To contribute, please fork Keymaster, add your patch and tests for it (in the test/ folder) and submit a pull request.

TODOs

  • Finish test suite

Keymaster is (c) 2011-2013 Thomas Fuchs and may be freely distributed under the MIT license. See the MIT-LICENSE file.

keymaster's People

Contributors

agnoster avatar burgestrand avatar burtonjc avatar chetan51 avatar ciaranj avatar clvrobj avatar davidchambers avatar ded avatar devongovett avatar dominictarr avatar dpehrson avatar elidupuis avatar esamattis avatar jlukic avatar livibetter avatar lowe avatar madrobby avatar micsco avatar mimshwright avatar nouzbe avatar nwinkler avatar okonet avatar qiao avatar rafbm avatar ryan-nauman avatar scq avatar smdern avatar snodgrass23 avatar treshugart avatar trevorburnham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

keymaster's Issues

JQuery tmpl + keymaster

Hello.
I had a finished project with lots of Ajax and jquery tmpl (https://github.com/jquery/jquery-tmpl) .

I simply inlcude keymaster.js

On some pages,when calling $("#tmplId").tmpl(data) , keymaster throws an error.

http://screencast.com/t/AU9d7l37pCBs- stack

Problem: during the generation of the template, calling function assignKey with 3 undefined input params. This is true with and without any using keymaster.js. Need only include file.

Has anyone had a similar problem?

Changing scope while in potential shortcut for loop

Recreating instructions:

  1. add a keybind shortcut "z" with scope "x" and callback that changes scope to "y"
  2. use "z" shortcut

"z" shortcuts with scope "y" that where bound after shortcut "z" with scope "x" will execute due to the callback's side effect (changing scope).

"/" key doesn't bind to function

I can call:

key('s', function() { ... }

and the s key works. However, trying to bind slash ('/') to the exact same callback function doesn't get called, or doesn't seem to. In Chrome (latest gold) for OS X, it does nothing, and in Firefox 3.6 it opens the status line quick find. Is that intentional?

Thanks!

Modifier key state tracking is unreliable

Try setting up the following shortcut:

key('t', function(){ alert('Key pressed!') });

Open the page with this shortcut, then open an additional browser tab and switch to it. Press Ctrl+W to close the extra tab, and without letting go of Ctrl, press T to open a new tab. The alert fires, because keymaster tracks the state of modifier keys by looking at keydown events, and it never "saw" the Ctrl press that occurred in the other tab.

My app has no shortcuts that use Ctrl, so I can work around this by setting the filter to return !event.ctrlKey. It seems like this check could just be incorporated into keymaster using the full set of properties (ctrlKey, altKey, shiftKey, metaKey). It wouldn't have to replace the existing modifier tracking, but could serve as an extra safeguard.

[request] pass arguments to function

Like setTimeout:

key('j', go, +1);
key('k', go, -1);

instead of anonymous functions:

key('j', function() { go(+1); });
key('k', function() { go(-1); });

Feature Request - Checking host platform

I'm doing something which I imagine is fairly common, which is writing different selection cases for when "shift", "control"(on windows) or "command" are held down. The problem is that "key.command or key.control" also returns true when a mac's control is pressed, which is causing weird bubbling problems.

It would be great if "Command" and "Control" fired the same event depending on the host platform. Or if there was a way to differentiate between mac control and win control.

Scope change during filtering does not propagate until the next dispatch.

I may be incorrect in assuming that the filter is a place where one can not only check if an element can have keyboard events attached to it, but also change the scope if necessary.

This is useful in situations where one would want to change the scope for a particular input automatically, for example, based on a data- attribute.

key.filter = function(e) {
  var el = event.target || event.srcElement
    , isInput = ['INPUT', 'SELECT', 'TEXTAREA'].indexOf(el.tagName) > -1
    , oldScope = key.getScope()
    , newScope = el.getAttribute('data-scope');

  if (newScope && isInput) {
    if (oldScope !== newScope) {
      var restoreScope = function() {
        key.setScope(oldScope);
        el.removeEventListener('blur', restoreScope);
      };

      key.setScope(newScope);
      el.addEventListener('blur', restoreScope);
    }

    return true;
  }

  return !isInput;
};

The code shown above would change the scope to the scope specified in the data-scope attribute of any element during filtering and restore it when focus is lost. Needless to say, cross-browser considerations as well as internal functions would be taken into account if the above was implemented internally.

The problem when doing something like this, currently, is that since _scope is passed to dispatch(), key.filter() cannot override it during the dispatch call. Only on the second dispatch() call is the new scope passed.

I have a PR and tests for the proposed changes that I will attach to this shortly. I also think that implementing something like the above filter would be extremely useful in decoupling the presentation layer from the actual key mappings. I can submit a separate issue and proposed PR if thought to be appropriate.

Add tags to the repo to make it play nice with bower

It would be nice to have tags in the repo to play nice with bower. What I mean by "nice" is that right now I can't specify a tag so I have to pull the latest code which is not very safe. It would be nice to be able to specify a tag in my bower configure that way I am specific tell bower if I want to upgrade. I don't want code upgraded automatically and potentially having it break something. Generally I upgrade libraries when I know I will be able to do regression test to cover the upgrading and possibly fixing any issues.

Allow first argument to be array instead of string.

So basically allowing key(['⌘+r', 'ctrl+r'], function(){ });
instead of key('⌘+r, ctrl+r', function(){ });

The point of this is allowing the array objects to be separate.
i.e. key( (load settings from server), function(){ });

Flag to accept shortcuts in input fields

I think that it's not uncommon to need shortcuts while in input fields. The most trivial example of it would be command+s.

So I think having to rewrite the filter() function is not ideal.

I was thinking of two ways this could be handled:

  1. The scope defines that it should include input fields. This could be handled like this: key.setScope('issues', true); (where true tells keymaster to handle input fields as well)
  2. The shortcut defines it (this is my preferred alternative). IMO it's pretty obvious normally if a shortcut should work inside an input field or not. command-s, command-p, etc... most shortcuts with modifier keys should be working. So it could be like: key('command+s', function() { }, true)

Of course, those are just the first two ideas that popped in my head. But I think that adding such a flag possibility would really benefit this project.

Central KeyHandler Pattern

Can you add the ability to add a central keyHandler, with the way to check the key (not the keycode)

key(centralFn);

function centralFn(key) {
if (key == 'shift + k') {
}
...
}

Pro: Central Handler can be deactivated (no need to do this for every key Handler)

Key events not captured when page content loses focus then regains it

If I click the location bar, then the webpage again, key events seem to not be captured. The same goes for if I use a keyboard shortcut to launch Developer Tools and turn it off again. The key events get captured again if I switch to a different tab and back, or if I reload the page. Can anyone reproduce this?

I'm on a Mac, using Chrome 13.0.782.218

Add support for special keys

It should be possible to bind key events directly to special keys such as ? or * without having to specify shift+/ or shift+8 which are not consistent across all keyboards.

insert key

Hi, I can't setup shortcut using the 'insert' key.

thanks!

Keyboard + Mouse Combinations

It would be nice to be able to use this for custom keyboard + mouse combinations.

Being able to check the status of modifier keys when a mouse event is fired should take care of it.

unbind the comma

It appears that the logic to catch the comma needs a little work.
key.unbind(','); will kick an error about 188 toUpperCase().
I just decided to pop the extra element, but will break some unit tests I'm sure, like key.unbind('k,,,j');

Original:
function getKeys(key) {
var keys;
key = key.replace(/\s/g, '');
keys = key.split(',');
if ((keys[keys.length - 1]) == '') {
keys[keys.length - 2] += ',';
}
return keys;
}

Modified:
function getKeys(key) {
var keys;
key = key.replace(/\s/g, '');
keys = key.split(',');
if ((keys[keys.length - 1]) == '') {
keys[keys.length - 2] += ',';
keys.pop();
}
return keys;
}

Unbind not working properly with multiple keys

Hi, I've seen a couple of issues similar to this one. They state the problem is fixed.
#88 and #97

I have a very simple test case that shows that problem still exists.

    key('a, b, c', function () {
         console.log('abc');
    });
    key.unbind('a, b, c');

I get a Uncaught TypeError: Object 65 has no method 'toUpperCase' and only a is unbound

In a more realistic test case, there are no errors, but ctrl+r is not unbound.

    key('shift+r, ctrl+r', function (evt) {
         console.log('reload');
        evt.preventDefault();
    });

    key.unbind('shift+r, ctrl+r');

And this one does not unbind anything it seems

    key('right, enter, l', function (evt) {
        console.log('choose');
    });

    key.unbind('right, enter, l');

P.S. description edited twice, but no more =)

Implement dynamic, pattern-matched keybindings

It would be great to be able to define keybindings that match patterns, such as:

key('⌘+([0-9])', function(e, handler) { … });

or

key('⌘+(\d)', function(e, handler) { … });

or

key('⌘+shift+(.*)+(.*)', function(e, handler) { … });

…where handler.matches is an array containing the key(s) that matched the pattern(s).

incorrect keycodes

It seems keymaster is reporting invalid keycodes.

If i log e.which from jquery and keymaster the results are different, keymaster's being the uppercase version

$(body).on('keypress', function (e) {
console.log(e.which);
});
key('a', function (e, handler) {
console.log(e.which);
});
// hit's 'a' on keyboard
// 65
// 97

if you lookup those codes, jquery is return 'a' and keymaster is returning 'A'

⌘ remains active forever

This seems similar to #37.

My pattern in a given app:

  • do ⌘-A in an input field to select all
  • then do ESC: ⌘-ESC will be reported instead of ESC

Unfortunately, I cannot provide a small reproduction so far, but I thought I'd still log my findings in hope I come back to it later or someone else find a clear reproduction.

My current attempt at reproducing this is here:

http://jsfiddle.net/thbar/gPEj4/

scope lost for minified code

The reordering of things in the minified code can lead to scope being confused. I am making panels that hide on ESC. on show, I get the current scope and store it. on hide I restore it. The full code works as expected. The minified version does not "sometimes". its quite odd what it does. I am guessing the _scope is not scoped right in the min code and is being updated by my reset where it is not in the verbose code. Example code is here: http://brian.moonspot.net/labs/dnjs/test.html. I is currently using the non-min version so i can continue testing.

Plus sign

Say I want to make a command like key('ctrl++'). Is this possible?

(So the user has to press ctrl and the plus sign). So for me that is holding ctrl and shift and =

Trigger on function keyboard (f1,f2 ... f12)

Hello,

Maybe it's my misunderstanding of how it works... but I try to attach FUNCTION KEY...and it doesn't work...

Ex:
key('f1', function () {console.log('f1');return false; });
key('f2', function () {console.log('f2');return false; });

(No javascript error message...)

Is it normal ? :)

Thanks

Tagging

It would be cool if you tagged the different versions. This would make it much easier to keep track of the development and to manage it as a dependencie!

Cannot assign to "?"

The only way to assign to "?" is to assign to "shift+/", but that isn't "?" on all keyboards. It seems that a keypress event with charCode 63 is the only way to reliably detect "?" across keyboards.

Redefining shortcut should replace behavior

If you define a function for a shortcut that has already been defined results in possibly unwanted behavior.
Both the old and the new function are performed when the shortcut is activated.

This could be an intended behavior, but according to me, it doesn't seem logical.
If you want to be able to do multiple things with one shortcut, you should define them in one function.

If a shortcut's function doesn't replace the previous one, you should at least be able to remove or reset the shortcut.

Key Combinations

It would be great if I could say: key('g&i', handler) and handler would fire if i was pressed within 1 second of g being pressed (similar to Gmail).

Npm version is pretty old

I was a bit confused because your documentation talks about overwriting the filter function. I installed the library with ender (which showed the same version as in you master branch) and noticed that the filter function wasn't even defined. After poking around a bit I realized that the npm version is pretty old.

I would suggest to put new changes in the develop branch, and also change developer versions to 1.0.3-dev so it's clear which version documentation is about.

indexOf() breaks in IE6-8

The indexOf() calls were introduced in this commit:

ee7621c

You could fix this easily with jQuery or Underscore, but that introduces a dependency that you've tried to avoid.

Feature - Scope Push and Pop

So I think a common need a lot of people have a common need of creating (pushing) and resetting (popping) to a previous scope without myself having to manually remember each individual context.

An example would be I have a dialog, which opens another dialog, obviously if I am in the context of the second dialog and I hit 'esc' for example, I wouldn't want it to close the first dialog and only the second dialog.

Example almost would be key.pushScope() ... and then when I am ready key.popScope().

Also filter out keypresses when focused on contenteditable elements

I just overrode key.filter() to also ignore keypresses when the current element is contenteditable, using this code:

key.filter = function(event) {
  var tagName = (event.target || event.srcElement).tagName;
  var editable = (event.target || event.srcElement).getAttribute('contenteditable');
  return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA' || editable);
}

I'm not sure if that's the best way to implement it, but it seems like this would be a good default, no?

Key Sequences

I'm writing this to start a "high" level discussion about how to implement this.

I'm looking to implement similar functionality to that of GMail. Allowing me to have a global first-press, followed by an action.

IE: I want to be able to press g then h, to go home. However at present this doesn't seem possible with keymaster.

I currently implement something similar by using setScope and having a scope specific for my "go actions", however if I'm already in a scope, and these are intended to be global actions, then it gets difficult to return to the previous scope.

My suggestion is a proposed setTemporaryScope method, whereby it automatically returns to the previous scope after next keypress, or if there is no keypress within a certain time frame (say 1second) then it'll return to the previous scope after time out.

Thoughts?

[BUG] Consistent Version Number

Please add a consistent version number to keymaster (maybe the build task could automate this so that it's always consistent?):

  • the actual tagged version is 1.5
  • the package.json file contains 1.0.2 as a version
  • the keymaster.js and keymaster.min.js contains none :(.

Thank you.

alt state dont restore after alt+tab

On test page i add this code:

key('down, alt+down', function (event) {
      console.log('command+right, or shift+left, or ctrl+shift+alt+d');
      console.log('here is the event: ', event);
      console.log('key.control', key.control);
      console.log('key.ctrl', key.ctrl);
      console.log('key.shift', key.shift);
      console.log('key.alt', key.alt);
      return false; // prevent default && stop propagation
});

then i press Alt+Tab, and Alt+Tab again to return on test page.
Press Down key without Alt and look at console - "key.alt: true".

How to intergrate Virtual Keyboard in dialog Jquery

Can you help me how intergrate Vitual Keyboard in Dialog as Jquery.

I was build code open Dialog and then show Keyboard as Jquery.

When i click button Open Keyboard show error "TypeError: e is undefined [Break On This Error]

...dden"!==this.css("visibility"))&&this.focus(),m.scrollTop=o,this;n?(k=m.selectio."

I have library Jquery 1.7 and Keyboard/script/jquery.keyboard.min.js.

We can help me fix to bug.

Thanks for help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.