mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-20 07:35: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>
|
||||
<br>
|
||||
<p>
|
||||
Simple, modern & fast web-based email client.
|
||||
Simple, modern, lightweight & fast web-based email client.
|
||||
</p>
|
||||
<p>
|
||||
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 simplestatemanager with @media
|
||||
* Replaced inputosaurus to native
|
||||
* Replaced keymaster with own shortcuts handler
|
||||
* Removed pikaday
|
||||
* Removed underscore
|
||||
* 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 |
|
||||
|----------- |--------: |--------: |
|
||||
|admin.js |2.130.942 | 850.202 |
|
||||
|app.js |4.184.455 |2.488.837 |
|
||||
|admin.js |2.130.942 | 844.753 |
|
||||
|app.js |4.184.455 |2.487.401 |
|
||||
|boot.js | 671.522 | 5.285 |
|
||||
|libs.js | 647.614 | 250.948 |
|
||||
|libs.js | 647.614 | 246.085 |
|
||||
|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 |
|
||||
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
||||
|admin.min.js | 252.147 | 116.626 | 73.657 | 33.343 | 28.869 |
|
||||
|app.min.js | 511.202 | 339.176 |140.462 | 89.271 | 72.393 |
|
||||
|admin.min.js | 252.147 | 116.068 | 73.657 | 33.279 | 28.711 |
|
||||
|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 |
|
||||
|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 |
|
||||
|TOTAL |1.434.353 | 607.040 |424.718 |176.330 |149.019 |
|
||||
|TOTAL (no admin) |1.182.206 | 490.414 |351.061 |142.987 |120.150 |
|
||||
|TOTAL |1.434.353 | 605.285 |424.718 |175.423 |148.015 |
|
||||
|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.
|
||||
|
||||
|
@ -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 |
|
||||
|-------------- |-------: |-------: |------: |------: |------: |
|
||||
|app.css | 340.334 | 190.855 | 46,959 | 29.156 | 24.561 |
|
||||
|app.min.css | 274.791 | 157.619 | 39.618 | 26.336 | 22.667 |
|
||||
|app.css | 340.334 | 190.086 | 46,959 | 29.415 | 24.717 |
|
||||
|app.min.css | 274.791 | 156.076 | 39.618 | 26.034 | 22.418 |
|
||||
|boot.css | | 2.534 | | 837 | 668 |
|
||||
|boot.min.css | | 2.055 | | 732 | 560 |
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class AbstractApp {
|
|||
$doc.addEventListener('keypress', fn);
|
||||
$doc.addEventListener('click', fn);
|
||||
|
||||
shortcuts.add(['escape','enter'], '', KeyState.All, () => rl.Dropdowns.detectVisibility());
|
||||
shortcuts.add('escape,enter', '', KeyState.All, () => rl.Dropdowns.detectVisibility());
|
||||
}
|
||||
|
||||
remote() {
|
||||
|
|
|
@ -286,11 +286,11 @@ class Selector {
|
|||
shortcuts.add('arrowup', '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);
|
||||
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);
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ class MenuSettingsAdminView extends AbstractViewNext {
|
|||
}
|
||||
|
||||
onBuild(dom) {
|
||||
shortcuts.add(['arrowup','arrowdown'], '', KeyState.Settings,
|
||||
shortcuts.add('arrowup,arrowdown', '', KeyState.Settings,
|
||||
settingsMenuKeysHandler(dom.querySelectorAll('.b-admin-menu .e-item')));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ class AskPopupView extends AbstractViewNext {
|
|||
|
||||
onBuild() {
|
||||
// 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');
|
||||
if (btn.matches(':focus')) {
|
||||
btn = this.querySelector('.buttonNo');
|
||||
|
|
|
@ -303,7 +303,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
|||
}
|
||||
|
||||
onBuild() {
|
||||
// shortcuts.add(('tab', 'shift', KeyState.PopupComposeOpenPGP, () => {
|
||||
// shortcuts.add('tab', 'shift', KeyState.PopupComposeOpenPGP, () => {
|
||||
shortcuts.add('tab', '', KeyState.PopupComposeOpenPGP, () => {
|
||||
let btn = this.querySelector('.inputPassword');
|
||||
if (btn.matches(':focus')) {
|
||||
|
|
|
@ -611,7 +611,7 @@ class ContactsPopupView extends AbstractViewNext {
|
|||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add(['c','w'], '', KeyState.ContactList, () => {
|
||||
shortcuts.add('c,w', '', KeyState.ContactList, () => {
|
||||
this.newMessageCommand();
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ class KeyboardShortcutsHelpPopupView extends AbstractViewNext {
|
|||
dom.querySelectorAll('a[data-toggle="tab"]').forEach(node => node.Tab || new BSN.Tab(node));
|
||||
|
||||
// shortcuts.add('tab', 'shift',
|
||||
shortcuts.add(['tab','arrowleft','arrowright'], '',
|
||||
shortcuts.add('tab,arrowleft,arrowright', '',
|
||||
KeyState.PopupKeyboardShortcutsHelp,
|
||||
((event, handler)=>{
|
||||
if (event && handler) {
|
||||
|
|
|
@ -79,7 +79,7 @@ class MessageOpenPgpPopupView extends AbstractViewNext {
|
|||
}
|
||||
|
||||
onBuild(oDom) {
|
||||
// shortcuts.add('tab','shift', KeyState.PopupMessageOpenPGP, () => {
|
||||
// shortcuts.add('tab', 'shift', KeyState.PopupMessageOpenPGP, () => {
|
||||
shortcuts.add('tab', '', KeyState.PopupMessageOpenPGP, () => {
|
||||
let btn = this.querySelector('.inputPassword');
|
||||
if (btn.matches(':focus')) {
|
||||
|
|
|
@ -121,7 +121,7 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
|||
el && fSelectFolder(el, event, false);
|
||||
});
|
||||
|
||||
shortcuts.add(['arrowup','arrowdown'], '', KeyState.FolderList, event => {
|
||||
shortcuts.add('arrowup,arrowdown', '', KeyState.FolderList, event => {
|
||||
let items = [], index = 0;
|
||||
dom.querySelectorAll('.b-folders .e-item .e-link:not(.hidden)').forEach(node => {
|
||||
if (node.offsetHeight || node.getClientRects().length) {
|
||||
|
@ -168,7 +168,7 @@ class FolderListMailBoxUserView extends AbstractViewNext {
|
|||
});
|
||||
|
||||
// shortcuts.add('tab', 'shift', KeyState.FolderList, () => {
|
||||
shortcuts.add(['escape','tab','arrowright'], '', KeyState.FolderList, () => {
|
||||
shortcuts.add('escape,tab,arrowright', '', KeyState.FolderList, () => {
|
||||
AppStore.focusedState(Focused.MessageList);
|
||||
moveAction(false);
|
||||
return false;
|
||||
|
|
|
@ -786,7 +786,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
|||
|
||||
if (Settings.capa(Capa.Composer)) {
|
||||
// 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'));
|
||||
return false;
|
||||
});
|
||||
|
@ -877,7 +877,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
|||
AppStore.focusedState(Focused.FolderList);
|
||||
return false;
|
||||
});
|
||||
shortcuts.add(['tab','arrowright'], '', KeyState.MessageList, () => {
|
||||
shortcuts.add('tab,arrowright', '', KeyState.MessageList, () => {
|
||||
this.message() && AppStore.focusedState(Focused.MessageView);
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -547,7 +547,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
|||
|
||||
initShortcuts() {
|
||||
// exit fullscreen, back
|
||||
shortcuts.add(['escape','backspace'], '', KeyState.MessageView, this.escShortcuts.bind(this));
|
||||
shortcuts.add('escape,backspace', '', KeyState.MessageView, this.escShortcuts.bind(this));
|
||||
|
||||
// fullscreen
|
||||
shortcuts.add('enter', '', KeyState.MessageView, () => {
|
||||
|
@ -602,12 +602,12 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
|||
return true;
|
||||
});
|
||||
|
||||
shortcuts.add(['arrowup','arrowleft'], 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||
shortcuts.add('arrowup,arrowleft', 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||
this.goUpCommand();
|
||||
return false;
|
||||
});
|
||||
|
||||
shortcuts.add(['arrowdown','arrowright'], 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||
shortcuts.add('arrowdown,arrowright', 'meta', [KeyState.MessageList, KeyState.MessageView], () => {
|
||||
this.goDownCommand();
|
||||
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')));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ const
|
|||
doc = document,
|
||||
meta = /Mac OS X/.test(navigator.userAgent) ? 'meta' : 'ctrl',
|
||||
_scopes = {},
|
||||
toArray = v => Array.isArray(v) ? v : [v],
|
||||
toArray = v => Array.isArray(v) ? v : v.split(/\s*,\s*/),
|
||||
|
||||
fire = (actions, event) => {
|
||||
let modifiers = [];
|
||||
|
@ -20,7 +20,7 @@ const
|
|||
actions[modifiers].forEach(cmd => {
|
||||
try {
|
||||
// call the handler and stop the event if neccessary
|
||||
if (!event.defaultPrevented && cmd(event) === false) {
|
||||
if (!event.defaultPrevented && cmd(event) === false) {
|
||||
event.preventDefault();
|
||||
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