first commit

This commit is contained in:
root
2026-03-14 09:49:00 +00:00
commit 708ff116e1
1958 changed files with 1718027 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
# Plugins
Plugins extend the functionality of Mousetrap. To use a plugin just include the plugin after mousetrap.
```html
<script src="mousetrap.js"></script>
<script src="mousetrap-record.js"></script>
```
## Bind dictionary
Allows you to make multiple bindings in a single ``Mousetrap.bind`` call.
## Global bind
Allows you to set global bindings that work even inside of input fields.
## Pause/unpause
Allows you to temporarily prevent Mousetrap events from firing.
## Record
Allows you to capture a keyboard shortcut or sequence defined by a user.

View File

@@ -0,0 +1,16 @@
# Bind Dictionary
This extension overwrites the default bind behavior and allows you to bind multiple combinations in a single bind call.
Usage looks like:
```javascript
Mousetrap.bind({
'a': function() { console.log('a'); },
'b': function() { console.log('b'); }
});
```
You can optionally pass in ``keypress``, ``keydown`` or ``keyup`` as a second argument.
Other bind calls work the same way as they do by default.

View File

@@ -0,0 +1,39 @@
/**
* Overwrites default Mousetrap.bind method to optionally accept
* an object to bind multiple key events in a single call
*
* You can pass it in like:
*
* Mousetrap.bind({
* 'a': function() { console.log('a'); },
* 'b': function() { console.log('b'); }
* });
*
* And can optionally pass in 'keypress', 'keydown', or 'keyup'
* as a second argument
*
*/
/* global Mousetrap:true */
(function(Mousetrap) {
var _oldBind = Mousetrap.prototype.bind;
var args;
Mousetrap.prototype.bind = function() {
var self = this;
args = arguments;
// normal call
if (typeof args[0] == 'string' || args[0] instanceof Array) {
return _oldBind.call(self, args[0], args[1], args[2]);
}
// object passed in
for (var key in args[0]) {
if (args[0].hasOwnProperty(key)) {
_oldBind.call(self, key, args[0][key], args[1]);
}
}
};
Mousetrap.init();
}) (Mousetrap);

View File

@@ -0,0 +1 @@
(function(b){var c=b.prototype.bind,a;b.prototype.bind=function(){a=arguments;if("string"==typeof a[0]||a[0]instanceof Array)return c.call(this,a[0],a[1],a[2]);for(var b in a[0])a[0].hasOwnProperty(b)&&c.call(this,b,a[0][b],a[1])};b.init()})(Mousetrap);

View File

@@ -0,0 +1,15 @@
# Global Bind
This extension allows you to specify keyboard events that will work anywhere including inside textarea/input fields.
Usage looks like:
```javascript
Mousetrap.bindGlobal('ctrl+s', function() {
_save();
});
```
This means that a keyboard event bound using ``Mousetrap.bind`` will only work outside of form input fields, but using ``Moustrap.bindGlobal`` will work in both places.
If you wanted to create keyboard shortcuts that only work when you are inside a specific textarea you can do that too by creating your own extension.

View File

@@ -0,0 +1,46 @@
/**
* adds a bindGlobal method to Mousetrap that allows you to
* bind specific keyboard shortcuts that will still work
* inside a text input field
*
* usage:
* Mousetrap.bindGlobal('ctrl+s', _saveChanges);
*/
/* global Mousetrap:true */
(function(Mousetrap) {
if (! Mousetrap) {
return;
}
var _globalCallbacks = {};
var _originalStopCallback = Mousetrap.prototype.stopCallback;
Mousetrap.prototype.stopCallback = function(e, element, combo, sequence) {
var self = this;
if (self.paused) {
return true;
}
if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
return false;
}
return _originalStopCallback.call(self, e, element, combo);
};
Mousetrap.prototype.bindGlobal = function(keys, callback, action) {
var self = this;
self.bind(keys, callback, action);
if (keys instanceof Array) {
for (var i = 0; i < keys.length; i++) {
_globalCallbacks[keys[i]] = true;
}
return;
}
_globalCallbacks[keys] = true;
};
Mousetrap.init();
}) (typeof Mousetrap !== "undefined" ? Mousetrap : undefined);

View File

@@ -0,0 +1 @@
(function(a){var c={},d=a.prototype.stopCallback;a.prototype.stopCallback=function(e,b,a,f){return this.paused?!0:c[a]||c[f]?!1:d.call(this,e,b,a)};a.prototype.bindGlobal=function(a,b,d){this.bind(a,b,d);if(a instanceof Array)for(b=0;b<a.length;b++)c[a[b]]=!0;else c[a]=!0};a.init()})(Mousetrap);

View File

