851 lines
25 KiB
JavaScript
851 lines
25 KiB
JavaScript
/* -*- js-indent-level: 8 -*- */
|
|
/*
|
|
* L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
|
|
*
|
|
* It handles keyboard interactions which are NOT text input, including those which
|
|
* don't require edit permissions (e.g. page scroll). Text input is handled
|
|
* at TextInput.
|
|
*/
|
|
|
|
/* global app UNOKey UNOModifier */
|
|
|
|
L.Map.mergeOptions({
|
|
keyboard: true,
|
|
keyboardPanOffset: 20,
|
|
keyboardZoomOffset: 1
|
|
});
|
|
|
|
L.Map.Keyboard = L.Handler.extend({
|
|
|
|
keymap: {
|
|
8 : UNOKey.BACKSPACE,
|
|
9 : UNOKey.TAB,
|
|
13 : UNOKey.RETURN,
|
|
16 : null, // shift : UNKOWN
|
|
17 : null, // ctrl : UNKOWN
|
|
18 : null, // alt : UNKOWN
|
|
19 : null, // pause/break : UNKOWN
|
|
20 : null, // caps lock : UNKOWN
|
|
27 : UNOKey.ESCAPE,
|
|
32 : UNOKey.SPACE,
|
|
33 : UNOKey.PAGEUP,
|
|
34 : UNOKey.PAGEDOWN,
|
|
35 : UNOKey.END,
|
|
36 : UNOKey.HOME,
|
|
37 : UNOKey.LEFT,
|
|
38 : UNOKey.UP,
|
|
39 : UNOKey.RIGHT,
|
|
40 : UNOKey.DOWN,
|
|
45 : UNOKey.INSERT,
|
|
46 : UNOKey.DELETE,
|
|
48 : UNOKey.NUM0,
|
|
49 : UNOKey.NUM1,
|
|
50 : UNOKey.NUM2,
|
|
51 : UNOKey.NUM3,
|
|
52 : UNOKey.NUM4,
|
|
53 : UNOKey.NUM5,
|
|
54 : UNOKey.NUM6,
|
|
55 : UNOKey.NUM7,
|
|
56 : UNOKey.NUM8,
|
|
57 : UNOKey.NUM9,
|
|
65 : UNOKey.A,
|
|
66 : UNOKey.B,
|
|
67 : UNOKey.C,
|
|
68 : UNOKey.D,
|
|
69 : UNOKey.E,
|
|
70 : UNOKey.F,
|
|
71 : UNOKey.G,
|
|
72 : UNOKey.H,
|
|
73 : UNOKey.I,
|
|
74 : UNOKey.J,
|
|
75 : UNOKey.K,
|
|
76 : UNOKey.L,
|
|
77 : UNOKey.M,
|
|
78 : UNOKey.N,
|
|
79 : UNOKey.O,
|
|
80 : UNOKey.P,
|
|
81 : UNOKey.Q,
|
|
82 : UNOKey.R,
|
|
83 : UNOKey.S,
|
|
84 : UNOKey.T,
|
|
85 : UNOKey.U,
|
|
86 : UNOKey.V,
|
|
87 : UNOKey.W,
|
|
88 : UNOKey.X,
|
|
89 : UNOKey.Y,
|
|
90 : UNOKey.Z,
|
|
91 : null, // left window key : UNKOWN
|
|
92 : null, // right window key : UNKOWN
|
|
93 : null, // select key : UNKOWN
|
|
96 : UNOKey.NUM0,
|
|
97 : UNOKey.NUM1,
|
|
98 : UNOKey.NUM2,
|
|
99 : UNOKey.NUM3,
|
|
100 : UNOKey.NUM4,
|
|
101 : UNOKey.NUM5,
|
|
102 : UNOKey.NUM6,
|
|
103 : UNOKey.NUM7,
|
|
104 : UNOKey.NUM8,
|
|
105 : UNOKey.NUM9,
|
|
106 : UNOKey.MULTIPLY,
|
|
107 : UNOKey.ADD,
|
|
109 : UNOKey.SUBTRACT,
|
|
110 : UNOKey.DECIMAL,
|
|
111 : UNOKey.DIVIDE,
|
|
112 : UNOKey.F1,
|
|
113 : UNOKey.F2,
|
|
114 : UNOKey.F3,
|
|
115 : UNOKey.F4,
|
|
116 : UNOKey.F5,
|
|
117 : UNOKey.F6,
|
|
118 : UNOKey.F7,
|
|
119 : UNOKey.F8,
|
|
120 : UNOKey.F9,
|
|
121 : UNOKey.F10,
|
|
122 : UNOKey.F11,
|
|
144 : UNOKey.NUMLOCK,
|
|
145 : UNOKey.SCROLLLOCK,
|
|
173 : UNOKey.SUBTRACT,
|
|
186 : UNOKey.SEMICOLON,
|
|
187 : UNOKey.EQUAL,
|
|
188 : UNOKey.COMMA,
|
|
189 : UNOKey.SUBTRACT,
|
|
190 : null, // period : UNKOWN
|
|
191 : null, // forward slash : UNKOWN
|
|
192 : null, // grave accent : UNKOWN
|
|
219 : null, // open bracket : UNKOWN
|
|
220 : null, // back slash : UNKOWN
|
|
221 : null, // close bracket : UNKOWN
|
|
222 : null // single quote : UNKOWN
|
|
},
|
|
|
|
handleOnKeyDownKeys: {
|
|
// these keys need to be handled on keydown in order for them
|
|
// to work on chrome
|
|
// Backspace and Delete are handled at TextInput's 'beforeinput' handler.
|
|
9 : true, // tab
|
|
19 : true, // pause/break
|
|
20 : true, // caps lock
|
|
27 : true, // escape
|
|
33 : true, // page up
|
|
34 : true, // page down
|
|
35 : true, // end
|
|
36 : true, // home
|
|
37 : true, // left arrow
|
|
38 : true, // up arrow
|
|
39 : true, // right arrow
|
|
40 : true, // down arrow
|
|
45 : true, // insert
|
|
113 : true // f2
|
|
},
|
|
|
|
keyCodes: {
|
|
|
|
pageUp: 33,
|
|
pageDown: 34,
|
|
enter: 13,
|
|
BACKSPACE:8,
|
|
TAB: 9,
|
|
SPACE : 32,
|
|
SHIFT: 16, // shift : UNKOWN
|
|
CTRL: 17, // ctrl : UNKOWN
|
|
ALT: 18, // alt : UNKOWN
|
|
PAUSE: 19, // pause/break : UNKOWN
|
|
CAPSLOCK: 20, // caps lock : UNKOWN,
|
|
END: 35,
|
|
HOME: 36,
|
|
LEFT: 37,
|
|
UP: 38,
|
|
RIGHT: 39,
|
|
DOWN: 40,
|
|
INSERT: 45,
|
|
DELETE: 46,
|
|
NUM0: [48,96], // two values because of different mapping in mac and windows for same keys
|
|
NUM1: [49,97],
|
|
NUM2: [50,98],
|
|
NUM3: [51,99],
|
|
NUM4: [52,100],
|
|
NUM5: [53,101],
|
|
NUM6: [54,102],
|
|
NUM7: [55,103],
|
|
NUM8: [56,103],
|
|
NUM9: [57,104],
|
|
A: 65,
|
|
B: 66,
|
|
C: [67,99],
|
|
//c: 99,
|
|
D: 68,
|
|
E: 69,
|
|
//f: 70, no need for separate as for Windows it will remain the same
|
|
F: 70,
|
|
G: 71,
|
|
H: 72,
|
|
I: 73,
|
|
J: 74,
|
|
K: 75,
|
|
L: 76,
|
|
M: 77,
|
|
N: 78,
|
|
O: 79,
|
|
P: 80,
|
|
Q: 81,
|
|
R: 82,
|
|
S: 83,
|
|
T: 84,
|
|
U: 85,
|
|
V: [86,118],
|
|
//v: 118, // this is for MAC as the value of v changes when we press keydown
|
|
W: 87,
|
|
X: [88,120],
|
|
//x: 120, // this is for MAC as the value of x changes when we press keydown
|
|
Y: 89,
|
|
Z: 90,
|
|
LEFTWINDOWKEY : [91,91], // left window key : UNKOWN and also for MAC
|
|
RIGHTWINDOWKEY: [92,93], // right window key : UNKOWN and also for MAC
|
|
SELECTKEY: 93, // select key : UNKOWN
|
|
// NUM0: 96,
|
|
// NUM1: 97,
|
|
// NUM2: 98,
|
|
// NUM3: 99,
|
|
// NUM4: 100,
|
|
// NUM5: 101,
|
|
// NUM6: 102,
|
|
// NUM7: 103,
|
|
// NUM8: 104,
|
|
MULTIPLY: 106,
|
|
ADD: 107,
|
|
//SUBTRACT: 109,
|
|
DECIMAL: 110,
|
|
DIVIDE: 111,
|
|
F1: 112,
|
|
F2: 113,
|
|
F3: 114,
|
|
F4: 115,
|
|
F5: 116,
|
|
F6: 117,
|
|
F7: 118,
|
|
F8: 119,
|
|
F9: 120,
|
|
F10: 121,
|
|
F11: 122,
|
|
NUMLOCK: 144,
|
|
SCROLLLOCK: 145,
|
|
SUBTRACT: [109,173,189],
|
|
SEMICOLON: 186,
|
|
EQUAL: 187,
|
|
COMMA: 188,
|
|
//SUBTRACT: 189,
|
|
PERIOD: 190, // period : UNKOWN
|
|
FORWARDSLASH: 191, // forward slash : UNKOWN
|
|
GRAVEACCENT: 192, // grave accent : UNKOWN
|
|
OPENBRACKET: 219, // open bracket : UNKOWN
|
|
BACKSLASH: 220, // back slash : UNKOWN
|
|
CLOSEBRACKET: 221, // close bracket : UNKOWN
|
|
SINGLEQUOTE : 222 // single quote : UNKOWN
|
|
|
|
},
|
|
|
|
navigationKeyCodes: {
|
|
left: [37],
|
|
right: [39],
|
|
down: [40],
|
|
up: [38],
|
|
zoomIn: [187, 107, 61, 171],
|
|
zoomOut: [189, 109, 173]
|
|
},
|
|
|
|
initialize: function (map) {
|
|
this._map = map;
|
|
this._setPanOffset(map.options.keyboardPanOffset);
|
|
this._setZoomOffset(map.options.keyboardZoomOffset);
|
|
this.modifier = 0;
|
|
},
|
|
|
|
|
|
addHooks: function () {
|
|
var container = this._map._container;
|
|
|
|
// make the container focusable by tabbing
|
|
if (container.tabIndex === -1) {
|
|
container.tabIndex = '0';
|
|
}
|
|
|
|
L.DomEvent.on(this._map.getContainer(), 'keydown keyup keypress', this._onKeyDown, this);
|
|
L.DomEvent.on(window.document, 'keydown', this._globalKeyEvent, this);
|
|
},
|
|
|
|
removeHooks: function () {
|
|
L.DomEvent.off(this._map.getContainer(), 'keydown keyup keypress', this._onKeyDown, this);
|
|
L.DomEvent.off(window.document, 'keydown', this._globalKeyEvent, this);
|
|
},
|
|
|
|
_ignoreKeyEvent: function(ev) {
|
|
var shift = ev.shiftKey ? UNOModifier.SHIFT : 0;
|
|
if (shift && (ev.keyCode === this.keyCodes.INSERT || ev.keyCode === this.keyCodes.DELETE)) {
|
|
// don't handle shift+insert, shift+delete
|
|
// These are converted to 'cut', 'paste' events which are
|
|
// automatically handled by us, so avoid double-handling
|
|
return true;
|
|
}
|
|
},
|
|
|
|
_setPanOffset: function (pan) {
|
|
var keys = this._panKeys = {},
|
|
codes = this.navigationKeyCodes,
|
|
i, len;
|
|
|
|
for (i = 0, len = codes.left.length; i < len; i++) {
|
|
keys[codes.left[i]] = [-1 * pan, 0];
|
|
}
|
|
for (i = 0, len = codes.right.length; i < len; i++) {
|
|
keys[codes.right[i]] = [pan, 0];
|
|
}
|
|
for (i = 0, len = codes.down.length; i < len; i++) {
|
|
keys[codes.down[i]] = [0, pan];
|
|
}
|
|
for (i = 0, len = codes.up.length; i < len; i++) {
|
|
keys[codes.up[i]] = [0, -1 * pan];
|
|
}
|
|
},
|
|
|
|
_setZoomOffset: function (zoom) {
|
|
var keys = this._zoomKeys = {},
|
|
codes = this.navigationKeyCodes,
|
|
i, len;
|
|
|
|
for (i = 0, len = codes.zoomIn.length; i < len; i++) {
|
|
keys[codes.zoomIn[i]] = zoom;
|
|
}
|
|
for (i = 0, len = codes.zoomOut.length; i < len; i++) {
|
|
keys[codes.zoomOut[i]] = -zoom;
|
|
}
|
|
},
|
|
|
|
// Convert javascript key codes to UNO key codes.
|
|
_toUNOKeyCode: function (keyCode) {
|
|
return this.keymap[keyCode] || keyCode;
|
|
},
|
|
|
|
// _onKeyDown - called only as a DOM event handler
|
|
// Calls _handleKeyEvent(), but only if the event doesn't have
|
|
// a charCode property (set to something different than 0) - that ignores
|
|
// any 'beforeinput', 'keypress' and 'input' events that would add
|
|
// printable characters. Those are handled by TextInput.js.
|
|
_onKeyDown: function (ev) {
|
|
if (this._map.uiManager.isUIBlocked() || (this._map.isReadOnlyMode() && !this.readOnlyAllowedShortcuts(ev))
|
|
|| ((this._map._docLayer._docType === 'presentation' || this._map._docLayer._docType === 'drawing') && this._map._docLayer._preview.partsFocused === true)
|
|
)
|
|
return;
|
|
|
|
var completeEvent = app.socket.createCompleteTraceEvent('L.Map.Keyboard._onKeyDown', { type: ev.type, charCode: ev.charCode });
|
|
window.app.console.log('keyboard handler:', ev.type, ev.key, ev.charCode, this._expectingInput, ev);
|
|
|
|
if (ev.charCode == 0) {
|
|
this._handleKeyEvent(ev);
|
|
}
|
|
if (this._map._docLayer)
|
|
if (ev.shiftKey && ev.type === 'keydown')
|
|
this._map._docLayer.shiftKeyPressed = true;
|
|
else if (ev.keyCode === this.keyCodes.SHIFT && ev.type === 'keyup')
|
|
this._map._docLayer.shiftKeyPressed = false;
|
|
if (completeEvent)
|
|
completeEvent.finish();
|
|
},
|
|
|
|
_globalKeyEvent: function(ev) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
if (this._map.jsdialog && this._map.jsdialog.hasDialogOpened()
|
|
&& this._map.jsdialog.handleKeyEvent(ev)) {
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
else if ((this._map._docLayer._docType === 'presentation' || this._map._docLayer._docType === 'drawing') && this._map._docLayer._preview.partsFocused === true) {
|
|
if (!this.modifier && (ev.keyCode === this.keyCodes.DOWN || ev.keyCode === this.keyCodes.UP || ev.keyCode === this.keyCodes.RIGHT || ev.keyCode === this.keyCodes.LEFT
|
|
|| ev.keyCode === this.keyCodes.DELETE || ev.keyCode === this.keyCodes.BACKSPACE) && ev.type === 'keydown') {
|
|
var partToSelect = (ev.keyCode === this.keyCodes.UP || ev.keyCode === this.keyCodes.LEFT) ? 'prev' : 'next';
|
|
var deletePart = (ev.keyCode === this.keyCodes.DELETE || ev.keyCode === this.keyCodes.BACKSPACE) ? true: false;
|
|
|
|
if (!deletePart) {
|
|
this._map.setPart(partToSelect);
|
|
if (app.file.fileBasedView)
|
|
this._map._docLayer._checkSelectedPart();
|
|
}
|
|
else if (this._map.isEditMode() && !app.file.fileBasedView) {
|
|
this._map.deletePage(this._map._docLayer._selectedPart);
|
|
}
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
else {
|
|
this._map._docLayer._preview.partsFocused = false;
|
|
}
|
|
}
|
|
},
|
|
|
|
// _handleKeyEvent - checks if the given keyboard event shall trigger
|
|
// a message to coolwsd, and calls the given keyEventFn(type, charcode, keycode)
|
|
// callback if so.
|
|
// Called from _onKeyDown
|
|
_handleKeyEvent: function (ev, keyEventFn) {
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
app.idleHandler.notifyActive();
|
|
if (this._map.slideShow && this._map.slideShow.fullscreen) {
|
|
return;
|
|
}
|
|
var docLayer = this._map._docLayer;
|
|
if (!keyEventFn && docLayer.postKeyboardEvent) {
|
|
// default is to post keyboard events on the document
|
|
keyEventFn = L.bind(docLayer.postKeyboardEvent, docLayer);
|
|
}
|
|
|
|
var docType = this._map._docLayer._docType;
|
|
this.modifier = 0;
|
|
var shift = ev.shiftKey ? UNOModifier.SHIFT : 0;
|
|
var ctrl = ev.ctrlKey ? UNOModifier.CTRL : 0;
|
|
var alt = ev.altKey ? UNOModifier.ALT : 0;
|
|
var cmd = ev.metaKey ? UNOModifier.CTRL : 0;
|
|
var location = ev.location;
|
|
this.modifier = shift | ctrl | alt | cmd;
|
|
|
|
// On Windows, pressing AltGr = Alt + Ctrl
|
|
// Presence of AltGr is detected if previous Ctrl + Alt 'location' === 2 (i.e right)
|
|
// because Ctrl + Alt + <some char> won't give any 'location' information.
|
|
if (ctrl && alt) {
|
|
if (ev.type === 'keydown' && location === 2) {
|
|
this._prevCtrlAltLocation = location;
|
|
return;
|
|
}
|
|
else if (location === 1) {
|
|
this._prevCtrlAltLocation = undefined;
|
|
}
|
|
|
|
if (this._prevCtrlAltLocation === 2 && location === 0) {
|
|
// and we got the final character
|
|
if (ev.type === 'keypress') {
|
|
ctrl = alt = this.modifier = 0;
|
|
}
|
|
else {
|
|
// Don't handle remnant 'keyup'
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctrl || cmd) {
|
|
if (this._handleCtrlCommand(ev)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
var charCode = ev.charCode;
|
|
var keyCode = ev.keyCode;
|
|
|
|
var DEFAULT =0;
|
|
//var MAC=1; use this when you encounter a MAC value
|
|
|
|
if ((this.modifier == UNOModifier.ALT || this.modifier == UNOModifier.SHIFT + UNOModifier.ALT) &&
|
|
keyCode >= this.keyCodes.NUM0[DEFAULT]) {
|
|
// Presumably a Mac or iOS client accessing a "special character". Just ignore the alt modifier.
|
|
// But don't ignore it for Alt + non-printing keys.
|
|
this.modifier -= alt;
|
|
alt = 0;
|
|
}
|
|
|
|
// handle help - F1
|
|
if (ev.type === 'keydown' && !ev.altKey && !this.modifier && keyCode === this.keyCodes.F1) {
|
|
this._map.showHelp('online-help');
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
|
|
if (ev.type === 'keydown' && ev.altKey && ev.keyCode === this.keyCodes.F1) {
|
|
var tabsContainer = document.getElementsByClassName('notebookbar-tabs-container')[0].children[0];
|
|
var elementToFocus;
|
|
if (tabsContainer) {
|
|
for (var i = 0; i < tabsContainer.children.length; i++) {
|
|
if (tabsContainer.children[i].classList.contains('selected')) {
|
|
elementToFocus = tabsContainer.children[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!elementToFocus)
|
|
elementToFocus = document.getElementById('Home-tab-label');
|
|
|
|
elementToFocus.focus();
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// disable F2 in Writer, formula bar is unsupported, and messes with further input
|
|
if (ev.type === 'keydown' && !this.modifier && keyCode === this.keyCodes.F2 && docType === 'text') {
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// don't trigger browser reload on F5, launch slideshow in Impress
|
|
if (ev.type === 'keydown' && keyCode === this.keyCodes.F5) {
|
|
ev.preventDefault();
|
|
if (docType === 'presentation')
|
|
{
|
|
this._map.fire('fullscreen');
|
|
}
|
|
return;
|
|
}
|
|
|
|
var unoKeyCode = this._toUNOKeyCode(keyCode);
|
|
|
|
if (this.modifier) {
|
|
unoKeyCode |= this.modifier;
|
|
if (ev.type !== 'keyup' && (this.modifier !== shift || (keyCode === this.keyCodes.SPACE && !this._map._isCursorVisible))) {
|
|
if (keyEventFn) {
|
|
keyEventFn('input', charCode, unoKeyCode);
|
|
ev.preventDefault();
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (this._map.stateChangeHandler._items['.uno:SlideMasterPage'] === 'true') {
|
|
ev.preventDefault();
|
|
return;
|
|
}
|
|
|
|
if (this._map.isEditMode()) {
|
|
docLayer._resetPreFetching();
|
|
|
|
if (this._ignoreKeyEvent(ev)) {
|
|
// key ignored
|
|
}
|
|
else if (ev.type === 'keydown') {
|
|
if (this.handleOnKeyDownKeys[keyCode] && charCode === 0) {
|
|
if (keyEventFn) {
|
|
keyEventFn('input', charCode, unoKeyCode);
|
|
ev.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
else if ((ev.type === 'keypress') && (!this.handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
|
|
if (keyCode === this.keyCodes.BACKSPACE || keyCode === this.keyCodes.DELETE || keyCode === this.keyCodes.enter)
|
|
{
|
|
// handled generically in TextInput.js
|
|
window.app.console.log('Ignore backspace/delete/enter keypress');
|
|
return;
|
|
}
|
|
if (charCode === keyCode && charCode !== this.keyCodes.enter) {
|
|
// Chrome sets keyCode = charCode for printable keys
|
|
// while LO requires it to be 0
|
|
keyCode = 0;
|
|
unoKeyCode = this._toUNOKeyCode(keyCode);
|
|
}
|
|
if (docLayer._debug) {
|
|
// key press times will be paired with the invalidation messages
|
|
docLayer._debugKeypressQueue.push(+new Date());
|
|
}
|
|
|
|
if (keyEventFn) {
|
|
keyEventFn('input', charCode, unoKeyCode);
|
|
}
|
|
}
|
|
else if (ev.type === 'keyup') {
|
|
if ((this.handleOnKeyDownKeys[keyCode] && charCode === 0) ||
|
|
(this.modifier) ||
|
|
unoKeyCode === UNOKey.RETURN) {
|
|
if (keyEventFn) {
|
|
keyEventFn('up', charCode, unoKeyCode);
|
|
}
|
|
} else {
|
|
// was handled as textinput
|
|
}
|
|
}
|
|
if (keyCode === this.keyCodes.TAB) {
|
|
// tab would change focus to other DOM elements
|
|
ev.preventDefault();
|
|
}
|
|
}
|
|
else if (!this.modifier && (keyCode === this.keyCodes.pageUp || keyCode === this.keyCodes.pageDown) && ev.type === 'keydown') {
|
|
if (docType === 'presentation' || docType === 'drawing') {
|
|
var partToSelect = keyCode === this.keyCodes.pageUp ? 'prev' : 'next';
|
|
this._map._docLayer._preview._scrollViewByDirection(partToSelect);
|
|
if (app.file.fileBasedView)
|
|
this._map._docLayer._checkSelectedPart();
|
|
}
|
|
return;
|
|
}
|
|
else if (!this.modifier && (keyCode === this.keyCodes.END || keyCode === this.keyCodes.HOME) && ev.type === 'keydown') {
|
|
if (docType === 'drawing' && app.file.fileBasedView === true) {
|
|
partToSelect = keyCode === this.keyCodes.HOME ? 0 : this._map._docLayer._parts -1;
|
|
this._map._docLayer._preview._scrollViewToPartPosition(partToSelect);
|
|
this._map._docLayer._checkSelectedPart();
|
|
}
|
|
return;
|
|
}
|
|
else if (ev.type === 'keydown') {
|
|
var key = ev.keyCode;
|
|
var map = this._map;
|
|
if (key in this._panKeys && !ev.shiftKey) {
|
|
if (map._panAnim && map._panAnim._inProgress) {
|
|
return;
|
|
}
|
|
map.fire('scrollby', {x: this._panKeys[key][0], y: this._panKeys[key][1]});
|
|
}
|
|
else if (key in this._panKeys && ev.shiftKey &&
|
|
!docLayer._textCSelections.empty()) {
|
|
// if there is a selection and the user wants to modify it
|
|
if (keyEventFn) {
|
|
keyEventFn('input', charCode, unoKeyCode);
|
|
}
|
|
}
|
|
else if (key in this._zoomKeys) {
|
|
map.setZoom(map.getZoom() + (ev.shiftKey ? 3 : 1) * this._zoomKeys[key], null, true /* animate? */);
|
|
}
|
|
}
|
|
|
|
// if any key is pressed, we stop the following other user feature.
|
|
if (this._map._docLayer._followUser) {
|
|
this._map.userList.followUser(this._map._docLayer._viewId);
|
|
}
|
|
|
|
|
|
L.DomEvent.stopPropagation(ev);
|
|
},
|
|
|
|
_isCtrlKey: function (e) {
|
|
if (window.ThisIsTheiOSApp || navigator.appVersion.indexOf('Mac') != -1 || navigator.userAgent.indexOf('Mac') != -1)
|
|
return e.metaKey;
|
|
else
|
|
return e.ctrlKey;
|
|
},
|
|
|
|
// Given a DOM keyboard event that happened while the Control key was depressed,
|
|
// triggers the appropriate action or coolwsd message.
|
|
_handleCtrlCommand: function (e) {
|
|
|
|
var DEFAULT =0;
|
|
var MAC=1;
|
|
if (this._map.uiManager.isUIBlocked())
|
|
return;
|
|
|
|
// Control
|
|
if (e.keyCode == this.keyCodes.CTRL)
|
|
return true;
|
|
|
|
if (e.type !== 'keydown' && e.key !== 'c' && e.key !== 'v' && e.key !== 'x' &&
|
|
/* Safari */ e.keyCode !== this.keyCodes.C[MAC] && e.keyCode !== this.keyCodes.V[MAC] && e.keyCode !== this.keyCodes.X[MAC]) {
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (e.keyCode !== this.keyCodes.C[DEFAULT] && e.keyCode !== this.keyCodes.V[DEFAULT] && e.keyCode !== this.keyCodes.X[DEFAULT] &&
|
|
/* Safari */ e.keyCode !== this.keyCodes.C[MAC] && e.keyCode !== this.keyCodes.V[MAC] && e.keyCode !== this.keyCodes.X[MAC] &&
|
|
e.key !== 'c' && e.key !== 'v' && e.key !== 'x') {
|
|
// not copy or paste
|
|
e.preventDefault();
|
|
}
|
|
|
|
if (this._isCtrlKey(e) && e.shiftKey && e.key === '?') {
|
|
this._map.showHelp('keyboard-shortcuts');
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
// Handles paste special. The "Your browser" thing seems to indicate that this code
|
|
// snippet is relevant in a browser only.
|
|
if (!window.ThisIsAMobileApp && e.ctrlKey && e.shiftKey && e.altKey && (e.key === 'v' || e.key === 'V')) {
|
|
this._map._clip._openPasteSpecialPopup();
|
|
return true;
|
|
}
|
|
|
|
// Handles unformatted paste
|
|
if (this._isCtrlKey(e) && e.shiftKey && (e.key === 'v' || e.key === 'V')) {
|
|
return true;
|
|
}
|
|
|
|
if (this._isCtrlKey(e) && (e.key === 'k' || e.key === 'K')) {
|
|
this._map.showHyperlinkDialog();
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (this._isCtrlKey(e) && (e.key === 'z' || e.key === 'Z')) {
|
|
app.socket.sendMessage('uno .uno:Undo');
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (this._isCtrlKey(e) && (e.key === 'y' || e.key === 'Y')) {
|
|
app.socket.sendMessage('uno .uno:Redo');
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (this._isCtrlKey(e) && !e.shiftKey && !e.altKey && (e.key === 'f' || e.key === 'F')) {
|
|
if (!this._map.uiManager.isStatusBarVisible()) {
|
|
this._map.uiManager.showStatusBar();
|
|
}
|
|
this._map.fire('focussearch');
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (e.altKey || e.shiftKey) {
|
|
|
|
// need to handle Ctrl + Alt + C separately for Firefox
|
|
if (e.key === 'c' && e.altKey) {
|
|
this._map.insertComment();
|
|
return true;
|
|
}
|
|
|
|
// Ctrl + Alt
|
|
if (!e.shiftKey) {
|
|
switch (e.keyCode) {
|
|
case this.keyCodes.NUM5[DEFAULT]: // 5
|
|
app.socket.sendMessage('uno .uno:Strikeout');
|
|
return true;
|
|
case this.keyCodes.F: // f but according to value it is F so I guess it is not MAC so safe to Give F
|
|
app.socket.sendMessage('uno .uno:InsertFootnote');
|
|
return true;
|
|
case this.keyCodes.C[DEFAULT]: // c
|
|
case this.keyCodes.M: // m
|
|
this._map.insertComment();
|
|
return true;
|
|
case this.keyCodes.D: // d
|
|
app.socket.sendMessage('uno .uno:InsertEndnote');
|
|
return true;
|
|
case this.keyCodes.pageUp:
|
|
case this.keyCodes.pageDown :
|
|
if (this._map.getDocType() === 'spreadsheet') {
|
|
var currentSelectedPart = this._map._docLayer._selectedPart;
|
|
var parts = this._map._docLayer._parts;
|
|
var partToSelect = 0;
|
|
this._map._docLayer._clearReferences();
|
|
|
|
if (e.keyCode === this.keyCodes.pageUp) {
|
|
partToSelect = currentSelectedPart != parts - 1 ? currentSelectedPart + 1 : 0;
|
|
}
|
|
else {
|
|
partToSelect = currentSelectedPart != 0 ? currentSelectedPart - 1 : parts - 1;
|
|
}
|
|
this._map.setPart(parseInt(partToSelect), /*external:*/ false, /*calledFromSetPartHandler:*/ true);
|
|
return;
|
|
}
|
|
|
|
}
|
|
} else if (e.altKey) {
|
|
switch (e.keyCode) {
|
|
case this.keyCodes.D: // Ctrl + Shift + Alt + d for tile debugging mode
|
|
this._map._docLayer.toggleTileDebugMode();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/* Without specifying the key type, the messages are sent twice (both keydown/up) */
|
|
if (e.type === 'keydown' && window.ThisIsAMobileApp) {
|
|
if (e.key === 'c' || e.key === 'C') {
|
|
app.socket.sendMessage('uno .uno:Copy');
|
|
return true;
|
|
}
|
|
else if (e.key === 'v' || e.key === 'V') {
|
|
app.socket.sendMessage('uno .uno:Paste');
|
|
return true;
|
|
}
|
|
else if (e.key === 'x' || e.key === 'X') {
|
|
app.socket.sendMessage('uno .uno:Cut');
|
|
return true;
|
|
}
|
|
if (window.ThisIsTheAndroidApp)
|
|
e.preventDefault();
|
|
}
|
|
|
|
switch (e.keyCode) {
|
|
case this.keyCodes.NUM3[DEFAULT]: // 3
|
|
if (this._map.getDocType() === 'spreadsheet') {
|
|
app.socket.sendMessage('uno .uno:SetOptimalColumnWidthDirect');
|
|
app.socket.sendMessage('commandvalues command=.uno:ViewRowColumnHeaders');
|
|
return true;
|
|
}
|
|
return false;
|
|
case this.keyCodes.NUM5[DEFAULT]: // 5
|
|
if (this._map.getDocType() === 'spreadsheet') {
|
|
app.socket.sendMessage('uno .uno:Strikeout');
|
|
return true;
|
|
}
|
|
return false;
|
|
case this.keyCodes.C[DEFAULT]: // 'C'
|
|
case this.keyCodes.X[DEFAULT]: // 'X'
|
|
case this.keyCodes.C[MAC]: // 'c' Since keydown+c as different value in mac so i had to update the mapping in keyCodes
|
|
case this.keyCodes.X[MAC]: // 'x' same reason as above
|
|
case this.keyCodes.LEFTWINDOWKEY[MAC]: // Left Cmd (Safari)
|
|
case this.keyCodes.RIGHTWINDOWKEY[MAC]: // Right Cmd (Safari)
|
|
// we prepare for a copy or cut event
|
|
this._map.focus();
|
|
// Not sure if the commented code is still used, so I didn't remove it.
|
|
// Anyhow, by when editable area is populated with the focused paragraph
|
|
// we can't select its content or on next editing the content is overwritten.
|
|
// this._map._textInput.select();
|
|
return true;
|
|
case this.keyCodes.P: // p
|
|
this._map.print();
|
|
return true;
|
|
case this.keyCodes.S: // s
|
|
// Save only when not read-only.
|
|
if (!this._map.isReadOnlyMode()) {
|
|
this._map.fire('postMessage', {msgId: 'UI_Save', args: { source: 'keyboard' }});
|
|
if (!this._map._disableDefaultAction['UI_Save']) {
|
|
this._map.save(false /* An explicit save should terminate cell edit */,
|
|
false /* An explicit save should save it again */);
|
|
}
|
|
}
|
|
return true;
|
|
case this.keyCodes.V[DEFAULT]: // v
|
|
case this.keyCodes.V[MAC]: // v (Safari) needs a separate mapping in keyCodes
|
|
return true;
|
|
case this.keyCodes.F1: // f1
|
|
app.socket.sendMessage('uno .uno:NoteVisible');
|
|
return true;
|
|
case this.keyCodes.COMMA: // ,
|
|
app.socket.sendMessage('uno .uno:SubScript');
|
|
return true;
|
|
case this.keyCodes.PERIOD: // .
|
|
app.socket.sendMessage('uno .uno:SuperScript');
|
|
return true;
|
|
}
|
|
if (e.type === 'keypress' && (e.ctrlKey || e.metaKey) &&
|
|
(e.key === 'c' || e.key === 'v' || e.key === 'x')) {
|
|
// need to handle this separately for Firefox
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
readOnlyAllowedShortcuts: function(e) {
|
|
// Open keyboard shortcuts help page
|
|
if (this._isCtrlKey(e) && e.shiftKey && e.key === '?')
|
|
return true;
|
|
// Open help with F1 if any special key is not pressed
|
|
else if (e.type === 'keydown' && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && e.keyCode === this.keyCodes.F1)
|
|
return true;
|
|
// comment insert
|
|
else if (e.key === 'c' && e.altKey && e.altKey && e.ctrlKey && this._map.isPermissionEditForComments())
|
|
return true;
|
|
// full-screen presentation
|
|
else if (e.type === 'keydown' && e.keyCode === this.keyCodes.F5 && this._map._docLayer._docType === 'presentation')
|
|
return true;
|
|
// moving around
|
|
else if (!this.modifier && (e.keyCode === this.keyCodes.pageUp || e.keyCode === this.keyCodes.pageDown) && e.type === 'keydown')
|
|
return true;
|
|
else if (!this.modifier && (e.keyCode === this.keyCodes.END || e.keyCode === this.keyCodes.HOME) && e.type === 'keydown')
|
|
return true;
|
|
else if (e.type === 'keydown' && e.keyCode in this._panKeys)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
});
|