mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 15:45:55 +08:00
There this works
This commit is contained in:
parent
3a48cc8b7f
commit
c7054ff56c
25
README.md
25
README.md
|
@ -6,7 +6,7 @@
|
||||||
<h1>SnappyMail</h1>
|
<h1>SnappyMail</h1>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
Simple, modern & fast web-based email client.
|
Simple, modern, lightweight & fast web-based email client.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The drastically upgraded & secured fork of <a href="https://github.com/RainLoop/rainloop-webmail">RainLoop Webmail Community edition</a>.
|
The drastically upgraded & secured fork of <a href="https://github.com/RainLoop/rainloop-webmail">RainLoop Webmail Community edition</a>.
|
||||||
|
@ -85,6 +85,7 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill
|
||||||
* Replaced knockout-sortable with native HTML5 drag&drop
|
* Replaced knockout-sortable with native HTML5 drag&drop
|
||||||
* Replaced simplestatemanager with @media
|
* Replaced simplestatemanager with @media
|
||||||
* Replaced inputosaurus to native
|
* Replaced inputosaurus to native
|
||||||
|
* Replaced keymaster with own shortcuts handler
|
||||||
* Removed pikaday
|
* Removed pikaday
|
||||||
* Removed underscore
|
* Removed underscore
|
||||||
* Removed polyfills
|
* Removed polyfills
|
||||||
|
@ -102,22 +103,22 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill
|
||||||
|
|
||||||
|js/* |1.14.0 |native |
|
|js/* |1.14.0 |native |
|
||||||
|----------- |--------: |--------: |
|
|----------- |--------: |--------: |
|
||||||
|admin.js |2.130.942 | 850.202 |
|
|admin.js |2.130.942 | 844.753 |
|
||||||
|app.js |4.184.455 |2.488.837 |
|
|app.js |4.184.455 |2.487.401 |
|
||||||
|boot.js | 671.522 | 5.285 |
|
|boot.js | 671.522 | 5.285 |
|
||||||
|libs.js | 647.614 | 250.948 |
|
|libs.js | 647.614 | 246.085 |
|
||||||
|polyfills.js | 325.834 | 0 |
|
|polyfills.js | 325.834 | 0 |
|
||||||
|TOTAL |7.960.367 |3.595.272 |
|
|TOTAL |7.960.367 |3.583.524 |
|
||||||
|
|
||||||
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
||||||
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
||||||
|admin.min.js | 252.147 | 116.626 | 73.657 | 33.343 | 28.869 |
|
|admin.min.js | 252.147 | 116.068 | 73.657 | 33.279 | 28.711 |
|
||||||
|app.min.js | 511.202 | 339.176 |140.462 | 89.271 | 72.393 |
|
|app.min.js | 511.202 | 339.263 |140.462 | 89.051 | 72.194 |
|
||||||
|boot.min.js | 66.007 | 2.935 | 22.567 | 1.510 | 1.285 |
|
|boot.min.js | 66.007 | 2.935 | 22.567 | 1.510 | 1.285 |
|
||||||
|libs.min.js | 572.545 | 148.303 |176.720 | 52.206 | 46.472 |
|
|libs.min.js | 572.545 | 147.019 |176.720 | 51.583 | 45.825 |
|
||||||
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
||||||
|TOTAL |1.434.353 | 607.040 |424.718 |176.330 |149.019 |
|
|TOTAL |1.434.353 | 605.285 |424.718 |175.423 |148.015 |
|
||||||
|TOTAL (no admin) |1.182.206 | 490.414 |351.061 |142.987 |120.150 |
|
|TOTAL (no admin) |1.182.206 | 489.217 |351.061 |142.144 |119.304 |
|
||||||
|
|
||||||
For a user its around 58% smaller and faster than traditional RainLoop.
|
For a user its around 58% smaller and faster than traditional RainLoop.
|
||||||
|
|
||||||
|
@ -144,8 +145,8 @@ For a user its around 58% smaller and faster than traditional RainLoop.
|
||||||
|
|
||||||
|css/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
|css/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
||||||
|-------------- |-------: |-------: |------: |------: |------: |
|
|-------------- |-------: |-------: |------: |------: |------: |
|
||||||
|app.css | 340.334 | 190.855 | 46,959 | 29.156 | 24.561 |
|
|app.css | 340.334 | 190.086 | 46,959 | 29.415 | 24.717 |
|
||||||
|app.min.css | 274.791 | 157.619 | 39.618 | 26.336 | 22.667 |
|
|app.min.css | 274.791 | 156.076 | 39.618 | 26.034 | 22.418 |
|
||||||
|boot.css | | 2.534 | | 837 | 668 |
|
|boot.css | | 2.534 | | 837 | 668 |
|
||||||
|boot.min.css | | 2.055 | | 732 | 560 |
|
|boot.min.css | | 2.055 | | 732 | 560 |
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ class AbstractApp {
|
||||||
$doc.addEventListener('keypress', fn);
|
$doc.addEventListener('keypress', fn);
|
||||||
$doc.addEventListener('click', fn);
|
$doc.addEventListener('click', fn);
|
||||||
|
|
||||||
shortcuts.add(['escape','enter'], '', KeyState.All, () => rl.Dropdowns.detectVisibility());
|
shortcuts.add('escape,enter', '', KeyState.All, () => rl.Dropdowns.detectVisibility());
|
||||||
}
|
}
|
||||||
|
|
||||||
remote() {
|
remote() {
|
||||||
|
|
|
@ -286,11 +286,11 @@ class Selector {
|
||||||
shortcuts.add('arrowup', 'meta', keyScope, () => false);
|
shortcuts.add('arrowup', 'meta', keyScope, () => false);
|
||||||
shortcuts.add('arrowdown', 'meta', keyScope, () => false);
|
shortcuts.add('arrowdown', 'meta', keyScope, () => false);
|
||||||
|
|
||||||
shortcuts.add(['arrowup','arrowdown'], 'shift', keyScope, event => {
|
shortcuts.add('arrowup,arrowdown', 'shift', keyScope, event => {
|
||||||
this.newSelectPosition(event.key, true);
|
this.newSelectPosition(event.key, true);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
shortcuts.add(['arrowup','arrowdown','home','end','pageup','pagedown','insert','space'], '', keyScope, event => {
|
shortcuts.add('arrowup,arrowdown,home,end,pageup,pagedown,insert,space', '', keyScope, event => {
|
||||||
this.newSelectPosition(event.key, false);
|
this.newSelectPosition(event.key, false);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,7 @@ class MenuSettingsAdminView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuild(dom) {
|
onBuild(dom) {
|
||||||
shortcuts.add(['arrowup','arrowdown'], '', KeyState.Settings,
|
shortcuts.add('arrowup,arrowdown', '', KeyState.Settings,
|
||||||
settingsMenuKeysHandler(dom.querySelectorAll('.b-admin-menu .e-item')));
|
settingsMenuKeysHandler(dom.querySelectorAll('.b-admin-menu .e-item')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ class AskPopupView extends AbstractViewNext {
|
||||||
|
|
||||||
onBuild() {
|
onBuild() {
|
||||||
// shortcuts.add('tab', 'shift', KeyState.PopupAsk, () => {
|
// shortcuts.add('tab', 'shift', KeyState.PopupAsk, () => {
|
||||||
shortcuts.add(['tab','arrowright','arrowleft'], '', KeyState.PopupAsk, () => {
|
shortcuts.add('tab,arrowright,arrowleft', '', KeyState.PopupAsk, () => {
|
||||||
let btn = this.querySelector('.buttonYes');
|
let btn = this.querySelector('.buttonYes');
|
||||||
if (btn.matches(':focus')) {
|
if (btn.matches(':focus')) {
|
||||||
btn = this.querySelector('.buttonNo');
|
btn = this.querySelector('.buttonNo');
|
||||||
|
|
|
@ -303,7 +303,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuild() {
|
onBuild() {
|
||||||
// shortcuts.add(('tab', 'shift', KeyState.PopupComposeOpenPGP, () => {
|
// shortcuts.add('tab', 'shift', KeyState.PopupComposeOpenPGP, () => {
|
||||||
shortcuts.add('tab', '', KeyState.PopupComposeOpenPGP, () => {
|
shortcuts.add('tab', '', KeyState.PopupComposeOpenPGP, () => {
|
||||||
let btn = this.querySelector('.inputPassword');
|
let btn = this.querySelector('.inputPassword');
|
||||||
if (btn.matches(':focus')) {
|
if (btn.matches(':focus')) {
|
||||||
|
|
|
@ -611,7 +611,7 @@ class ContactsPopupView extends AbstractViewNext {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcuts.add(['c','w'], '', KeyState.ContactList, () => {
|
shortcuts.add('c,w', '', KeyState.ContactList, () => {
|
||||||
this.newMessageCommand();
|
this.newMessageCommand();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ class KeyboardShortcutsHelpPopupView extends AbstractViewNext {
|
||||||
dom.querySelectorAll('a[data-toggle="tab"]').forEach(node => node.Tab || new BSN.Tab(node));
|
dom.querySelectorAll('a[data-toggle="tab"]').forEach(node => node.Tab || new BSN.Tab(node));
|
||||||
|
|
||||||
// shortcuts.add('tab', 'shift',
|
// shortcuts.add('tab', 'shift',
|
||||||
shortcuts.add(['tab','arrowleft','arrowright'], '',
|
shortcuts.add('tab,arrowleft,arrowright', '',
|
||||||
KeyState.PopupKeyboardShortcutsHelp,
|
KeyState.PopupKeyboardShortcutsHelp,
|
||||||
((event, handler)=>{
|
((event, handler)=>{
|
||||||
if (event && handler) {
|
if (event && handler) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class MessageOpenPgpPopupView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuild(oDom) {
|
onBuild(oDom) {
|
||||||
// shortcuts.add('tab','shift', KeyState.PopupMessageOpenPGP, () => {
|
// shortcuts.add('tab', 'shift', KeyState.PopupMessageOpenPGP, () => {
|
||||||
shortcuts.add('tab', '', KeyState.PopupMessageOpenPGP, () => {
|
shortcuts.add('tab', '', KeyState.PopupMessageOpenPGP, () => {
|
||||||
let btn = this.querySelector('.inputPassword');
|
let btn = this.querySelector('.inputPassword');
|
||||||
if (btn.matches(':focus')) {
|
if (btn.matches(':focus')) {
|
||||||
|
|
|
@ -121,7 +121,7 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||||
el && fSelectFolder(el, event, false);
|
el && fSelectFolder(el, event, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcuts.add(['arrowup','arrowdown'], '', KeyState.FolderList, event => {
|
shortcuts.add('arrowup,arrowdown', '', KeyState.FolderList, event => {
|
||||||
let items = [], index = 0;
|
let items = [], index = 0;
|
||||||
dom.querySelectorAll('.b-folders .e-item .e-link:not(.hidden)').forEach(node => {
|
dom.querySelectorAll('.b-folders .e-item .e-link:not(.hidden)').forEach(node => {
|
||||||
if (node.offsetHeight || node.getClientRects().length) {
|
if (node.offsetHeight || node.getClientRects().length) {
|
||||||
|
@ -168,7 +168,7 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
||||||
});
|
});
|
||||||
|
|
||||||
// shortcuts.add('tab', 'shift', KeyState.FolderList, () => {
|
// shortcuts.add('tab', 'shift', KeyState.FolderList, () => {
|
||||||
shortcuts.add(['escape','tab','arrowright'], '', KeyState.FolderList, () => {
|
shortcuts.add('escape,tab,arrowright', '', KeyState.FolderList, () => {
|
||||||
AppStore.focusedState(Focused.MessageList);
|
AppStore.focusedState(Focused.MessageList);
|
||||||
moveAction(false);
|
moveAction(false);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -786,7 +786,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
if (Settings.capa(Capa.Composer)) {
|
if (Settings.capa(Capa.Composer)) {
|
||||||
// write/compose (open compose popup)
|
// write/compose (open compose popup)
|
||||||
shortcuts.add(['w','c'], '', [KeyState.MessageList, KeyState.MessageView], () => {
|
shortcuts.add('w,c', '', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||||
showScreenPopup(require('View/Popup/Compose'));
|
showScreenPopup(require('View/Popup/Compose'));
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -877,7 +877,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
AppStore.focusedState(Focused.FolderList);
|
AppStore.focusedState(Focused.FolderList);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
shortcuts.add(['tab','arrowright'], '', KeyState.MessageList, () => {
|
shortcuts.add('tab,arrowright', '', KeyState.MessageList, () => {
|
||||||
this.message() && AppStore.focusedState(Focused.MessageView);
|
this.message() && AppStore.focusedState(Focused.MessageView);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -547,7 +547,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
initShortcuts() {
|
initShortcuts() {
|
||||||
// exit fullscreen, back
|
// exit fullscreen, back
|
||||||
shortcuts.add(['escape','backspace'], '', KeyState.MessageView, this.escShortcuts.bind(this));
|
shortcuts.add('escape,backspace', '', KeyState.MessageView, this.escShortcuts.bind(this));
|
||||||
|
|
||||||
// fullscreen
|
// fullscreen
|
||||||
shortcuts.add('enter', '', KeyState.MessageView, () => {
|
shortcuts.add('enter', '', KeyState.MessageView, () => {
|
||||||
|
@ -602,12 +602,12 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcuts.add(['arrowup','arrowleft'], 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
shortcuts.add('arrowup,arrowleft', 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||||
this.goUpCommand();
|
this.goUpCommand();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
shortcuts.add(['arrowdown','arrowright'], 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
shortcuts.add('arrowdown,arrowright', 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||||
this.goDownCommand();
|
this.goDownCommand();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,7 +32,7 @@ class MenuSettingsUserView extends AbstractViewNext {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcuts.add(['ArrowUp','ArrowDown'], '', KeyState.Settings,
|
shortcuts.add('arrowup,arrowdown', '', KeyState.Settings,
|
||||||
settingsMenuKeysHandler(dom.querySelectorAll('.b-settings-menu .e-item')));
|
settingsMenuKeysHandler(dom.querySelectorAll('.b-settings-menu .e-item')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ const
|
||||||
doc = document,
|
doc = document,
|
||||||
meta = /Mac OS X/.test(navigator.userAgent) ? 'meta' : 'ctrl',
|
meta = /Mac OS X/.test(navigator.userAgent) ? 'meta' : 'ctrl',
|
||||||
_scopes = {},
|
_scopes = {},
|
||||||
toArray = v => Array.isArray(v) ? v : [v],
|
toArray = v => Array.isArray(v) ? v : v.split(/\s*,\s*/),
|
||||||
|
|
||||||
fire = (actions, event) => {
|
fire = (actions, event) => {
|
||||||
let modifiers = [];
|
let modifiers = [];
|
||||||
|
@ -20,7 +20,7 @@ const
|
||||||
actions[modifiers].forEach(cmd => {
|
actions[modifiers].forEach(cmd => {
|
||||||
try {
|
try {
|
||||||
// call the handler and stop the event if neccessary
|
// call the handler and stop the event if neccessary
|
||||||
if (!event.defaultPrevented && cmd(event) === false) {
|
if (!event.defaultPrevented && cmd(event) === false) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
0
vendors/keymaster/.code-changed
vendored
0
vendors/keymaster/.code-changed
vendored
20
vendors/keymaster/MIT-LICENSE
vendored
20
vendors/keymaster/MIT-LICENSE
vendored
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2011-2013 Thomas Fuchs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
212
vendors/keymaster/README.markdown
vendored
212
vendors/keymaster/README.markdown
vendored
|
@ -1,212 +0,0 @@
|
||||||
# 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:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 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`)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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...
|
|
||||||
```javascript
|
|
||||||
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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 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'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 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:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
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`.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 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/](http://madrobby.github.com/keymaster/) for a live demo.
|
|
||||||
|
|
||||||
|
|
||||||
## CoffeeScript
|
|
||||||
|
|
||||||
If you're using CoffeeScript, configuring key shortcuts couldn't be simpler:
|
|
||||||
|
|
||||||
```coffeescript
|
|
||||||
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.
|
|
167
vendors/keymaster/keymaster.js
vendored
167
vendors/keymaster/keymaster.js
vendored
|
@ -1,167 +0,0 @@
|
||||||
// keymaster.js
|
|
||||||
// (c) 2011-2013 Thomas Fuchs
|
|
||||||
// keymaster.js may be freely distributed under the MIT license.
|
|
||||||
|
|
||||||
(global=>{
|
|
||||||
var k,
|
|
||||||
_handlers = {},
|
|
||||||
_mods = { 16: false, 18: false, 17: false, 91: false },
|
|
||||||
_scope = 'all',
|
|
||||||
// modifier keys
|
|
||||||
_MODIFIERS = {
|
|
||||||
'⇧': 16, shift: 16,
|
|
||||||
'⌥': 18, alt: 18, option: 18,
|
|
||||||
'⌃': 17, ctrl: 17, control: 17,
|
|
||||||
'⌘': 91, command: 91
|
|
||||||
},
|
|
||||||
// special keys
|
|
||||||
_MAP = {
|
|
||||||
backspace: 8, tab: 9, clear: 12,
|
|
||||||
enter: 13, 'return': 13,
|
|
||||||
esc: 27, escape: 27, space: 32,
|
|
||||||
left: 37, up: 38,
|
|
||||||
right: 39, down: 40,
|
|
||||||
insert: 45,
|
|
||||||
del: 46, 'delete': 46,
|
|
||||||
home: 36, end: 35,
|
|
||||||
pageup: 33, pagedown: 34,
|
|
||||||
',': 188, '.': 190, '/': 191,
|
|
||||||
'`': 192, '-': 189, '=': 187,
|
|
||||||
';': 186, '\'': 222,
|
|
||||||
'[': 219, ']': 221, '\\': 220
|
|
||||||
},
|
|
||||||
code = x => _MAP[x] || x.toUpperCase().charCodeAt(0),
|
|
||||||
_downKeys = [];
|
|
||||||
|
|
||||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
|
|
||||||
|
|
||||||
var modifierMap = {
|
|
||||||
16:'shiftKey',
|
|
||||||
18:'altKey',
|
|
||||||
17:'ctrlKey',
|
|
||||||
91:'metaKey'
|
|
||||||
};
|
|
||||||
|
|
||||||
// parse and assign shortcut
|
|
||||||
function assignKey(key, scope, method){
|
|
||||||
key = key.replace(/\s/g, '');
|
|
||||||
var keys = key.split(','), mods;
|
|
||||||
if ((keys[keys.length - 1]) == '') {
|
|
||||||
keys[keys.length - 2] += ',';
|
|
||||||
}
|
|
||||||
if (method === undefined) {
|
|
||||||
method = scope;
|
|
||||||
scope = 'all';
|
|
||||||
}
|
|
||||||
|
|
||||||
// for each shortcut
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
// set modifier keys if any
|
|
||||||
mods = [];
|
|
||||||
key = keys[i].split('+');
|
|
||||||
if (key.length > 1){
|
|
||||||
mods = key.slice(0, key.length - 1);
|
|
||||||
mods.forEach((mod, mi) => mods[mi] = _MODIFIERS[mod]);
|
|
||||||
key = [key[key.length-1]];
|
|
||||||
}
|
|
||||||
// convert to keycode and...
|
|
||||||
key = code(key[0]);
|
|
||||||
// ...store handler
|
|
||||||
if (!(key in _handlers)) _handlers[key] = [];
|
|
||||||
|
|
||||||
if (typeof scope !== 'string' && scope.length && typeof scope[0] === 'string') {
|
|
||||||
scope.forEach(item => {
|
|
||||||
_handlers[key].push({ shortcut: keys[i], scope: item, method: method, key: keys[i], mods: mods });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize key.<modifier> to false
|
|
||||||
for(k in _MODIFIERS) assignKey[k] = false;
|
|
||||||
|
|
||||||
const getScope = () => _scope || 'all';
|
|
||||||
|
|
||||||
// set the handlers globally on document
|
|
||||||
document.addEventListener('keydown', event => {
|
|
||||||
var key = event.keyCode, k, modifiersMatch, scope;
|
|
||||||
|
|
||||||
if (!_downKeys.includes(key)) {
|
|
||||||
_downKeys.push(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if a modifier key, set the key.<modifierkeyname> property to true and return
|
|
||||||
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
|
|
||||||
if(key in _mods) {
|
|
||||||
_mods[key] = true;
|
|
||||||
// 'assignKey' from inside this closure is exported to window.key
|
|
||||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
|
||||||
|
|
||||||
// see if we need to ignore the keypress (filter() can can be overridden)
|
|
||||||
// by default ignore key presses if a select, textarea, or input is focused
|
|
||||||
if(!assignKey.filter.call(this, event)) return;
|
|
||||||
|
|
||||||
// abort if no potentially matching shortcuts found
|
|
||||||
if (!(key in _handlers)) return;
|
|
||||||
|
|
||||||
scope = getScope();
|
|
||||||
|
|
||||||
// for each potential shortcut
|
|
||||||
_handlers[key].forEach(handler => {
|
|
||||||
// see if it's in the current scope
|
|
||||||
if(handler.scope == scope || handler.scope == 'all'){
|
|
||||||
// check if modifiers match if any
|
|
||||||
modifiersMatch = handler.mods.length > 0;
|
|
||||||
for(k in _mods)
|
|
||||||
if((!_mods[k] && handler.mods.includes(+k)) ||
|
|
||||||
(_mods[k] && !handler.mods.includes(+k))) modifiersMatch = false;
|
|
||||||
// call the handler and stop the event if neccessary
|
|
||||||
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
|
|
||||||
if(handler.method(event, handler)===false){
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// unset modifier keys on keyup
|
|
||||||
document.addEventListener('keyup', event => {
|
|
||||||
var key = event.keyCode, k,
|
|
||||||
i = _downKeys.indexOf(key);
|
|
||||||
|
|
||||||
// remove key from _downKeys
|
|
||||||
if (i >= 0) {
|
|
||||||
_downKeys.splice(i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(key == 93 || key == 224) key = 91;
|
|
||||||
if(key in _mods) {
|
|
||||||
_mods[key] = false;
|
|
||||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset modifiers to false whenever the window is (re)focused.
|
|
||||||
addEventListener('focus', () => {
|
|
||||||
for(let k in _mods) _mods[k] = false;
|
|
||||||
for(let k in _MODIFIERS) assignKey[k] = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// set window.key and window.key.set/get, and the default filter
|
|
||||||
global.key = assignKey;
|
|
||||||
global.key.setScope = scope => { _scope = scope || 'all' };
|
|
||||||
global.key.getScope = getScope;
|
|
||||||
global.key.filter = event => {
|
|
||||||
var tagName = event.target.tagName;
|
|
||||||
// ignore keypressed in any elements that support keyboard data input
|
|
||||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
|
||||||
};
|
|
||||||
|
|
||||||
})(this);
|
|
11
vendors/keymaster/package.json
vendored
11
vendors/keymaster/package.json
vendored
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"name": "keymaster",
|
|
||||||
"description": "library for defining and dispatching keyboard shortcuts",
|
|
||||||
"version": "1.6.2",
|
|
||||||
"author": "Thomas Fuchs <thomas@slash7.com> (http://mir.aculo.us)",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/madrobby/keymaster"
|
|
||||||
},
|
|
||||||
"main": "./keymaster.js"
|
|
||||||
}
|
|
Loading…
Reference in a new issue