mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Added keyboard shortcuts (part 1) (#70)
Scrolling in message view (#109)
This commit is contained in:
parent
3936a818b7
commit
8979687f88
37 changed files with 2095 additions and 333 deletions
|
|
@ -56,6 +56,7 @@
|
|||
"ifvisible" : true,
|
||||
"crossroads" : true,
|
||||
"hasher" : true,
|
||||
"key" : true,
|
||||
"Jua" : true,
|
||||
"_" : true,
|
||||
"Dropbox" : true
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@ module.exports = function (grunt) {
|
|||
"vendors/knockout-projections/knockout-projections-1.0.0.min.js",
|
||||
"vendors/ssm/ssm.min.js",
|
||||
"vendors/jua/jua.min.js",
|
||||
"vendors/keymaster/keymaster.js",
|
||||
"vendors/ifvisible/ifvisible.min.js",
|
||||
"vendors/jquery-magnific-popup/jquery.magnific-popup.min.js",
|
||||
"vendors/bootstrap/js/bootstrap.min.js",
|
||||
|
|
@ -233,8 +234,8 @@ module.exports = function (grunt) {
|
|||
options: {
|
||||
stripBanners: true,
|
||||
banner: '/*! RainLoop Webmail Main Module (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */\n' +
|
||||
'(function (window, $, ko, crossroads, hasher, moment, Jua, _, ifvisible) {\n',
|
||||
footer: '\n\n}(window, jQuery, ko, crossroads, hasher, moment, Jua, _, ifvisible));'
|
||||
'(function (window, $, ko, crossroads, hasher, moment, Jua, _, ifvisible, key) {\n',
|
||||
footer: '\n\n}(window, jQuery, ko, crossroads, hasher, moment, Jua, _, ifvisible, key));'
|
||||
},
|
||||
src: [
|
||||
"dev/Common/_Begin.js",
|
||||
|
|
@ -287,6 +288,7 @@ module.exports = function (grunt) {
|
|||
"dev/ViewModels/PopupsLanguagesViewModel.js",
|
||||
"dev/ViewModels/PopupsTwoFactorTestViewModel.js",
|
||||
"dev/ViewModels/PopupsAskViewModel.js",
|
||||
"dev/ViewModels/PopupsKeyboardShortcutsHelpViewModel.js",
|
||||
|
||||
"dev/ViewModels/LoginViewModel.js",
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,18 @@ Enums.StateType = {
|
|||
'Admin': 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.KeyState = {
|
||||
'None': 'none',
|
||||
'ContactList': 'contact-list',
|
||||
'MessageList': 'message-list',
|
||||
'MessageView': 'message-view',
|
||||
'Compose': 'compose',
|
||||
'PopupAsk': 'popup-ask'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -137,11 +137,13 @@ Selector.prototype.goUp = function ()
|
|||
this.newSelectPosition(Enums.EventKeyCode.Up, false);
|
||||
};
|
||||
|
||||
Selector.prototype.init = function (oContentVisible, oContentScrollable)
|
||||
Selector.prototype.init = function (oContentVisible, oContentScrollable, sKeyScope)
|
||||
{
|
||||
this.oContentVisible = oContentVisible;
|
||||
this.oContentScrollable = oContentScrollable;
|
||||
|
||||
sKeyScope = sKeyScope || 'all';
|
||||
|
||||
if (this.oContentVisible && this.oContentScrollable)
|
||||
{
|
||||
var
|
||||
|
|
@ -183,27 +185,43 @@ Selector.prototype.init = function (oContentVisible, oContentScrollable)
|
|||
})
|
||||
;
|
||||
|
||||
$(window.document).on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.bUseKeyboard && !Utils.inFocus())
|
||||
key('space, enter', sKeyScope, function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
key('up, shift+up, down, shift+down, insert, home, end, pageup, pagedown', sKeyScope, function (event, handler) {
|
||||
if (event && handler && handler.shortcut)
|
||||
{
|
||||
if (-1 < Utils.inArray(oEvent.keyCode, [Enums.EventKeyCode.Up, Enums.EventKeyCode.Down, Enums.EventKeyCode.Insert,
|
||||
Enums.EventKeyCode.Home, Enums.EventKeyCode.End, Enums.EventKeyCode.PageUp, Enums.EventKeyCode.PageDown]))
|
||||
var iKey = 0, aCodes = null;
|
||||
switch (handler.shortcut)
|
||||
{
|
||||
self.newSelectPosition(oEvent.keyCode, oEvent.shiftKey);
|
||||
bResult = false;
|
||||
case 'up':
|
||||
case 'shift+up':
|
||||
iKey = Enums.EventKeyCode.Up;
|
||||
break;
|
||||
case 'down':
|
||||
case 'shift+down':
|
||||
iKey = Enums.EventKeyCode.Down;
|
||||
break;
|
||||
case 'insert':
|
||||
case 'home':
|
||||
case 'end':
|
||||
case 'pageup':
|
||||
case 'pagedown':
|
||||
aCodes = key.getPressedKeyCodes();
|
||||
if (aCodes && aCodes[0])
|
||||
{
|
||||
iKey = aCodes[0];
|
||||
}
|
||||
else if (Enums.EventKeyCode.Delete === oEvent.keyCode && !oEvent.ctrlKey && !oEvent.shiftKey)
|
||||
{
|
||||
if (self.oCallbacks['onDelete'])
|
||||
{
|
||||
self.oCallbacks['onDelete']();
|
||||
break;
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
if (0 < iKey)
|
||||
{
|
||||
self.newSelectPosition(iKey, key.shift);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1719,3 +1719,31 @@ Utils.selectElement = function (element)
|
|||
}
|
||||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.disableKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function () {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Utils.restoreKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function (event) {
|
||||
var
|
||||
element = event.target || event.srcElement,
|
||||
tagName = element ? element.tagName : ''
|
||||
;
|
||||
|
||||
tagName = tagName.toUpperCase();
|
||||
return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA' ||
|
||||
(element && tagName === 'DIV' && 'editorHtmlArea' === element.className && element.contentEditable)
|
||||
);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,14 +63,11 @@ KnoinAbstractViewModel.prototype.cancelCommand = KnoinAbstractViewModel.prototyp
|
|||
|
||||
KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility())
|
||||
key('esc', _.bind(function () {
|
||||
if (this.modalVisibility && this.modalVisibility())
|
||||
{
|
||||
Utils.delegateRun(self, 'cancelCommand');
|
||||
Utils.delegateRun(this, 'cancelCommand');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ function ContactModel()
|
|||
this.readOnly = false;
|
||||
this.scopeType = Enums.ContactScopeType.Default;
|
||||
|
||||
this.checked = ko.observable(false);
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.checked = ko.observable(false);
|
||||
this.deleted = ko.observable(false);
|
||||
this.shared = ko.observable(false);
|
||||
}
|
||||
|
|
@ -118,6 +119,10 @@ ContactModel.prototype.lineAsCcc = function ()
|
|||
{
|
||||
aResult.push('shared');
|
||||
}
|
||||
if (this.focused())
|
||||
{
|
||||
aResult.push('focused');
|
||||
}
|
||||
|
||||
return aResult.join(' ');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ function FolderModel()
|
|||
|
||||
this.type = ko.observable(Enums.FolderType.User);
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.edited = ko.observable(false);
|
||||
this.collapsed = ko.observable(true);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ function MessageModel()
|
|||
this.forwarded = ko.observable(false);
|
||||
this.isReadReceipt = ko.observable(false);
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.checked = ko.observable(false);
|
||||
this.hasAttachments = ko.observable(false);
|
||||
|
|
@ -514,6 +515,10 @@ MessageModel.prototype.lineAsCcc = function ()
|
|||
{
|
||||
aResult.push('forwarded');
|
||||
}
|
||||
if (this.focused())
|
||||
{
|
||||
aResult.push('focused');
|
||||
}
|
||||
if (this.hasAttachments())
|
||||
{
|
||||
aResult.push('withAttachments');
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ MailBoxScreen.prototype.setNewTitle = function ()
|
|||
MailBoxScreen.prototype.onShow = function ()
|
||||
{
|
||||
this.setNewTitle();
|
||||
|
||||
RL.data().keyScope(Enums.KeyState.MessageList);
|
||||
};
|
||||
|
||||
MailBoxScreen.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(Enums.KeyState.None);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -37,12 +37,27 @@ function WebMailDataStorage()
|
|||
this.accountIncLogin = ko.observable('');
|
||||
this.accountOutLogin = ko.observable('');
|
||||
this.projectHash = ko.observable('');
|
||||
|
||||
this.threading = ko.observable(false);
|
||||
this.lastFoldersHash = '';
|
||||
|
||||
this.lastFoldersHash = '';
|
||||
this.remoteSuggestions = false;
|
||||
|
||||
this.keyScope = ko.observable(Enums.KeyState.None);
|
||||
this.keyScope.subscribe(function (sValue) {
|
||||
|
||||
if (Enums.KeyState.Compose === sValue)
|
||||
{
|
||||
Utils.disableKeyFilter();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.restoreKeyFilter();
|
||||
}
|
||||
|
||||
// window.console.log(sValue);
|
||||
key.setScope(sValue);
|
||||
});
|
||||
|
||||
// system folders
|
||||
this.sentFolder = ko.observable('');
|
||||
this.draftFolder = ko.observable('');
|
||||
|
|
@ -257,6 +272,23 @@ function WebMailDataStorage()
|
|||
this.messageLoading = ko.observable(false);
|
||||
this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50});
|
||||
|
||||
this.message.focused = ko.observable(false);
|
||||
|
||||
this.message.subscribe(function (oMessage) {
|
||||
if (!oMessage)
|
||||
{
|
||||
this.message.focused(false);
|
||||
}
|
||||
else if (Enums.Layout.NoPreview === this.layout())
|
||||
{
|
||||
this.message.focused(true);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.message.focused.subscribe(function (bValue) {
|
||||
RL.data().keyScope(bValue ? Enums.KeyState.MessageView : Enums.KeyState.MessageList);
|
||||
});
|
||||
|
||||
this.messageLoading.subscribe(function (bValue) {
|
||||
this.messageLoadingThrottle(bValue);
|
||||
}, this);
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
@import "SystemDropDown.less";
|
||||
@import "Login.less";
|
||||
@import "Ask.less";
|
||||
@import "Shortcuts.less";
|
||||
@import "FolderList.less";
|
||||
@import "FolderClear.less";
|
||||
@import "FolderCreate.less";
|
||||
|
|
|
|||
|
|
@ -478,6 +478,10 @@ html.rl-no-preview-pane {
|
|||
}
|
||||
}
|
||||
|
||||
&.message-focused .b-content {
|
||||
.opacity(97);
|
||||
}
|
||||
|
||||
&.hideMessageListCheckbox {
|
||||
.checkedParent, .checkboxCkeckAll {
|
||||
display: none !important;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@ html.rl-no-preview-pane {
|
|||
top: 50px + @rlLowMargin;
|
||||
bottom: @rlLowMargin + @rlBottomMargin;
|
||||
right: @rlLowMargin;
|
||||
left: 0;
|
||||
left: -1px;
|
||||
overflow: hidden;
|
||||
border: @rlLowBorderSize solid @rlMainDarkColor;
|
||||
border-left: 0px;
|
||||
.border-top-right-radius(@rlLowBorderRadius);
|
||||
.border-bottom-right-radius(@rlLowBorderRadius);
|
||||
|
||||
|
|
@ -351,6 +350,17 @@ html.rl-no-preview-pane {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.message-focused .messageItemHeader {
|
||||
background-color: #fafafa !important;
|
||||
}
|
||||
|
||||
&.message-focused .b-content {
|
||||
z-index: 102;
|
||||
.box-shadow(0px 2px 8px rgba(0, 0, 0, 0.3));
|
||||
border-color: darken(@rlMainDarkColor, 5%);
|
||||
.border-radius(@rlLowBorderRadius);
|
||||
}
|
||||
}
|
||||
|
||||
html.rl-no-preview-pane .messageView {
|
||||
|
|
|
|||
7
dev/Styles/Shortcuts.less
Normal file
7
dev/Styles/Shortcuts.less
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.popups {
|
||||
.b-shortcuts-content {
|
||||
.modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,11 +24,19 @@ Utils.extendAsViewModel('MailBoxFolderListViewModel', MailBoxFolderListViewModel
|
|||
MailBoxFolderListViewModel.prototype.onBuild = function (oDom)
|
||||
{
|
||||
oDom
|
||||
.on('click', '.b-folders .b-content', function () {
|
||||
if (RL.data().useKeyboardShortcuts() && RL.data().message.focused())
|
||||
{
|
||||
RL.data().message.focused(false);
|
||||
}
|
||||
})
|
||||
.on('click', '.b-folders .e-item .e-link .e-collapsed-sign', function (oEvent) {
|
||||
|
||||
var
|
||||
oFolder = ko.dataFor(this),
|
||||
bCollapsed = false
|
||||
;
|
||||
|
||||
if (oFolder && oEvent)
|
||||
{
|
||||
bCollapsed = oFolder.collapsed();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ function MailBoxMessageListViewModel()
|
|||
|
||||
this.popupVisibility = RL.popupVisibility;
|
||||
|
||||
this.message = oData.message;
|
||||
this.messageList = oData.messageList;
|
||||
this.currentMessage = oData.currentMessage;
|
||||
this.isMessageSelected = oData.isMessageSelected;
|
||||
|
|
@ -214,13 +215,6 @@ function MailBoxMessageListViewModel()
|
|||
return oMessage ? oMessage.generateUid() : '';
|
||||
});
|
||||
|
||||
this.selector.on('onDelete', _.bind(function () {
|
||||
if (0 < RL.data().messageListCheckedOrSelected().length)
|
||||
{
|
||||
this.deleteCommand();
|
||||
}
|
||||
}, this));
|
||||
|
||||
RL
|
||||
.sub('mailbox.message-list.selector.go-down', function () {
|
||||
this.selector.goDown();
|
||||
|
|
@ -540,31 +534,15 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
return false;
|
||||
});
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable);
|
||||
|
||||
$document.on('keydown', function (oEvent) {
|
||||
|
||||
var
|
||||
bResult = true,
|
||||
iKeyCode = oEvent ? oEvent.keyCode : 0
|
||||
;
|
||||
|
||||
if (oEvent && self.viewModelVisibility() && oData.useKeyboardShortcuts() && !RL.popupVisibility() && !oData.messageFullScreenMode() && !Utils.inFocus())
|
||||
{
|
||||
if (Enums.Layout.NoPreview !== oData.layout() || (!oData.message() && (Enums.EventKeyCode.Delete === iKeyCode || Enums.EventKeyCode.A === iKeyCode)))
|
||||
{
|
||||
if (oEvent.ctrlKey && Enums.EventKeyCode.A === iKeyCode)
|
||||
{
|
||||
self.checkAll(!(self.checkAll() && !self.isIncompleteChecked()));
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable, Enums.KeyState.MessageList);
|
||||
|
||||
oDom
|
||||
.on('click', '.messageList .b-message-list-wrapper', function () {
|
||||
if (oData.useKeyboardShortcuts() && self.message.focused())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
})
|
||||
.on('click', '.e-pagenator .e-page', function () {
|
||||
var oPage = ko.dataFor(this);
|
||||
if (oPage)
|
||||
|
|
@ -625,6 +603,7 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
}, this).extend({'notify': 'always'});
|
||||
|
||||
this.initUploaderForAppend();
|
||||
this.initShortcuts();
|
||||
|
||||
if (!Globals.bMobileDevice && !!RL.settingsGet('AllowPrefetch') && ifvisible)
|
||||
{
|
||||
|
|
@ -636,6 +615,121 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
}
|
||||
};
|
||||
|
||||
MailBoxMessageListViewModel.prototype.initShortcuts = function ()
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
oData = RL.data()
|
||||
;
|
||||
|
||||
// disable print
|
||||
key('ctrl+p, command+p', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// delete
|
||||
key('delete, shift+delete', Enums.KeyState.MessageList, function (event, handler) {
|
||||
if (oData.useKeyboardShortcuts() && event)
|
||||
{
|
||||
if (0 < RL.data().messageListCheckedOrSelected().length)
|
||||
{
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
self.deleteWithoutMoveCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// check all
|
||||
key('ctrl+a, command+a', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.checkAll(!(self.checkAll() && !self.isIncompleteChecked()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// new message (open compose popup)
|
||||
key('c,n', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
kn.showScreenPopup(PopupsComposeViewModel);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// shortcuts help
|
||||
key('shift+/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
kn.showScreenPopup(PopupsKeyboardShortcutsHelpViewModel);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// search input focus
|
||||
key('/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
|
||||
self.inputMessageListSearchFocus(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// cancel search
|
||||
key('esc', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts() && '' !== self.messageListSearchDesc())
|
||||
{
|
||||
self.cancelSearch();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// change focused state
|
||||
key('tab, enter', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+left, command+left', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+right, command+right', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
MailBoxMessageListViewModel.prototype.prefetchNextTick = function ()
|
||||
{
|
||||
if (!this.bPrefetch && !ifvisible.now() && this.viewModelVisibility())
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ function MailBoxMessageViewViewModel()
|
|||
|
||||
this.oMessageScrollerDom = null;
|
||||
|
||||
this.keyScope = oData.keyScope;
|
||||
this.message = oData.message;
|
||||
this.messageLoading = oData.messageLoading;
|
||||
this.messageLoadingThrottle = oData.messageLoadingThrottle;
|
||||
|
|
@ -36,6 +37,7 @@ function MailBoxMessageViewViewModel()
|
|||
this.fullScreenMode = oData.messageFullScreenMode;
|
||||
|
||||
this.showFullInfo = ko.observable(false);
|
||||
this.messageDomFocused = ko.observable(false);
|
||||
|
||||
this.messageVisibility = ko.computed(function () {
|
||||
return !this.messageLoadingThrottle() && !!this.message();
|
||||
|
|
@ -68,36 +70,39 @@ function MailBoxMessageViewViewModel()
|
|||
}, this.messageVisibility);
|
||||
|
||||
this.deleteCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.deleteWithoutMoveCommand = Utils.createCommand(this, function () {
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
RL.data().currentFolderFullNameRaw(),
|
||||
RL.data().messageListCheckedOrSelectedUidsWithSubMails(), false);
|
||||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.archiveCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Archive,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.spamCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Spam,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
|
||||
}, this.messageVisibility);
|
||||
|
||||
// viewer
|
||||
|
|
@ -292,27 +297,12 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
oData = RL.data()
|
||||
;
|
||||
|
||||
$document.on('keydown', function (oEvent) {
|
||||
|
||||
var
|
||||
bResult = true,
|
||||
iKeyCode = oEvent ? oEvent.keyCode : 0
|
||||
;
|
||||
|
||||
if (0 < iKeyCode && (Enums.EventKeyCode.Esc === iKeyCode) &&
|
||||
self.viewModelVisibility() && oData.useKeyboardShortcuts() && !Utils.inFocus() && oData.message())
|
||||
this.fullScreenMode.subscribe(function (bValue) {
|
||||
if (bValue)
|
||||
{
|
||||
self.fullScreenMode(false);
|
||||
if (Enums.Layout.NoPreview === oData.layout())
|
||||
{
|
||||
RL.historyBack();
|
||||
self.message.focused(true);
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
}, this);
|
||||
|
||||
$('.attachmentsPlace', oDom).magnificPopup({
|
||||
'delegate': '.magnificPopupImage:visible',
|
||||
|
|
@ -335,6 +325,12 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
});
|
||||
|
||||
oDom
|
||||
.on('click', '.messageView .messageItem', function () {
|
||||
if (oData.useKeyboardShortcuts() && self.message())
|
||||
{
|
||||
self.message.focused(true);
|
||||
}
|
||||
})
|
||||
.on('mousedown', 'a', function (oEvent) {
|
||||
// setup maito protocol
|
||||
return !(oEvent && 3 !== oEvent['which'] && RL.mailToHelper($(this).attr('href')));
|
||||
|
|
@ -358,8 +354,171 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
})
|
||||
;
|
||||
|
||||
this.message.focused.subscribe(function (bValue) {
|
||||
this.messageDomFocused(!!bValue);
|
||||
}, this);
|
||||
|
||||
this.keyScope.subscribe(function (sValue) {
|
||||
if (Enums.KeyState.MessageView === sValue && this.message.focused())
|
||||
{
|
||||
this.messageDomFocused(true);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.oMessageScrollerDom = oDom.find('.messageItem .content');
|
||||
this.oMessageScrollerDom = this.oMessageScrollerDom && this.oMessageScrollerDom[0] ? this.oMessageScrollerDom : null;
|
||||
|
||||
this.initShortcuts();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
MailBoxMessageViewViewModel.prototype.escShortcuts = function ()
|
||||
{
|
||||
if (this.viewModelVisibility() && RL.data().useKeyboardShortcuts() && this.message())
|
||||
{
|
||||
if (this.fullScreenMode())
|
||||
{
|
||||
this.fullScreenMode(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.message.focused(false);
|
||||
}
|
||||
|
||||
if (Enums.Layout.NoPreview === RL.data().layout())
|
||||
{
|
||||
RL.historyBack();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
MailBoxMessageViewViewModel.prototype.initShortcuts = function ()
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
oData = RL.data()
|
||||
;
|
||||
|
||||
// exit fullscreen, back
|
||||
key('esc', Enums.KeyState.MessageView, _.bind(this.escShortcuts, this));
|
||||
|
||||
// fullscreen
|
||||
key('enter', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.toggleFullScreen();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// reply
|
||||
key('r', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.replyCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// replaAll
|
||||
key('a', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.replyAllCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// forward
|
||||
key('f', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.forwardCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// message information
|
||||
key('i', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.showFullInfo(!self.showFullInfo());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+left, command+left', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.goDownCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+right, command+right', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.goUpCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// print
|
||||
key('ctrl+p, command+p', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message().printMessage();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// archive
|
||||
// key('delete', Enums.KeyState.MessageView, function () {
|
||||
// if (oData.useKeyboardShortcuts())
|
||||
// {
|
||||
// self.archiveCommand();
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
|
||||
// delete
|
||||
key('delete, shift+delete', Enums.KeyState.MessageView, function (event, handler) {
|
||||
if (oData.useKeyboardShortcuts() && event)
|
||||
{
|
||||
self.deleteCommand();
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
// self.deleteWithoutMoveCommand();
|
||||
self.deleteCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// change focused state
|
||||
key('tab', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ function PopupsAskViewModel()
|
|||
this.fNoAction = null;
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
|
@ -40,22 +41,22 @@ PopupsAskViewModel.prototype.clearPopup = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.yesClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fYesAction))
|
||||
{
|
||||
this.fYesAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.noClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fNoAction))
|
||||
{
|
||||
this.fNoAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -82,6 +83,9 @@ PopupsAskViewModel.prototype.onShow = function (sAskDesc, fYesFunc, fNoFunc, sYe
|
|||
{
|
||||
this.yesButton(sNoButton);
|
||||
}
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.PopupAsk);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onFocus = function ()
|
||||
|
|
@ -91,31 +95,25 @@ PopupsAskViewModel.prototype.onFocus = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.modalVisibility())
|
||||
key('tab, right, left', Enums.KeyState.PopupAsk, _.bind(function () {
|
||||
if (this.modalVisibility())
|
||||
{
|
||||
if (Enums.EventKeyCode.Tab === oEvent.keyCode || Enums.EventKeyCode.Right === oEvent.keyCode || Enums.EventKeyCode.Left === oEvent.keyCode)
|
||||
if (this.yesFocus())
|
||||
{
|
||||
if (self.yesFocus())
|
||||
{
|
||||
self.noFocus(true);
|
||||
this.noFocus(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.yesFocus(true);
|
||||
this.yesFocus(true);
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ function PopupsComposeViewModel()
|
|||
// this.driveCallback = _.bind(this.driveCallback, this);
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
|
@ -568,6 +569,8 @@ PopupsComposeViewModel.prototype.onHide = function ()
|
|||
{
|
||||
this.reset();
|
||||
kn.routeOn();
|
||||
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -640,6 +643,9 @@ PopupsComposeViewModel.prototype.onShow = function (sType, oMessageOrArray, aToE
|
|||
{
|
||||
kn.routeOff();
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.Compose);
|
||||
|
||||
var
|
||||
self = this,
|
||||
sFrom = '',
|
||||
|
|
@ -914,6 +920,11 @@ PopupsComposeViewModel.prototype.tryToClosePopup = function ()
|
|||
}]);
|
||||
};
|
||||
|
||||
PopupsComposeViewModel.prototype.useShortcuts = function ()
|
||||
{
|
||||
return this.modalVisibility() && RL.data().useKeyboardShortcuts();
|
||||
};
|
||||
|
||||
PopupsComposeViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
this.initUploader();
|
||||
|
|
@ -923,29 +934,25 @@ PopupsComposeViewModel.prototype.onBuild = function ()
|
|||
oScript = null
|
||||
;
|
||||
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
|
||||
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
key('ctrl+s, command+s', Enums.KeyState.Compose, function () {
|
||||
if (self.useShortcuts())
|
||||
{
|
||||
self.saveCommand();
|
||||
bResult = false;
|
||||
return false;
|
||||
}
|
||||
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
});
|
||||
|
||||
key('ctrl+enter, command+enter', Enums.KeyState.Compose, function () {
|
||||
if (self.useShortcuts())
|
||||
{
|
||||
self.sendCommand();
|
||||
bResult = false;
|
||||
}
|
||||
else if (Enums.EventKeyCode.Esc === oEvent.keyCode)
|
||||
{
|
||||
self.tryToClosePopup();
|
||||
bResult = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return bResult;
|
||||
key('esc', Enums.KeyState.Compose, function () {
|
||||
self.tryToClosePopup();
|
||||
return false;
|
||||
});
|
||||
|
||||
$window.on('resize', function () {
|
||||
|
|
|
|||
|
|
@ -171,10 +171,6 @@ function PopupsContactsViewModel()
|
|||
return oContact ? oContact.generateUid() : '';
|
||||
});
|
||||
|
||||
this.selector.on('onDelete', _.bind(function () {
|
||||
this.deleteCommand();
|
||||
}, this));
|
||||
|
||||
this.newCommand = Utils.createCommand(this, function () {
|
||||
this.populateViewContact(null);
|
||||
this.currentContact(null);
|
||||
|
|
@ -307,6 +303,8 @@ function PopupsContactsViewModel()
|
|||
}
|
||||
}, this);
|
||||
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
|
|
@ -577,10 +575,18 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
|
|||
this.oContentVisible = $('.b-list-content', oDom);
|
||||
this.oContentScrollable = $('.content', this.oContentVisible);
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable);
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable, Enums.KeyState.ContactList);
|
||||
|
||||
var self = this;
|
||||
|
||||
key('delete', Enums.KeyState.ContactList, function () {
|
||||
if (RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
self.deleteCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ko.computed(function () {
|
||||
var
|
||||
bModalVisibility = this.modalVisibility(),
|
||||
|
|
@ -607,6 +613,9 @@ PopupsContactsViewModel.prototype.onShow = function ()
|
|||
{
|
||||
kn.routeOff();
|
||||
this.reloadContactList(true);
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.ContactList);
|
||||
};
|
||||
|
||||
PopupsContactsViewModel.prototype.onHide = function ()
|
||||
|
|
@ -619,4 +628,6 @@ PopupsContactsViewModel.prototype.onHide = function ()
|
|||
_.each(this.contacts(), function (oItem) {
|
||||
oItem.checked(false);
|
||||
});
|
||||
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
|
|
|||
27
dev/ViewModels/PopupsKeyboardShortcutsHelpViewModel.js
Normal file
27
dev/ViewModels/PopupsKeyboardShortcutsHelpViewModel.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
*/
|
||||
function PopupsKeyboardShortcutsHelpViewModel()
|
||||
{
|
||||
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsKeyboardShortcutsHelp');
|
||||
|
||||
this.sKeyScope = Enums.KeyState.None;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
Utils.extendAsViewModel('PopupsKeyboardShortcutsHelpViewModel', PopupsKeyboardShortcutsHelpViewModel);
|
||||
|
||||
PopupsKeyboardShortcutsHelpViewModel.prototype.onShow = function ()
|
||||
{
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.None);
|
||||
};
|
||||
|
||||
PopupsKeyboardShortcutsHelpViewModel.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
|
@ -127,14 +127,11 @@ PopupsPluginViewModel.prototype.tryToClosePopup = function ()
|
|||
|
||||
PopupsPluginViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility())
|
||||
key('esc', _.bind(function () {
|
||||
if (this.modalVisibility())
|
||||
{
|
||||
self.tryToClosePopup();
|
||||
bResult = false;
|
||||
this.tryToClosePopup();
|
||||
return false;
|
||||
}
|
||||
return bResult;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<div id="rl-sub-left">
|
||||
<div class="messageList g-ui-user-select-none" data-bind="css: {'message-selected': isMessageSelected, 'loading': messageListCompleteLoadingThrottle, 'hideMessageListCheckbox': !useCheckboxesInList() }">
|
||||
<div class="messageList g-ui-user-select-none"
|
||||
data-bind="css: {'message-selected': isMessageSelected, 'message-focused': message.focused, 'loading': messageListCompleteLoadingThrottle, 'hideMessageListCheckbox': !useCheckboxesInList() }">
|
||||
<div class="toolbar">
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn-group">
|
||||
|
|
@ -111,10 +112,6 @@
|
|||
</a>
|
||||
</div>
|
||||
<i class="checkboxCkeckAll" data-bind="css: checkAll() ? (isIncompleteChecked() ? 'icon-checkbox-partial' : 'icon-checkbox-checked') : 'icon-checkbox-unchecked'"></i>
|
||||
<!--
|
||||
<i class="checkboxCkeckAll" data-bind="css: checkAll() ? (isIncompleteChecked() ? 'icon-checkbox-partial' : 'icon-checkbox-checked') : 'icon-checkbox-unchecked', visible: !messageListCompleteLoadingThrottle()"></i>
|
||||
<i class="icon-spinner animated" style="margin-top: 4px;" data-bind="visible: messageListCompleteLoadingThrottle"></i>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="mainDelimiter toolbarDelimiter"></div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div id="rl-sub-right">
|
||||
<div class="messageView" data-bind="css: {'message-selected': isMessageSelected}">
|
||||
<div class="messageView" data-bind="css: {'message-selected': isMessageSelected, 'message-focused': message.focused}">
|
||||
<div class="toolbar g-ui-user-select-none">
|
||||
<div class="messageButtons btn-toolbar">
|
||||
<div class="btn-group" data-placement="bottom" data-bind="visible: !usePreviewPane(), tooltip: 'MESSAGE/BUTTON_CLOSE'">
|
||||
|
|
@ -131,7 +131,7 @@
|
|||
</div>
|
||||
<div data-bind="visible: message">
|
||||
<div class="messageItem" data-bind="css: viewLineAsCcc(), nano: true">
|
||||
<div class="content g-scrollbox">
|
||||
<div class="content g-scrollbox" tabindex="0" data-bind="hasfocus: messageDomFocused">
|
||||
<div class="content-wrapper">
|
||||
<div>
|
||||
<span class="buttonUp" data-bind="click: scrollToTop">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<div class="popups">
|
||||
<div class="modal hide b-shortcuts-content" data-bind="modal: modalVisibility">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-bind="command: cancelCommand">×</button>
|
||||
<h3>
|
||||
Keyboard shortcuts help
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<br />
|
||||
Coming soon!
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -6416,6 +6416,9 @@ html.rl-no-preview-pane #rl-right .ui-resizable-handle {
|
|||
.popups .b-ask-content .desc-place {
|
||||
font-size: 18px;
|
||||
}
|
||||
.popups .b-shortcuts-content .modal-header {
|
||||
background-color: #fff;
|
||||
}
|
||||
.b-folders .b-toolbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -7048,6 +7051,10 @@ html.rl-no-preview-pane .messageList.message-selected {
|
|||
.messageList .b-content .messageListItem.flagged .flagOn {
|
||||
display: inline-block;
|
||||
}
|
||||
.messageList.message-focused .b-content {
|
||||
opacity: 0.97;
|
||||
filter: alpha(opacity=97);
|
||||
}
|
||||
.messageList.hideMessageListCheckbox .checkedParent,
|
||||
.messageList.hideMessageListCheckbox .checkboxCkeckAll {
|
||||
display: none !important;
|
||||
|
|
@ -7096,10 +7103,9 @@ html.rl-no-preview-pane .messageView.message-selected {
|
|||
top: 58px;
|
||||
bottom: 13px;
|
||||
right: 8px;
|
||||
left: 0;
|
||||
left: -1px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #aaaaaa;
|
||||
border-left: 0px;
|
||||
-webkit-border-top-right-radius: 3px;
|
||||
-moz-border-radius-topright: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
|
|
@ -7380,6 +7386,19 @@ html.rl-no-preview-pane .messageView.message-selected {
|
|||
border-left: 2px solid red;
|
||||
color: red;
|
||||
}
|
||||
.messageView.message-focused .messageItemHeader {
|
||||
background-color: #fafafa !important;
|
||||
}
|
||||
.messageView.message-focused .b-content {
|
||||
z-index: 102;
|
||||
-webkit-box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.3);
|
||||
border-color: #9d9d9d;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
html.rl-no-preview-pane .messageView .toolbar {
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
|
|
|||
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -367,6 +367,18 @@ Enums.StateType = {
|
|||
'Admin': 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.KeyState = {
|
||||
'None': 'none',
|
||||
'ContactList': 'contact-list',
|
||||
'MessageList': 'message-list',
|
||||
'MessageView': 'message-view',
|
||||
'Compose': 'compose',
|
||||
'PopupAsk': 'popup-ask'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
|
|
@ -2421,6 +2433,34 @@ Utils.selectElement = function (element)
|
|||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.disableKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function () {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Utils.restoreKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function (event) {
|
||||
var
|
||||
element = event.target || event.srcElement,
|
||||
tagName = element ? element.tagName : ''
|
||||
;
|
||||
|
||||
tagName = tagName.toUpperCase();
|
||||
return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA' ||
|
||||
(element && tagName === 'DIV' && 'editorHtmlArea' === element.className && element.contentEditable)
|
||||
);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Base64 encode / decode
|
||||
// http://www.webtoolkit.info/
|
||||
|
||||
|
|
@ -3889,16 +3929,13 @@ KnoinAbstractViewModel.prototype.cancelCommand = KnoinAbstractViewModel.prototyp
|
|||
|
||||
KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility())
|
||||
key('esc', _.bind(function () {
|
||||
if (this.modalVisibility && this.modalVisibility())
|
||||
{
|
||||
Utils.delegateRun(self, 'cancelCommand');
|
||||
Utils.delegateRun(this, 'cancelCommand');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5072,16 +5109,13 @@ PopupsPluginViewModel.prototype.tryToClosePopup = function ()
|
|||
|
||||
PopupsPluginViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility())
|
||||
key('esc', _.bind(function () {
|
||||
if (this.modalVisibility())
|
||||
{
|
||||
self.tryToClosePopup();
|
||||
bResult = false;
|
||||
this.tryToClosePopup();
|
||||
return false;
|
||||
}
|
||||
return bResult;
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5279,6 +5313,7 @@ function PopupsAskViewModel()
|
|||
this.fNoAction = null;
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
|
@ -5300,22 +5335,22 @@ PopupsAskViewModel.prototype.clearPopup = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.yesClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fYesAction))
|
||||
{
|
||||
this.fYesAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.noClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fNoAction))
|
||||
{
|
||||
this.fNoAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5342,6 +5377,9 @@ PopupsAskViewModel.prototype.onShow = function (sAskDesc, fYesFunc, fNoFunc, sYe
|
|||
{
|
||||
this.yesButton(sNoButton);
|
||||
}
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.PopupAsk);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onFocus = function ()
|
||||
|
|
@ -5351,32 +5389,26 @@ PopupsAskViewModel.prototype.onFocus = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.modalVisibility())
|
||||
key('tab, right, left', Enums.KeyState.PopupAsk, _.bind(function () {
|
||||
if (this.modalVisibility())
|
||||
{
|
||||
if (Enums.EventKeyCode.Tab === oEvent.keyCode || Enums.EventKeyCode.Right === oEvent.keyCode || Enums.EventKeyCode.Left === oEvent.keyCode)
|
||||
if (this.yesFocus())
|
||||
{
|
||||
if (self.yesFocus())
|
||||
{
|
||||
self.noFocus(true);
|
||||
this.noFocus(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.yesFocus(true);
|
||||
this.yesFocus(true);
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
8
rainloop/v/0.0.0/static/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
/*! RainLoop Webmail Main Module (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
|
||||
(function (window, $, ko, crossroads, hasher, moment, Jua, _, ifvisible) {
|
||||
(function (window, $, ko, crossroads, hasher, moment, Jua, _, ifvisible, key) {
|
||||
|
||||
'use strict';
|
||||
|
||||
|
|
@ -371,6 +371,18 @@ Enums.StateType = {
|
|||
'Admin': 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
Enums.KeyState = {
|
||||
'None': 'none',
|
||||
'ContactList': 'contact-list',
|
||||
'MessageList': 'message-list',
|
||||
'MessageView': 'message-view',
|
||||
'Compose': 'compose',
|
||||
'PopupAsk': 'popup-ask'
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
|
|
@ -2425,6 +2437,34 @@ Utils.selectElement = function (element)
|
|||
/* jshint onevar: true */
|
||||
};
|
||||
|
||||
Utils.disableKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function () {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Utils.restoreKeyFilter = function ()
|
||||
{
|
||||
if (window.key)
|
||||
{
|
||||
key.filter = function (event) {
|
||||
var
|
||||
element = event.target || event.srcElement,
|
||||
tagName = element ? element.tagName : ''
|
||||
;
|
||||
|
||||
tagName = tagName.toUpperCase();
|
||||
return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA' ||
|
||||
(element && tagName === 'DIV' && 'editorHtmlArea' === element.className && element.contentEditable)
|
||||
);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Base64 encode / decode
|
||||
// http://www.webtoolkit.info/
|
||||
|
||||
|
|
@ -3979,11 +4019,13 @@ Selector.prototype.goUp = function ()
|
|||
this.newSelectPosition(Enums.EventKeyCode.Up, false);
|
||||
};
|
||||
|
||||
Selector.prototype.init = function (oContentVisible, oContentScrollable)
|
||||
Selector.prototype.init = function (oContentVisible, oContentScrollable, sKeyScope)
|
||||
{
|
||||
this.oContentVisible = oContentVisible;
|
||||
this.oContentScrollable = oContentScrollable;
|
||||
|
||||
sKeyScope = sKeyScope || 'all';
|
||||
|
||||
if (this.oContentVisible && this.oContentScrollable)
|
||||
{
|
||||
var
|
||||
|
|
@ -4025,27 +4067,43 @@ Selector.prototype.init = function (oContentVisible, oContentScrollable)
|
|||
})
|
||||
;
|
||||
|
||||
$(window.document).on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.bUseKeyboard && !Utils.inFocus())
|
||||
key('space, enter', sKeyScope, function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
key('up, shift+up, down, shift+down, insert, home, end, pageup, pagedown', sKeyScope, function (event, handler) {
|
||||
if (event && handler && handler.shortcut)
|
||||
{
|
||||
if (-1 < Utils.inArray(oEvent.keyCode, [Enums.EventKeyCode.Up, Enums.EventKeyCode.Down, Enums.EventKeyCode.Insert,
|
||||
Enums.EventKeyCode.Home, Enums.EventKeyCode.End, Enums.EventKeyCode.PageUp, Enums.EventKeyCode.PageDown]))
|
||||
var iKey = 0, aCodes = null;
|
||||
switch (handler.shortcut)
|
||||
{
|
||||
self.newSelectPosition(oEvent.keyCode, oEvent.shiftKey);
|
||||
bResult = false;
|
||||
case 'up':
|
||||
case 'shift+up':
|
||||
iKey = Enums.EventKeyCode.Up;
|
||||
break;
|
||||
case 'down':
|
||||
case 'shift+down':
|
||||
iKey = Enums.EventKeyCode.Down;
|
||||
break;
|
||||
case 'insert':
|
||||
case 'home':
|
||||
case 'end':
|
||||
case 'pageup':
|
||||
case 'pagedown':
|
||||
aCodes = key.getPressedKeyCodes();
|
||||
if (aCodes && aCodes[0])
|
||||
{
|
||||
iKey = aCodes[0];
|
||||
}
|
||||
else if (Enums.EventKeyCode.Delete === oEvent.keyCode && !oEvent.ctrlKey && !oEvent.shiftKey)
|
||||
{
|
||||
if (self.oCallbacks['onDelete'])
|
||||
{
|
||||
self.oCallbacks['onDelete']();
|
||||
break;
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
if (0 < iKey)
|
||||
{
|
||||
self.newSelectPosition(iKey, key.shift);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bResult;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -4706,16 +4764,13 @@ KnoinAbstractViewModel.prototype.cancelCommand = KnoinAbstractViewModel.prototyp
|
|||
|
||||
KnoinAbstractViewModel.prototype.registerPopupEscapeKey = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
if (oEvent && Enums.EventKeyCode.Esc === oEvent.keyCode && self.modalVisibility())
|
||||
key('esc', _.bind(function () {
|
||||
if (this.modalVisibility && this.modalVisibility())
|
||||
{
|
||||
Utils.delegateRun(self, 'cancelCommand');
|
||||
Utils.delegateRun(this, 'cancelCommand');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -5556,8 +5611,9 @@ function ContactModel()
|
|||
this.readOnly = false;
|
||||
this.scopeType = Enums.ContactScopeType.Default;
|
||||
|
||||
this.checked = ko.observable(false);
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.checked = ko.observable(false);
|
||||
this.deleted = ko.observable(false);
|
||||
this.shared = ko.observable(false);
|
||||
}
|
||||
|
|
@ -5662,6 +5718,10 @@ ContactModel.prototype.lineAsCcc = function ()
|
|||
{
|
||||
aResult.push('shared');
|
||||
}
|
||||
if (this.focused())
|
||||
{
|
||||
aResult.push('focused');
|
||||
}
|
||||
|
||||
return aResult.join(' ');
|
||||
};
|
||||
|
|
@ -6020,6 +6080,7 @@ function MessageModel()
|
|||
this.forwarded = ko.observable(false);
|
||||
this.isReadReceipt = ko.observable(false);
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.checked = ko.observable(false);
|
||||
this.hasAttachments = ko.observable(false);
|
||||
|
|
@ -6499,6 +6560,10 @@ MessageModel.prototype.lineAsCcc = function ()
|
|||
{
|
||||
aResult.push('forwarded');
|
||||
}
|
||||
if (this.focused())
|
||||
{
|
||||
aResult.push('focused');
|
||||
}
|
||||
if (this.hasAttachments())
|
||||
{
|
||||
aResult.push('withAttachments');
|
||||
|
|
@ -7189,6 +7254,7 @@ function FolderModel()
|
|||
|
||||
this.type = ko.observable(Enums.FolderType.User);
|
||||
|
||||
this.focused = ko.observable(false);
|
||||
this.selected = ko.observable(false);
|
||||
this.edited = ko.observable(false);
|
||||
this.collapsed = ko.observable(true);
|
||||
|
|
@ -8262,6 +8328,7 @@ function PopupsComposeViewModel()
|
|||
// this.driveCallback = _.bind(this.driveCallback, this);
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
|
@ -8479,6 +8546,8 @@ PopupsComposeViewModel.prototype.onHide = function ()
|
|||
{
|
||||
this.reset();
|
||||
kn.routeOn();
|
||||
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -8551,6 +8620,9 @@ PopupsComposeViewModel.prototype.onShow = function (sType, oMessageOrArray, aToE
|
|||
{
|
||||
kn.routeOff();
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.Compose);
|
||||
|
||||
var
|
||||
self = this,
|
||||
sFrom = '',
|
||||
|
|
@ -8825,6 +8897,11 @@ PopupsComposeViewModel.prototype.tryToClosePopup = function ()
|
|||
}]);
|
||||
};
|
||||
|
||||
PopupsComposeViewModel.prototype.useShortcuts = function ()
|
||||
{
|
||||
return this.modalVisibility() && RL.data().useKeyboardShortcuts();
|
||||
};
|
||||
|
||||
PopupsComposeViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
this.initUploader();
|
||||
|
|
@ -8834,29 +8911,25 @@ PopupsComposeViewModel.prototype.onBuild = function ()
|
|||
oScript = null
|
||||
;
|
||||
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
|
||||
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
|
||||
key('ctrl+s, command+s', Enums.KeyState.Compose, function () {
|
||||
if (self.useShortcuts())
|
||||
{
|
||||
self.saveCommand();
|
||||
bResult = false;
|
||||
return false;
|
||||
}
|
||||
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
|
||||
});
|
||||
|
||||
key('ctrl+enter, command+enter', Enums.KeyState.Compose, function () {
|
||||
if (self.useShortcuts())
|
||||
{
|
||||
self.sendCommand();
|
||||
bResult = false;
|
||||
}
|
||||
else if (Enums.EventKeyCode.Esc === oEvent.keyCode)
|
||||
{
|
||||
self.tryToClosePopup();
|
||||
bResult = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return bResult;
|
||||
key('esc', Enums.KeyState.Compose, function () {
|
||||
self.tryToClosePopup();
|
||||
return false;
|
||||
});
|
||||
|
||||
$window.on('resize', function () {
|
||||
|
|
@ -9569,10 +9642,6 @@ function PopupsContactsViewModel()
|
|||
return oContact ? oContact.generateUid() : '';
|
||||
});
|
||||
|
||||
this.selector.on('onDelete', _.bind(function () {
|
||||
this.deleteCommand();
|
||||
}, this));
|
||||
|
||||
this.newCommand = Utils.createCommand(this, function () {
|
||||
this.populateViewContact(null);
|
||||
this.currentContact(null);
|
||||
|
|
@ -9705,6 +9774,8 @@ function PopupsContactsViewModel()
|
|||
}
|
||||
}, this);
|
||||
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
|
|
@ -9975,10 +10046,18 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
|
|||
this.oContentVisible = $('.b-list-content', oDom);
|
||||
this.oContentScrollable = $('.content', this.oContentVisible);
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable);
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable, Enums.KeyState.ContactList);
|
||||
|
||||
var self = this;
|
||||
|
||||
key('delete', Enums.KeyState.ContactList, function () {
|
||||
if (RL.data().useKeyboardShortcuts())
|
||||
{
|
||||
self.deleteCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ko.computed(function () {
|
||||
var
|
||||
bModalVisibility = this.modalVisibility(),
|
||||
|
|
@ -10005,6 +10084,9 @@ PopupsContactsViewModel.prototype.onShow = function ()
|
|||
{
|
||||
kn.routeOff();
|
||||
this.reloadContactList(true);
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.ContactList);
|
||||
};
|
||||
|
||||
PopupsContactsViewModel.prototype.onHide = function ()
|
||||
|
|
@ -10017,6 +10099,8 @@ PopupsContactsViewModel.prototype.onHide = function ()
|
|||
_.each(this.contacts(), function (oItem) {
|
||||
oItem.checked(false);
|
||||
});
|
||||
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -10936,6 +11020,7 @@ function PopupsAskViewModel()
|
|||
this.fNoAction = null;
|
||||
|
||||
this.bDisabeCloseOnEsc = true;
|
||||
this.sKeyScope = Enums.KeyState.MessageList;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
|
@ -10957,22 +11042,22 @@ PopupsAskViewModel.prototype.clearPopup = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.yesClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fYesAction))
|
||||
{
|
||||
this.fYesAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.noClick = function ()
|
||||
{
|
||||
this.cancelCommand();
|
||||
|
||||
if (Utils.isFunc(this.fNoAction))
|
||||
{
|
||||
this.fNoAction.call(null);
|
||||
}
|
||||
|
||||
this.cancelCommand();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -10999,6 +11084,9 @@ PopupsAskViewModel.prototype.onShow = function (sAskDesc, fYesFunc, fNoFunc, sYe
|
|||
{
|
||||
this.yesButton(sNoButton);
|
||||
}
|
||||
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.PopupAsk);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onFocus = function ()
|
||||
|
|
@ -11008,35 +11096,55 @@ PopupsAskViewModel.prototype.onFocus = function ()
|
|||
|
||||
PopupsAskViewModel.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
PopupsAskViewModel.prototype.onBuild = function ()
|
||||
{
|
||||
var self = this;
|
||||
$window.on('keydown', function (oEvent) {
|
||||
var bResult = true;
|
||||
if (oEvent && self.modalVisibility())
|
||||
key('tab, right, left', Enums.KeyState.PopupAsk, _.bind(function () {
|
||||
if (this.modalVisibility())
|
||||
{
|
||||
if (Enums.EventKeyCode.Tab === oEvent.keyCode || Enums.EventKeyCode.Right === oEvent.keyCode || Enums.EventKeyCode.Left === oEvent.keyCode)
|
||||
if (this.yesFocus())
|
||||
{
|
||||
if (self.yesFocus())
|
||||
{
|
||||
self.noFocus(true);
|
||||
this.noFocus(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.yesFocus(true);
|
||||
this.yesFocus(true);
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
*/
|
||||
function PopupsKeyboardShortcutsHelpViewModel()
|
||||
{
|
||||
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsKeyboardShortcutsHelp');
|
||||
|
||||
this.sKeyScope = Enums.KeyState.None;
|
||||
|
||||
Knoin.constructorEnd(this);
|
||||
}
|
||||
|
||||
Utils.extendAsViewModel('PopupsKeyboardShortcutsHelpViewModel', PopupsKeyboardShortcutsHelpViewModel);
|
||||
|
||||
PopupsKeyboardShortcutsHelpViewModel.prototype.onShow = function ()
|
||||
{
|
||||
this.sKeyScope = RL.data().keyScope();
|
||||
RL.data().keyScope(Enums.KeyState.None);
|
||||
};
|
||||
|
||||
PopupsKeyboardShortcutsHelpViewModel.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(this.sKeyScope);
|
||||
};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends KnoinAbstractViewModel
|
||||
|
|
@ -11461,11 +11569,19 @@ Utils.extendAsViewModel('MailBoxFolderListViewModel', MailBoxFolderListViewModel
|
|||
MailBoxFolderListViewModel.prototype.onBuild = function (oDom)
|
||||
{
|
||||
oDom
|
||||
.on('click', '.b-folders .b-content', function () {
|
||||
if (RL.data().useKeyboardShortcuts() && RL.data().message.focused())
|
||||
{
|
||||
RL.data().message.focused(false);
|
||||
}
|
||||
})
|
||||
.on('click', '.b-folders .e-item .e-link .e-collapsed-sign', function (oEvent) {
|
||||
|
||||
var
|
||||
oFolder = ko.dataFor(this),
|
||||
bCollapsed = false
|
||||
;
|
||||
|
||||
if (oFolder && oEvent)
|
||||
{
|
||||
bCollapsed = oFolder.collapsed();
|
||||
|
|
@ -11582,6 +11698,7 @@ function MailBoxMessageListViewModel()
|
|||
|
||||
this.popupVisibility = RL.popupVisibility;
|
||||
|
||||
this.message = oData.message;
|
||||
this.messageList = oData.messageList;
|
||||
this.currentMessage = oData.currentMessage;
|
||||
this.isMessageSelected = oData.isMessageSelected;
|
||||
|
|
@ -11780,13 +11897,6 @@ function MailBoxMessageListViewModel()
|
|||
return oMessage ? oMessage.generateUid() : '';
|
||||
});
|
||||
|
||||
this.selector.on('onDelete', _.bind(function () {
|
||||
if (0 < RL.data().messageListCheckedOrSelected().length)
|
||||
{
|
||||
this.deleteCommand();
|
||||
}
|
||||
}, this));
|
||||
|
||||
RL
|
||||
.sub('mailbox.message-list.selector.go-down', function () {
|
||||
this.selector.goDown();
|
||||
|
|
@ -12106,31 +12216,15 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
return false;
|
||||
});
|
||||
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable);
|
||||
|
||||
$document.on('keydown', function (oEvent) {
|
||||
|
||||
var
|
||||
bResult = true,
|
||||
iKeyCode = oEvent ? oEvent.keyCode : 0
|
||||
;
|
||||
|
||||
if (oEvent && self.viewModelVisibility() && oData.useKeyboardShortcuts() && !RL.popupVisibility() && !oData.messageFullScreenMode() && !Utils.inFocus())
|
||||
{
|
||||
if (Enums.Layout.NoPreview !== oData.layout() || (!oData.message() && (Enums.EventKeyCode.Delete === iKeyCode || Enums.EventKeyCode.A === iKeyCode)))
|
||||
{
|
||||
if (oEvent.ctrlKey && Enums.EventKeyCode.A === iKeyCode)
|
||||
{
|
||||
self.checkAll(!(self.checkAll() && !self.isIncompleteChecked()));
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
this.selector.init(this.oContentVisible, this.oContentScrollable, Enums.KeyState.MessageList);
|
||||
|
||||
oDom
|
||||
.on('click', '.messageList .b-message-list-wrapper', function () {
|
||||
if (oData.useKeyboardShortcuts() && self.message.focused())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
})
|
||||
.on('click', '.e-pagenator .e-page', function () {
|
||||
var oPage = ko.dataFor(this);
|
||||
if (oPage)
|
||||
|
|
@ -12191,6 +12285,7 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
}, this).extend({'notify': 'always'});
|
||||
|
||||
this.initUploaderForAppend();
|
||||
this.initShortcuts();
|
||||
|
||||
if (!Globals.bMobileDevice && !!RL.settingsGet('AllowPrefetch') && ifvisible)
|
||||
{
|
||||
|
|
@ -12202,6 +12297,121 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
|
|||
}
|
||||
};
|
||||
|
||||
MailBoxMessageListViewModel.prototype.initShortcuts = function ()
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
oData = RL.data()
|
||||
;
|
||||
|
||||
// disable print
|
||||
key('ctrl+p, command+p', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// delete
|
||||
key('delete, shift+delete', Enums.KeyState.MessageList, function (event, handler) {
|
||||
if (oData.useKeyboardShortcuts() && event)
|
||||
{
|
||||
if (0 < RL.data().messageListCheckedOrSelected().length)
|
||||
{
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
self.deleteWithoutMoveCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// check all
|
||||
key('ctrl+a, command+a', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.checkAll(!(self.checkAll() && !self.isIncompleteChecked()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// new message (open compose popup)
|
||||
key('c,n', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
kn.showScreenPopup(PopupsComposeViewModel);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// shortcuts help
|
||||
key('shift+/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
kn.showScreenPopup(PopupsKeyboardShortcutsHelpViewModel);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// search input focus
|
||||
key('/', [Enums.KeyState.MessageList, Enums.KeyState.MessageView], function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
|
||||
self.inputMessageListSearchFocus(true);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// cancel search
|
||||
key('esc', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts() && '' !== self.messageListSearchDesc())
|
||||
{
|
||||
self.cancelSearch();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// change focused state
|
||||
key('tab, enter', Enums.KeyState.MessageList, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+left, command+left', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+right, command+right', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
MailBoxMessageListViewModel.prototype.prefetchNextTick = function ()
|
||||
{
|
||||
if (!this.bPrefetch && !ifvisible.now() && this.viewModelVisibility())
|
||||
|
|
@ -12329,6 +12539,7 @@ function MailBoxMessageViewViewModel()
|
|||
|
||||
this.oMessageScrollerDom = null;
|
||||
|
||||
this.keyScope = oData.keyScope;
|
||||
this.message = oData.message;
|
||||
this.messageLoading = oData.messageLoading;
|
||||
this.messageLoadingThrottle = oData.messageLoadingThrottle;
|
||||
|
|
@ -12344,6 +12555,7 @@ function MailBoxMessageViewViewModel()
|
|||
this.fullScreenMode = oData.messageFullScreenMode;
|
||||
|
||||
this.showFullInfo = ko.observable(false);
|
||||
this.messageDomFocused = ko.observable(false);
|
||||
|
||||
this.messageVisibility = ko.computed(function () {
|
||||
return !this.messageLoadingThrottle() && !!this.message();
|
||||
|
|
@ -12376,36 +12588,39 @@ function MailBoxMessageViewViewModel()
|
|||
}, this.messageVisibility);
|
||||
|
||||
this.deleteCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.deleteWithoutMoveCommand = Utils.createCommand(this, function () {
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Trash,
|
||||
RL.data().currentFolderFullNameRaw(),
|
||||
RL.data().messageListCheckedOrSelectedUidsWithSubMails(), false);
|
||||
}
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.archiveCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Archive,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
|
||||
}, this.messageVisibility);
|
||||
|
||||
this.spamCommand = Utils.createCommand(this, function () {
|
||||
|
||||
if (this.message())
|
||||
{
|
||||
RL.deleteMessagesFromFolder(Enums.FolderType.Spam,
|
||||
this.message().folderFullNameRaw,
|
||||
[this.message().uid], true);
|
||||
}
|
||||
|
||||
}, this.messageVisibility);
|
||||
|
||||
// viewer
|
||||
|
|
@ -12600,27 +12815,12 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
oData = RL.data()
|
||||
;
|
||||
|
||||
$document.on('keydown', function (oEvent) {
|
||||
|
||||
var
|
||||
bResult = true,
|
||||
iKeyCode = oEvent ? oEvent.keyCode : 0
|
||||
;
|
||||
|
||||
if (0 < iKeyCode && (Enums.EventKeyCode.Esc === iKeyCode) &&
|
||||
self.viewModelVisibility() && oData.useKeyboardShortcuts() && !Utils.inFocus() && oData.message())
|
||||
this.fullScreenMode.subscribe(function (bValue) {
|
||||
if (bValue)
|
||||
{
|
||||
self.fullScreenMode(false);
|
||||
if (Enums.Layout.NoPreview === oData.layout())
|
||||
{
|
||||
RL.historyBack();
|
||||
self.message.focused(true);
|
||||
}
|
||||
|
||||
bResult = false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
});
|
||||
}, this);
|
||||
|
||||
$('.attachmentsPlace', oDom).magnificPopup({
|
||||
'delegate': '.magnificPopupImage:visible',
|
||||
|
|
@ -12643,6 +12843,12 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
});
|
||||
|
||||
oDom
|
||||
.on('click', '.messageView .messageItem', function () {
|
||||
if (oData.useKeyboardShortcuts() && self.message())
|
||||
{
|
||||
self.message.focused(true);
|
||||
}
|
||||
})
|
||||
.on('mousedown', 'a', function (oEvent) {
|
||||
// setup maito protocol
|
||||
return !(oEvent && 3 !== oEvent['which'] && RL.mailToHelper($(this).attr('href')));
|
||||
|
|
@ -12666,8 +12872,171 @@ MailBoxMessageViewViewModel.prototype.onBuild = function (oDom)
|
|||
})
|
||||
;
|
||||
|
||||
this.message.focused.subscribe(function (bValue) {
|
||||
this.messageDomFocused(!!bValue);
|
||||
}, this);
|
||||
|
||||
this.keyScope.subscribe(function (sValue) {
|
||||
if (Enums.KeyState.MessageView === sValue && this.message.focused())
|
||||
{
|
||||
this.messageDomFocused(true);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.oMessageScrollerDom = oDom.find('.messageItem .content');
|
||||
this.oMessageScrollerDom = this.oMessageScrollerDom && this.oMessageScrollerDom[0] ? this.oMessageScrollerDom : null;
|
||||
|
||||
this.initShortcuts();
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
MailBoxMessageViewViewModel.prototype.escShortcuts = function ()
|
||||
{
|
||||
if (this.viewModelVisibility() && RL.data().useKeyboardShortcuts() && this.message())
|
||||
{
|
||||
if (this.fullScreenMode())
|
||||
{
|
||||
this.fullScreenMode(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.message.focused(false);
|
||||
}
|
||||
|
||||
if (Enums.Layout.NoPreview === RL.data().layout())
|
||||
{
|
||||
RL.historyBack();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
MailBoxMessageViewViewModel.prototype.initShortcuts = function ()
|
||||
{
|
||||
var
|
||||
self = this,
|
||||
oData = RL.data()
|
||||
;
|
||||
|
||||
// exit fullscreen, back
|
||||
key('esc', Enums.KeyState.MessageView, _.bind(this.escShortcuts, this));
|
||||
|
||||
// fullscreen
|
||||
key('enter', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.toggleFullScreen();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// reply
|
||||
key('r', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.replyCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// replaAll
|
||||
key('a', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.replyAllCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// forward
|
||||
key('f', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.forwardCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// message information
|
||||
key('i', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.showFullInfo(!self.showFullInfo());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+left, command+left', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.goDownCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
key('ctrl+right, command+right', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
self.goUpCommand();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// print
|
||||
key('ctrl+p, command+p', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message().printMessage();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// archive
|
||||
// key('delete', Enums.KeyState.MessageView, function () {
|
||||
// if (oData.useKeyboardShortcuts())
|
||||
// {
|
||||
// self.archiveCommand();
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
|
||||
// delete
|
||||
key('delete, shift+delete', Enums.KeyState.MessageView, function (event, handler) {
|
||||
if (oData.useKeyboardShortcuts() && event)
|
||||
{
|
||||
self.deleteCommand();
|
||||
if (handler && 'shift+delete' === handler.shortcut)
|
||||
{
|
||||
// self.deleteWithoutMoveCommand();
|
||||
self.deleteCommand();
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deleteCommand();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// change focused state
|
||||
key('tab', Enums.KeyState.MessageView, function () {
|
||||
if (oData.useKeyboardShortcuts())
|
||||
{
|
||||
if (self.message())
|
||||
{
|
||||
self.message.focused(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -14241,12 +14610,27 @@ function WebMailDataStorage()
|
|||
this.accountIncLogin = ko.observable('');
|
||||
this.accountOutLogin = ko.observable('');
|
||||
this.projectHash = ko.observable('');
|
||||
|
||||
this.threading = ko.observable(false);
|
||||
this.lastFoldersHash = '';
|
||||
|
||||
this.lastFoldersHash = '';
|
||||
this.remoteSuggestions = false;
|
||||
|
||||
this.keyScope = ko.observable(Enums.KeyState.None);
|
||||
this.keyScope.subscribe(function (sValue) {
|
||||
|
||||
if (Enums.KeyState.Compose === sValue)
|
||||
{
|
||||
Utils.disableKeyFilter();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.restoreKeyFilter();
|
||||
}
|
||||
|
||||
// window.console.log(sValue);
|
||||
key.setScope(sValue);
|
||||
});
|
||||
|
||||
// system folders
|
||||
this.sentFolder = ko.observable('');
|
||||
this.draftFolder = ko.observable('');
|
||||
|
|
@ -14461,6 +14845,23 @@ function WebMailDataStorage()
|
|||
this.messageLoading = ko.observable(false);
|
||||
this.messageLoadingThrottle = ko.observable(false).extend({'throttle': 50});
|
||||
|
||||
this.message.focused = ko.observable(false);
|
||||
|
||||
this.message.subscribe(function (oMessage) {
|
||||
if (!oMessage)
|
||||
{
|
||||
this.message.focused(false);
|
||||
}
|
||||
else if (Enums.Layout.NoPreview === this.layout())
|
||||
{
|
||||
this.message.focused(true);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.message.focused.subscribe(function (bValue) {
|
||||
RL.data().keyScope(bValue ? Enums.KeyState.MessageView : Enums.KeyState.MessageList);
|
||||
});
|
||||
|
||||
this.messageLoading.subscribe(function (bValue) {
|
||||
this.messageLoadingThrottle(bValue);
|
||||
}, this);
|
||||
|
|
@ -17063,6 +17464,13 @@ MailBoxScreen.prototype.setNewTitle = function ()
|
|||
MailBoxScreen.prototype.onShow = function ()
|
||||
{
|
||||
this.setNewTitle();
|
||||
|
||||
RL.data().keyScope(Enums.KeyState.MessageList);
|
||||
};
|
||||
|
||||
MailBoxScreen.prototype.onHide = function ()
|
||||
{
|
||||
RL.data().keyScope(Enums.KeyState.None);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -18752,4 +19160,4 @@ if (window.SimplePace) {
|
|||
window.SimplePace.add(10);
|
||||
}
|
||||
|
||||
}(window, jQuery, ko, crossroads, hasher, moment, Jua, _, ifvisible));
|
||||
}(window, jQuery, ko, crossroads, hasher, moment, Jua, _, ifvisible, key));
|
||||
17
rainloop/v/0.0.0/static/js/app.min.js
vendored
17
rainloop/v/0.0.0/static/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -227,6 +227,314 @@ this.j[c]=i,g.on("click",function(){var a=b.b.d("onDialog");a&&a()}).on("change"
|
|||
j.c=e;j.h=0;j.a={};j.m={};j.q=f;j.e=f;j.on=function(a,b){this.m[a]=b;return this};j.g=function(a,b){this.m[a]&&this.m[a].apply(f,b||[])};j.d=function(a){return this.m[a]||f};j.cancel=function(a){this.e.cancel(a)};j.p=function(){return B.p};j.v=function(a){this.c=!!a};j.k=function(){return this.e.k()};j.s=function(a){this.r(w(),a)};
|
||||
j.r=function(a,b){var c=this.d("onSelect");if(b&&(!c||h!==c(a,b))){this.e.t(a);var g=this.e.u,d=this.e;this.q.defer(function(){return g.apply(q(d)?f:d,Array.prototype.slice.call(arguments))},a,b)}else this.e.cancel(a)};l.Jua=B;
|
||||
|
||||
// keymaster.js
|
||||
// (c) 2011-2013 Thomas Fuchs
|
||||
// keymaster.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function(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 = function(x){
|
||||
return _MAP[x] || x.toUpperCase().charCodeAt(0);
|
||||
},
|
||||
_downKeys = [];
|
||||
|
||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
|
||||
|
||||
// IE doesn't support Array#indexOf, so have a simple replacement
|
||||
function index(array, item){
|
||||
var i = array.length;
|
||||
while(i--) if(array[i]===item) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// for comparing mods before unassignment
|
||||
function compareArray(a1, a2) {
|
||||
if (a1.length != a2.length) return false;
|
||||
for (var i = 0; i < a1.length; i++) {
|
||||
if (a1[i] !== a2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var modifierMap = {
|
||||
16:'shiftKey',
|
||||
18:'altKey',
|
||||
17:'ctrlKey',
|
||||
91:'metaKey'
|
||||
};
|
||||
function updateModifierKey(event) {
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
key = event.keyCode;
|
||||
|
||||
if (index(_downKeys, key) == -1) {
|
||||
_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;
|
||||
}
|
||||
updateModifierKey(event);
|
||||
|
||||
// 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
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
handler = _handlers[key][i];
|
||||
|
||||
// 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] && index(handler.mods, +k) > -1) ||
|
||||
(_mods[k] && index(handler.mods, +k) == -1)) 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){
|
||||
if(event.preventDefault) event.preventDefault();
|
||||
else event.returnValue = false;
|
||||
if(event.stopPropagation) event.stopPropagation();
|
||||
if(event.cancelBubble) event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unset modifier keys on keyup
|
||||
function clearModifier(event){
|
||||
var key = event.keyCode, k,
|
||||
i = index(_downKeys, 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;
|
||||
}
|
||||
};
|
||||
|
||||
function resetModifiers() {
|
||||
for(k in _mods) _mods[k] = false;
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
};
|
||||
|
||||
// parse and assign shortcut
|
||||
function assignKey(key, scope, method){
|
||||
var keys, mods, bScopeIsArray = false;
|
||||
keys = getKeys(key);
|
||||
if (method === undefined) {
|
||||
method = scope;
|
||||
scope = 'all';
|
||||
}
|
||||
|
||||
bScopeIsArray = !!(typeof scope !== 'string' && scope.length && typeof scope[0] === 'string');
|
||||
|
||||
// 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 = getMods(key);
|
||||
key = [key[key.length-1]];
|
||||
}
|
||||
// convert to keycode and...
|
||||
key = key[0];
|
||||
key = code(key);
|
||||
// ...store handler
|
||||
if (!(key in _handlers)) _handlers[key] = [];
|
||||
|
||||
if (bScopeIsArray) {
|
||||
for (var j = 0; j < scope.length; j++) {
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope[j], method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
} else {
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unbind all handlers for given key in current scope
|
||||
function unbindKey(key, scope) {
|
||||
var multipleKeys, keys,
|
||||
mods = [],
|
||||
i, j, obj;
|
||||
|
||||
multipleKeys = getKeys(key);
|
||||
|
||||
for (j = 0; j < multipleKeys.length; j++) {
|
||||
keys = multipleKeys[j].split('+');
|
||||
|
||||
if (keys.length > 1) {
|
||||
mods = getMods(keys);
|
||||
key = keys[keys.length - 1];
|
||||
}
|
||||
|
||||
key = code(key);
|
||||
|
||||
if (scope === undefined) {
|
||||
scope = getScope();
|
||||
}
|
||||
if (!_handlers[key]) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
obj = _handlers[key][i];
|
||||
// only clear handlers if correct scope and mods match
|
||||
if (obj.scope === scope && compareArray(obj.mods, mods)) {
|
||||
_handlers[key][i] = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if the key with code 'keyCode' is currently down
|
||||
// Converts strings into key codes.
|
||||
function isPressed(keyCode) {
|
||||
if (typeof(keyCode)=='string') {
|
||||
keyCode = code(keyCode);
|
||||
}
|
||||
return index(_downKeys, keyCode) != -1;
|
||||
}
|
||||
|
||||
function getPressedKeyCodes() {
|
||||
return _downKeys.slice(0);
|
||||
}
|
||||
|
||||
function filter(event){
|
||||
var tagName = (event.target || event.srcElement).tagName;
|
||||
// ignore keypressed in any elements that support keyboard data input
|
||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
||||
}
|
||||
|
||||
// initialize key.<modifier> to false
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
|
||||
// set current scope (default 'all')
|
||||
function setScope(scope){ _scope = scope || 'all' };
|
||||
function getScope(){ return _scope || 'all' };
|
||||
|
||||
// delete all handlers for a given scope
|
||||
function deleteScope(scope){
|
||||
var key, handlers, i;
|
||||
|
||||
for (key in _handlers) {
|
||||
handlers = _handlers[key];
|
||||
for (i = 0; i < handlers.length; ) {
|
||||
if (handlers[i].scope === scope) handlers.splice(i, 1);
|
||||
else i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// abstract key logic for assign and unassign
|
||||
function getKeys(key) {
|
||||
var keys;
|
||||
key = key.replace(/\s/g, '');
|
||||
keys = key.split(',');
|
||||
if ((keys[keys.length - 1]) == '') {
|
||||
keys[keys.length - 2] += ',';
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// abstract mods logic for assign and unassign
|
||||
function getMods(key) {
|
||||
var mods = key.slice(0, key.length - 1);
|
||||
for (var mi = 0; mi < mods.length; mi++)
|
||||
mods[mi] = _MODIFIERS[mods[mi]];
|
||||
return mods;
|
||||
}
|
||||
|
||||
// cross-browser events
|
||||
function addEvent(object, event, method) {
|
||||
if (object.addEventListener)
|
||||
object.addEventListener(event, method, false);
|
||||
else if(object.attachEvent)
|
||||
object.attachEvent('on'+event, function(){ method(window.event) });
|
||||
};
|
||||
|
||||
// set the handlers globally on document
|
||||
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
|
||||
addEvent(document, 'keyup', clearModifier);
|
||||
|
||||
// reset modifiers to false whenever the window is (re)focused.
|
||||
addEvent(window, 'focus', resetModifiers);
|
||||
|
||||
// store previously defined key
|
||||
var previousKey = global.key;
|
||||
|
||||
// restore previously defined key and return reference to our key object
|
||||
function noConflict() {
|
||||
var k = global.key;
|
||||
global.key = previousKey;
|
||||
return k;
|
||||
}
|
||||
|
||||
// set window.key and window.key.set/get/deleteScope, and the default filter
|
||||
global.key = assignKey;
|
||||
global.key.setScope = setScope;
|
||||
global.key.getScope = getScope;
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = key;
|
||||
|
||||
})(this);
|
||||
|
||||
|
||||
/*!ifvisible.js v1.0.0 (c) 2013 Serkan Yersen | MIT */
|
||||
(function(){"use strict";var a,b,c,d,e,f,g,h,i,j,k,l,m,n;i={},c=document,k=!1,l="active",g=6e4,f=!1,b=function(){var a,b,c,d,e,f;return a=function(){return(65536*(1+Math.random())|0).toString(16).substring(1)},e=function(){return a()+a()+"-"+a()+"-"+a()+"-"+a()+"-"+a()+a()+a()},f={},c="__ceGUID",b=function(a,b,d){return a[c]=void 0,a[c]||(a[c]="ifvisible.object.event.identifier"),f[a[c]]||(f[a[c]]={}),f[a[c]][b]||(f[a[c]][b]=[]),f[a[c]][b].push(d)},d=function(a,b,d){var e,g,h,i,j;if(a[c]&&f[a[c]]&&f[a[c]][b]){for(i=f[a[c]][b],j=[],g=0,h=i.length;h>g;g++)e=i[g],j.push(e(d||{}));return j}},{add:b,fire:d}}(),a=function(){var a;return a=!1,function(b,c,d){return a||(a=b.addEventListener?function(a,b,c){return a.addEventListener(b,c,!1)}:b.attachEvent?function(a,b,c){return a.attachEvent("on"+b,c,!1)}:function(a,b,c){return a["on"+b]=c}),a(b,c,d)}}(),d=function(a,b){var d;return c.createEventObject?a.fireEvent("on"+b,d):(d=c.createEvent("HTMLEvents"),d.initEvent(b,!0,!0),!a.dispatchEvent(d))},h=function(){var a,b,d,e,f;for(e=void 0,f=3,d=c.createElement("div"),a=d.getElementsByTagName("i"),b=function(){return d.innerHTML="<!--[if gt IE "+ ++f+"]><i></i><![endif]-->",a[0]};b(););return f>4?f:e}(),e=!1,n=void 0,"undefined"!=typeof c.hidden?(e="hidden",n="visibilitychange"):"undefined"!=typeof c.mozHidden?(e="mozHidden",n="mozvisibilitychange"):"undefined"!=typeof c.msHidden?(e="msHidden",n="msvisibilitychange"):"undefined"!=typeof c.webkitHidden&&(e="webkitHidden",n="webkitvisibilitychange"),m=function(){var b,d;return b=!1,d=function(){return clearTimeout(b),"active"!==l&&i.wakeup(),f=+new Date,b=setTimeout(function(){return"active"===l?i.idle():void 0},g)},d(),a(c,"mousemove",d),a(c,"keyup",d),a(window,"scroll",d),i.focus(d)},j=function(){var b;return k?!0:(e===!1?(b="blur",9>h&&(b="focusout"),a(window,b,function(){return i.blur()}),a(window,"focus",function(){return i.focus()})):a(c,n,function(){return c[e]?i.blur():i.focus()},!1),k=!0,m())},i={setIdleDuration:function(a){return g=1e3*a},getIdleDuration:function(){return g},getIdleInfo:function(){var a,b;return a=+new Date,b={},"idle"===l?(b.isIdle=!0,b.idleFor=a-f,b.timeLeft=0,b.timeLeftPer=100):(b.isIdle=!1,b.idleFor=a-f,b.timeLeft=f+g-a,b.timeLeftPer=(100-100*b.timeLeft/g).toFixed(2)),b},focus:function(a){return"function"==typeof a?this.on("focus",a):(l="active",b.fire(this,"focus"),b.fire(this,"wakeup"),b.fire(this,"statusChanged",{status:l}))},blur:function(a){return"function"==typeof a?this.on("blur",a):(l="hidden",b.fire(this,"blur"),b.fire(this,"idle"),b.fire(this,"statusChanged",{status:l}))},idle:function(a){return"function"==typeof a?this.on("idle",a):(l="idle",b.fire(this,"idle"),b.fire(this,"statusChanged",{status:l}))},wakeup:function(a){return"function"==typeof a?this.on("wakeup",a):(l="active",b.fire(this,"wakeup"),b.fire(this,"statusChanged",{status:l}))},on:function(a,c){return j(),b.add(this,a,c)},onEvery:function(a,b){var c;return j(),c=setInterval(function(){return"active"===l?b():void 0},1e3*a),{stop:function(){return clearInterval(c)},code:c,callback:b}},now:function(){return j(),"active"===l}},"function"==typeof define&&define.amd?define(function(){return i}):window.ifvisible=i}).call(this);
|
||||
|
||||
|
|
|
|||
20
vendors/keymaster/MIT-LICENSE
vendored
Normal file
20
vendors/keymaster/MIT-LICENSE
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
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
Normal file
212
vendors/keymaster/README.markdown
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# 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.
|
||||
306
vendors/keymaster/keymaster.js
vendored
Normal file
306
vendors/keymaster/keymaster.js
vendored
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
// keymaster.js
|
||||
// (c) 2011-2013 Thomas Fuchs
|
||||
// keymaster.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function(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 = function(x){
|
||||
return _MAP[x] || x.toUpperCase().charCodeAt(0);
|
||||
},
|
||||
_downKeys = [];
|
||||
|
||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
|
||||
|
||||
// IE doesn't support Array#indexOf, so have a simple replacement
|
||||
function index(array, item){
|
||||
var i = array.length;
|
||||
while(i--) if(array[i]===item) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// for comparing mods before unassignment
|
||||
function compareArray(a1, a2) {
|
||||
if (a1.length != a2.length) return false;
|
||||
for (var i = 0; i < a1.length; i++) {
|
||||
if (a1[i] !== a2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var modifierMap = {
|
||||
16:'shiftKey',
|
||||
18:'altKey',
|
||||
17:'ctrlKey',
|
||||
91:'metaKey'
|
||||
};
|
||||
function updateModifierKey(event) {
|
||||
for(k in _mods) _mods[k] = event[modifierMap[k]];
|
||||
};
|
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) {
|
||||
var key, handler, k, i, modifiersMatch, scope;
|
||||
key = event.keyCode;
|
||||
|
||||
if (index(_downKeys, key) == -1) {
|
||||
_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;
|
||||
}
|
||||
updateModifierKey(event);
|
||||
|
||||
// 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
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
handler = _handlers[key][i];
|
||||
|
||||
// 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] && index(handler.mods, +k) > -1) ||
|
||||
(_mods[k] && index(handler.mods, +k) == -1)) 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){
|
||||
if(event.preventDefault) event.preventDefault();
|
||||
else event.returnValue = false;
|
||||
if(event.stopPropagation) event.stopPropagation();
|
||||
if(event.cancelBubble) event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unset modifier keys on keyup
|
||||
function clearModifier(event){
|
||||
var key = event.keyCode, k,
|
||||
i = index(_downKeys, 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;
|
||||
}
|
||||
};
|
||||
|
||||
function resetModifiers() {
|
||||
for(k in _mods) _mods[k] = false;
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
};
|
||||
|
||||
// parse and assign shortcut
|
||||
function assignKey(key, scope, method){
|
||||
var keys, mods, bScopeIsArray = false;
|
||||
keys = getKeys(key);
|
||||
if (method === undefined) {
|
||||
method = scope;
|
||||
scope = 'all';
|
||||
}
|
||||
|
||||
bScopeIsArray = !!(typeof scope !== 'string' && scope.length && typeof scope[0] === 'string');
|
||||
|
||||
// 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 = getMods(key);
|
||||
key = [key[key.length-1]];
|
||||
}
|
||||
// convert to keycode and...
|
||||
key = key[0];
|
||||
key = code(key);
|
||||
// ...store handler
|
||||
if (!(key in _handlers)) _handlers[key] = [];
|
||||
|
||||
if (bScopeIsArray) {
|
||||
for (var j = 0; j < scope.length; j++) {
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope[j], method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
} else {
|
||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// unbind all handlers for given key in current scope
|
||||
function unbindKey(key, scope) {
|
||||
var multipleKeys, keys,
|
||||
mods = [],
|
||||
i, j, obj;
|
||||
|
||||
multipleKeys = getKeys(key);
|
||||
|
||||
for (j = 0; j < multipleKeys.length; j++) {
|
||||
keys = multipleKeys[j].split('+');
|
||||
|
||||
if (keys.length > 1) {
|
||||
mods = getMods(keys);
|
||||
key = keys[keys.length - 1];
|
||||
}
|
||||
|
||||
key = code(key);
|
||||
|
||||
if (scope === undefined) {
|
||||
scope = getScope();
|
||||
}
|
||||
if (!_handlers[key]) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < _handlers[key].length; i++) {
|
||||
obj = _handlers[key][i];
|
||||
// only clear handlers if correct scope and mods match
|
||||
if (obj.scope === scope && compareArray(obj.mods, mods)) {
|
||||
_handlers[key][i] = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns true if the key with code 'keyCode' is currently down
|
||||
// Converts strings into key codes.
|
||||
function isPressed(keyCode) {
|
||||
if (typeof(keyCode)=='string') {
|
||||
keyCode = code(keyCode);
|
||||
}
|
||||
return index(_downKeys, keyCode) != -1;
|
||||
}
|
||||
|
||||
function getPressedKeyCodes() {
|
||||
return _downKeys.slice(0);
|
||||
}
|
||||
|
||||
function filter(event){
|
||||
var tagName = (event.target || event.srcElement).tagName;
|
||||
// ignore keypressed in any elements that support keyboard data input
|
||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
||||
}
|
||||
|
||||
// initialize key.<modifier> to false
|
||||
for(k in _MODIFIERS) assignKey[k] = false;
|
||||
|
||||
// set current scope (default 'all')
|
||||
function setScope(scope){ _scope = scope || 'all' };
|
||||
function getScope(){ return _scope || 'all' };
|
||||
|
||||
// delete all handlers for a given scope
|
||||
function deleteScope(scope){
|
||||
var key, handlers, i;
|
||||
|
||||
for (key in _handlers) {
|
||||
handlers = _handlers[key];
|
||||
for (i = 0; i < handlers.length; ) {
|
||||
if (handlers[i].scope === scope) handlers.splice(i, 1);
|
||||
else i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// abstract key logic for assign and unassign
|
||||
function getKeys(key) {
|
||||
var keys;
|
||||
key = key.replace(/\s/g, '');
|
||||
keys = key.split(',');
|
||||
if ((keys[keys.length - 1]) == '') {
|
||||
keys[keys.length - 2] += ',';
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// abstract mods logic for assign and unassign
|
||||
function getMods(key) {
|
||||
var mods = key.slice(0, key.length - 1);
|
||||
for (var mi = 0; mi < mods.length; mi++)
|
||||
mods[mi] = _MODIFIERS[mods[mi]];
|
||||
return mods;
|
||||
}
|
||||
|
||||
// cross-browser events
|
||||
function addEvent(object, event, method) {
|
||||
if (object.addEventListener)
|
||||
object.addEventListener(event, method, false);
|
||||
else if(object.attachEvent)
|
||||
object.attachEvent('on'+event, function(){ method(window.event) });
|
||||
};
|
||||
|
||||
// set the handlers globally on document
|
||||
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
|
||||
addEvent(document, 'keyup', clearModifier);
|
||||
|
||||
// reset modifiers to false whenever the window is (re)focused.
|
||||
addEvent(window, 'focus', resetModifiers);
|
||||
|
||||
// store previously defined key
|
||||
var previousKey = global.key;
|
||||
|
||||
// restore previously defined key and return reference to our key object
|
||||
function noConflict() {
|
||||
var k = global.key;
|
||||
global.key = previousKey;
|
||||
return k;
|
||||
}
|
||||
|
||||
// set window.key and window.key.set/get/deleteScope, and the default filter
|
||||
global.key = assignKey;
|
||||
global.key.setScope = setScope;
|
||||
global.key.getScope = getScope;
|
||||
global.key.deleteScope = deleteScope;
|
||||
global.key.filter = filter;
|
||||
global.key.isPressed = isPressed;
|
||||
global.key.getPressedKeyCodes = getPressedKeyCodes;
|
||||
global.key.noConflict = noConflict;
|
||||
global.key.unbind = unbindKey;
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = key;
|
||||
|
||||
})(this);
|
||||
11
vendors/keymaster/package.json
vendored
Normal file
11
vendors/keymaster/package.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"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…
Add table
Add a link
Reference in a new issue