@@ -0,0 +1,13 @@
# Pause/unpause
This extension allows Mousetrap to be paused and unpaused without having to reset keyboard shortcuts and rebind them.
Usage looks like:
```javascript
// stop Mousetrap events from firing
Mousetrap.pause();
// allow Mousetrap events to fire again
Mousetrap.unpause();
```

View File

@@ -0,0 +1,31 @@
/**
* adds a pause and unpause method to Mousetrap
* this allows you to enable or disable keyboard shortcuts
* without having to reset Mousetrap and rebind everything
*/
/* global Mousetrap:true */
(function(Mousetrap) {
var _originalStopCallback = Mousetrap.prototype.stopCallback;
Mousetrap.prototype.stopCallback = function(e, element, combo) {
var self = this;
if (self.paused) {
return true;
}
return _originalStopCallback.call(self, e, element, combo);
};
Mousetrap.prototype.pause = function() {
var self = this;
self.paused = true;
};
Mousetrap.prototype.unpause = function() {
var self = this;
self.paused = false;
};
Mousetrap.init();
}) (Mousetrap);

View File

@@ -0,0 +1 @@
(function(a){var b=a.prototype.stopCallback;a.prototype.stopCallback=function(a,c,d){return this.paused?!0:b.call(this,a,c,d)};a.prototype.pause=function(){this.paused=!0};a.prototype.unpause=function(){this.paused=!1};a.init()})(Mousetrap);

View File

@@ -0,0 +1,16 @@
# Record
This extension lets you use Mousetrap to record keyboard sequences and play them back:
```html
<button onclick="recordSequence()">Record</button>
<script>
function recordSequence() {
Mousetrap.record(function(sequence) {
// sequence is an array like ['ctrl+k', 'c']
alert('You pressed: ' + sequence.join(' '));
});
}
</script>
```

View File

@@ -0,0 +1,205 @@
/**
* This extension allows you to record a sequence using Mousetrap.
*
* @author Dan Tao <daniel.tao@gmail.com>
*/
(function(Mousetrap) {
/**
* the sequence currently being recorded
*
* @type {Array}
*/
var _recordedSequence = [],
/**
* a callback to invoke after recording a sequence
*
* @type {Function|null}
*/
_recordedSequenceCallback = null,
/**
* a list of all of the keys currently held down
*
* @type {Array}
*/
_currentRecordedKeys = [],
/**
* temporary state where we remember if we've already captured a
* character key in the current combo
*
* @type {boolean}
*/
_recordedCharacterKey = false,
/**
* a handle for the timer of the current recording
*
* @type {null|number}
*/
_recordTimer = null,
/**
* the original handleKey method to override when Mousetrap.record() is
* called
*
* @type {Function}
*/
_origHandleKey = Mousetrap.prototype.handleKey;
/**
* handles a character key event
*
* @param {string} character
* @param {Array} modifiers
* @param {Event} e
* @returns void
*/
function _handleKey(character, modifiers, e) {
var self = this;
if (!self.recording) {
_origHandleKey.apply(self, arguments);
return;
}
// remember this character if we're currently recording a sequence
if (e.type == 'keydown') {
if (character.length === 1 && _recordedCharacterKey) {
_recordCurrentCombo();
}
for (i = 0; i < modifiers.length; ++i) {
_recordKey(modifiers[i]);
}
_recordKey(character);
// once a key is released, all keys that were held down at the time
// count as a keypress
} else if (e.type == 'keyup' && _currentRecordedKeys.length > 0) {
_recordCurrentCombo();
}
}
/**
* marks a character key as held down while recording a sequence
*
* @param {string} key
* @returns void
*/
function _recordKey(key) {
var i;
// one-off implementation of Array.indexOf, since IE6-9 don't support it
for (i = 0; i < _currentRecordedKeys.length; ++i) {
if (_currentRecordedKeys[i] === key) {
return;
}
}
_currentRecordedKeys.push(key);
if (key.length === 1) {
_recordedCharacterKey = true;
}
}
/**
* marks whatever key combination that's been recorded so far as finished
* and gets ready for the next combo
*
* @returns void
*/
function _recordCurrentCombo() {
_recordedSequence.push(_currentRecordedKeys);
_currentRecordedKeys = [];
_recordedCharacterKey = false;
_restartRecordTimer();
}
/**
* ensures each combo in a sequence is in a predictable order and formats
* key combos to be '+'-delimited
*
* modifies the sequence in-place
*
* @param {Array} sequence
* @returns void
*/
function _normalizeSequence(sequence) {
var i;
for (i = 0; i < sequence.length; ++i) {
sequence[i].sort(function(x, y) {
// modifier keys always come first, in alphabetical order
if (x.length > 1 && y.length === 1) {
return -1;
} else if (x.length === 1 && y.length > 1) {
return 1;
}
// character keys come next (list should contain no duplicates,
// so no need for equality check)
return x > y ? 1 : -1;
});
sequence[i] = sequence[i].join('+');
}
}
/**
* finishes the current recording, passes the recorded sequence to the stored
* callback, and sets Mousetrap.handleKey back to its original function
*
* @returns void
*/
function _finishRecording() {
if (_recordedSequenceCallback) {
_normalizeSequence(_recordedSequence);
_recordedSequenceCallback(_recordedSequence);
}
// reset all recorded state
_recordedSequence = [];
_recordedSequenceCallback = null;
_currentRecordedKeys = [];
}
/**
* called to set a 1 second timeout on the current recording
*
* this is so after each key press in the sequence the recording will wait for
* 1 more second before executing the callback
*
* @returns void
*/
function _restartRecordTimer() {
clearTimeout(_recordTimer);
_recordTimer = setTimeout(_finishRecording, 1000);
}
/**
* records the next sequence and passes it to a callback once it's
* completed
*
* @param {Function} callback
* @returns void
*/
Mousetrap.prototype.record = function(callback) {
var self = this;
self.recording = true;
_recordedSequenceCallback = function() {
self.recording = false;
callback.apply(self, arguments);
};
};
Mousetrap.prototype.handleKey = function() {
var self = this;
_handleKey.apply(self, arguments);
};
Mousetrap.init();
})(Mousetrap);

View File

@@ -0,0 +1,2 @@
(function(d){function n(b,a,h){if(this.recording)if("keydown"==h.type){1===b.length&&g&&k();for(i=0;i<a.length;++i)l(a[i]);l(b)}else"keyup"==h.type&&0<c.length&&k();else p.apply(this,arguments)}function l(b){var a;for(a=0;a<c.length;++a)if(c[a]===b)return;c.push(b);1===b.length&&(g=!0)}function k(){e.push(c);c=[];g=!1;clearTimeout(m);m=setTimeout(q,1E3)}function r(b){var a;for(a=0;a<b.length;++a)b[a].sort(function(a,b){return 1<a.length&&1===b.length?-1:1===a.length&&1<b.length?1:a>b?1:-1}),b[a]=
b[a].join("+")}function q(){f&&(r(e),f(e));e=[];f=null;c=[]}var e=[],f=null,c=[],g=!1,m=null,p=d.prototype.handleKey;d.prototype.record=function(b){var a=this;a.recording=!0;f=function(){a.recording=!1;b.apply(a,arguments)}};d.prototype.handleKey=function(){n.apply(this,arguments)};d.init()})(Mousetrap);

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>Jelly</title>
<meta charset=utf-8>
<link href="jelly.css" rel="stylesheet">
</head>
<body>
<h1>Jelly</h1>
<h2>For testing the <strong>record</strong> extension</h2>
<p>Click "Record" to test recording a sequence.</p>
<button class="test-record">Record</button>
<div class="test-record-result"></div>
<script type="text/javascript" src="../../../tests/libs/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="../../../mousetrap.js"></script>
<script type="text/javascript" src="../mousetrap-record.js"></script>
<script type="text/javascript" src="jelly.js"></script>
<script type="text/javascript">
Jelly.spread();
</script>
</body>
</html>

View File

@@ -0,0 +1,18 @@
body {
font-family: helvetica, arial, sans-serif;
line-height: 20px;
}
kbd {
background-color: #ccc;
display: inline-block;
padding: 0.5ex 1em;
}
.test-record-result {
margin-top: 20px;
}
.test-record-result span:nth-child(n+2) {
margin-left: 10px;
}

View File

@@ -0,0 +1,53 @@
/**
* Peanut butter goes great with jelly.
*
* @author Dan Tao <daniel.tao@gmail.com>
*/
var Jelly = (function() {
var recordButton = $("button.test-record"),
recordResult = $("div.test-record-result");
function _formatSequenceAsHtml(sequence) {
var combos = [],
i;
for (i = 0; i < sequence.length; ++i) {
combos.push('<span>' + _formatKeysAsHtml(sequence[i].split('+')) + '</span>');
}
return combos.join(' ');
}
function _formatKeysAsHtml(keys) {
var htmlKeys = [],
i;
for (i = 0; i < keys.length; ++i) {
htmlKeys.push('<kbd>' + keys[i] + '</kbd>');
}
return htmlKeys.join('+');
}
function _prepareRecordTest() {
recordButton.prop('disabled', true);
recordButton.text('Recording');
Mousetrap.record(function(sequence) {
recordResult.html(_formatSequenceAsHtml(sequence));
recordButton.prop('disabled', false);
recordButton.text('Record');
});
// take focus away from the button so that Mousetrap will actually
// capture keystrokes
recordButton.blur();
}
return {
spread: function() {
recordButton.click(_prepareRecordTest);
}
};
})();