Remove dependency on Mousetrap

This commit is contained in:
Frederic Guillot
2016-12-30 20:14:36 -05:00
parent e1344e3e44
commit ff79ec72c1
20 changed files with 348 additions and 202 deletions

View File

@@ -6,13 +6,14 @@ New features:
* Add slideshow for images * Add slideshow for images
* Add API calls to manage tags * Add API calls to manage tags
* Offer the possibility to override internal formatter objects from plugins * Offer the possibility to override internal formatter objects from plugins
* Open PDF attachments in browser tab (preview)
Improvements: Improvements:
* Open PDF attachments in browser tab (preview)
* Handle username with dots in user mentions * Handle username with dots in user mentions
* Replace Chosen jQuery plugin by custom UI component * Replace Chosen jQuery plugin by custom UI component
* Rewrite UI component that change user/group roles * Rewrite UI component that change user/group roles
* Remove dependency on Mousetrap Javascript library
* Disable PageSpeed module from .htaccess if present * Disable PageSpeed module from .htaccess if present
Bug fixes: Bug fixes:

File diff suppressed because one or more lines are too long

View File

@@ -2,14 +2,14 @@ KB.component('image-slideshow', function (containerElement, options) {
var currentImage; var currentImage;
function onKeyDown(e) { function onKeyDown(e) {
switch (e.keyCode) { switch (KB.utils.getKey(e)) {
case 27: case 'Escape':
destroySlide(); destroySlide();
break; break;
case 39: case 'ArrowRight':
renderNextSlide(); renderNextSlide();
break; break;
case 37: case 'ArrowLeft':
renderPreviousSlide(); renderPreviousSlide();
break; break;
} }

View File

@@ -1,4 +1,105 @@
// Open board selector: "b" KB.keyboardShortcuts = function () {
KB.onKey(98, function () { function goToLink (selector) {
KB.trigger('board.selector.open'); var element = KB.find(selector);
});
if (element !== null) {
window.location = element.attr('href');
}
}
function submitForm() {
var forms = $("form");
if (forms.length == 1) {
forms.submit();
} else if (forms.length > 1) {
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
$(document.activeElement).parents("form").submit();
} else if (_KB.get("Popover").isOpen()) {
$("#popover-container form").submit();
}
}
}
KB.onKey('?', function () {
_KB.get("Popover").open($("body").data("keyboard-shortcut-url"));
});
KB.onKey('Escape', function () {
if (! KB.exists('#suggest-menu')) {
_KB.get("Popover").close();
_KB.get("Dropdown").close();
}
});
KB.onKey('Meta+Enter', submitForm, true);
KB.onKey('Control+Enter', submitForm, true);
KB.onKey('b', function () {
KB.trigger('board.selector.open');
});
if (KB.exists('#board')) {
KB.onKey('c', function () {
_KB.get('BoardHorizontalScrolling').toggle();
});
KB.onKey('s', function () {
_KB.get('BoardCollapsedMode').toggle();
});
KB.onKey('n', function () {
_KB.get("Popover").open($("#board").data("task-creation-url"));
});
}
if (KB.exists('#task-view')) {
KB.onKey('e', function () {
_KB.get("Popover").open(KB.find('#task-view').data('editUrl'));
});
KB.onKey('c', function () {
_KB.get("Popover").open(KB.find('#task-view').data('commentUrl'));
});
KB.onKey('s', function () {
_KB.get("Popover").open(KB.find('#task-view').data('subtaskUrl'));
});
KB.onKey('l', function () {
_KB.get("Popover").open(KB.find('#task-view').data('internalLinkUrl'));
});
}
KB.onKey('f', function () {
KB.focus('#form-search');
});
KB.onKey('r', function () {
var reset = $(".filter-reset").data("filter");
var input = $("#form-search");
input.val(reset);
$("form.search").submit();
});
KB.onKey('v+o', function () {
goToLink('a.view-overview');
});
KB.onKey('v+b', function () {
goToLink('a.view-board');
});
KB.onKey('v+c', function () {
goToLink('a.view-calendar');
});
KB.onKey('v+l', function () {
goToLink('a.view-listing');
});
KB.onKey('v+g', function () {
goToLink('a.view-gantt');
});
};

View File

@@ -2,22 +2,22 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
var componentElement, inputElement, inputHiddenElement; var componentElement, inputElement, inputHiddenElement;
function onKeyDown(e) { function onKeyDown(e) {
switch (e.keyCode) { switch (KB.utils.getKey(e)) {
case 27: case 'Escape':
inputElement.value = ''; inputElement.value = '';
destroyDropdownMenu(); destroyDropdownMenu();
break; break;
case 38: case 'ArrowUp':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
moveUp(); moveUp();
break; break;
case 40: case 'ArrowDown':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
moveDown(); moveDown();
break; break;
case 13: case 'Enter':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
insertSelectedItem(); insertSelectedItem();

View File

@@ -1,21 +1,21 @@
KB.component('suggest-menu', function(containerElement, options) { KB.component('suggest-menu', function(containerElement, options) {
function onKeyDown(e) { function onKeyDown(e) {
switch (e.keyCode) { switch (KB.utils.getKey(e)) {
case 27: case 'Escape':
destroy(); destroy();
break; break;
case 38: case 'ArrowUp':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
moveUp(); moveUp();
break; break;
case 40: case 'ArrowDown':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
moveDown(); moveDown();
break; break;
case 13: case 'Enter':
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
insertSelectedItem(); insertSelectedItem();

View File

@@ -37,12 +37,16 @@ KB.onChange = function (selector, callback) {
this.listeners.changes[selector] = callback; this.listeners.changes[selector] = callback;
}; };
KB.onKey = function (key, callback) { KB.onKey = function (key, callback, ignoreInputField) {
this.listeners.keys[key] = callback; this.listeners.keys[key] = {
'callback': callback,
'ignoreInputField': ignoreInputField || false
};
}; };
KB.listen = function () { KB.listen = function () {
var self = this; var self = this;
var keysQueue = [];
function onClick(e) { function onClick(e) {
for (var selector in self.listeners.clicks) { for (var selector in self.listeners.clicks) {
@@ -61,28 +65,48 @@ KB.listen = function () {
} }
} }
function onKeypress(e) { function onKeyPressed(e) {
var key = (typeof e.which === 'number') ? e.which : e.keyCode; var key = KB.utils.getKey(e);
var element = e.target; var isInputField = KB.utils.isInputField(e);
if (element.tagName === 'INPUT' || if (! isInputField || ['Escape', 'Meta', 'Enter', 'Control'].indexOf(key) !== -1) {
element.tagName === 'SELECT' || keysQueue.push(key);
element.tagName === 'TEXTAREA' ||
element.isContentEditable) {
return;
} }
for (var keyMap in self.listeners.keys) { if (keysQueue.length > 0) {
if (self.listeners.keys.hasOwnProperty(keyMap) && key === parseInt(keyMap)) { var reset = true;
e.preventDefault();
self.listeners.keys[key](e); for (var combination in self.listeners.keys) {
if (self.listeners.keys.hasOwnProperty(combination)) {
var keyboardListener = self.listeners.keys[combination];
var sequence = combination.split('+');
if (KB.utils.arraysIdentical(keysQueue, sequence)) {
if (isInputField && !keyboardListener.ignoreInputField) {
keysQueue = [];
return;
}
e.preventDefault();
e.stopPropagation();
keysQueue = [];
keyboardListener.callback(e);
break;
} else if (KB.utils.arraysStartsWith(keysQueue, sequence)) {
reset = false;
}
}
}
if (reset) {
keysQueue = [];
} }
} }
} }
document.addEventListener('click', onClick, false); document.addEventListener('click', onClick, false);
document.addEventListener('change', onChange, false); document.addEventListener('change', onChange, false);
document.addEventListener('keypress', onKeypress, false); window.addEventListener('keydown', onKeyPressed, false);
}; };
KB.component = function (name, object) { KB.component = function (name, object) {

View File

@@ -2,4 +2,5 @@
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
KB.render(); KB.render();
KB.listen(); KB.listen();
KB.keyboardShortcuts();
}); });

View File

@@ -4,10 +4,12 @@ KB.dom = function (tag) {
var element = typeof tag === 'string' ? document.createElement(tag) : tag; var element = typeof tag === 'string' ? document.createElement(tag) : tag;
this.attr = function (attribute, value) { this.attr = function (attribute, value) {
if (value !== null) { if (value !== null && typeof value !== 'undefined') {
element.setAttribute(attribute, value); element.setAttribute(attribute, value);
return this;
} else {
return element.getAttribute(attribute);
} }
return this;
}; };
this.data = function (attribute, value) { this.data = function (attribute, value) {
@@ -175,3 +177,15 @@ KB.find = function (selector) {
return null; return null;
}; };
KB.exists = function (selector) {
return !!document.querySelector(selector);
};
KB.focus = function (selector) {
var element = document.querySelector(selector);
if (element) {
return element.focus();
}
};

View File

@@ -32,3 +32,58 @@ KB.utils.getSelectionPosition = function (element) {
selectionEnd: selectionEnd selectionEnd: selectionEnd
}; };
}; };
KB.utils.arraysIdentical = function (a, b) {
var i = a.length;
if (i !== b.length) {
return false;
}
while (i--) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
};
KB.utils.arraysStartsWith = function (array1, array2) {
var length = Math.min(array1.length, array2.length);
for (var i = 0; i < length; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
};
KB.utils.isInputField = function (event) {
var element = event.target;
return !!(element.tagName === 'INPUT' ||
element.tagName === 'SELECT' ||
element.tagName === 'TEXTAREA' ||
element.isContentEditable);
};
KB.utils.getKey = function (e) {
var mapping = {
'Esc': 'Escape',
'Up': 'ArrowUp',
'Down': 'ArrowDown',
'Left': 'ArrowLeft',
'Right': 'ArrowRight'
};
for (var key in mapping) {
if (mapping.hasOwnProperty(key) && key === e.key) {
return mapping[key];
}
}
return e.key;
};

View File

@@ -0,0 +1,113 @@
(function () {
var keyboardeventKeyPolyfill = {
polyfill: polyfill,
keys: {
3: 'Cancel',
6: 'Help',
8: 'Backspace',
9: 'Tab',
12: 'Clear',
13: 'Enter',
16: 'Shift',
17: 'Control',
18: 'Alt',
19: 'Pause',
20: 'CapsLock',
27: 'Escape',
28: 'Convert',
29: 'NonConvert',
30: 'Accept',
31: 'ModeChange',
32: ' ',
33: 'PageUp',
34: 'PageDown',
35: 'End',
36: 'Home',
37: 'ArrowLeft',
38: 'ArrowUp',
39: 'ArrowRight',
40: 'ArrowDown',
41: 'Select',
42: 'Print',
43: 'Execute',
44: 'PrintScreen',
45: 'Insert',
46: 'Delete',
48: ['0', ')'],
49: ['1', '!'],
50: ['2', '@'],
51: ['3', '#'],
52: ['4', '$'],
53: ['5', '%'],
54: ['6', '^'],
55: ['7', '&'],
56: ['8', '*'],
57: ['9', '('],
91: 'OS',
93: 'ContextMenu',
144: 'NumLock',
145: 'ScrollLock',
181: 'VolumeMute',
182: 'VolumeDown',
183: 'VolumeUp',
186: [';', ':'],
187: ['=', '+'],
188: [',', '<'],
189: ['-', '_'],
190: ['.', '>'],
191: ['/', '?'],
192: ['`', '~'],
219: ['[', '{'],
220: ['\\', '|'],
221: [']', '}'],
222: ["'", '"'],
224: 'Meta',
225: 'AltGraph',
246: 'Attn',
247: 'CrSel',
248: 'ExSel',
249: 'EraseEof',
250: 'Play',
251: 'ZoomOut'
}
};
// Function keys (F1-24).
var i;
for (i = 1; i < 25; i++) {
keyboardeventKeyPolyfill.keys[111 + i] = 'F' + i;
}
// Printable ASCII characters.
var letter = '';
for (i = 65; i < 91; i++) {
letter = String.fromCharCode(i);
keyboardeventKeyPolyfill.keys[i] = [letter.toLowerCase(), letter.toUpperCase()];
}
function polyfill() {
if (!('KeyboardEvent' in window) ||
'key' in KeyboardEvent.prototype) {
return false;
}
// Polyfill `key` on `KeyboardEvent`.
var proto = {
get: function (x) {
var key = keyboardeventKeyPolyfill.keys[this.which || this.keyCode];
if (Array.isArray(key)) {
key = key[+this.shiftKey];
}
return key;
}
};
Object.defineProperty(KeyboardEvent.prototype, 'key', proto);
return proto;
}
keyboardeventKeyPolyfill.polyfill();
})();

View File

@@ -23,52 +23,15 @@ Kanboard.App.prototype.execute = function() {
if (typeof controller.focus === "function") { if (typeof controller.focus === "function") {
controller.focus(); controller.focus();
} }
if (typeof controller.keyboardShortcuts === "function") {
controller.keyboardShortcuts();
}
} }
} }
this.focus(); this.focus();
this.keyboardShortcuts();
this.datePicker(); this.datePicker();
this.autoComplete(); this.autoComplete();
this.tagAutoComplete(); this.tagAutoComplete();
}; };
Kanboard.App.prototype.keyboardShortcuts = function() {
var self = this;
// Submit form
Mousetrap.bindGlobal("mod+enter", function() {
var forms = $("form");
if (forms.length == 1) {
forms.submit();
} else if (forms.length > 1) {
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
$(document.activeElement).parents("form").submit();
} else if (self.get("Popover").isOpen()) {
$("#popover-container form").submit();
}
}
});
// Close popover and dropdown
Mousetrap.bindGlobal("esc", function() {
if (! document.getElementById('suggest-menu')) {
self.get("Popover").close();
self.get("Dropdown").close();
}
});
// Show keyboard shortcut
Mousetrap.bind("?", function() {
self.get("Popover").open($("body").data("keyboard-shortcut-url"));
});
};
Kanboard.App.prototype.focus = function() { Kanboard.App.prototype.focus = function() {
// Auto-select input fields // Auto-select input fields
$(document).on('focus', '.auto-select', function() { $(document).on('focus', '.auto-select', function() {

View File

@@ -2,16 +2,6 @@ Kanboard.BoardCollapsedMode = function(app) {
this.app = app; this.app = app;
}; };
Kanboard.BoardCollapsedMode.prototype.keyboardShortcuts = function() {
var self = this;
if (self.app.hasId("board")) {
Mousetrap.bind("s", function() {
self.toggle();
});
}
};
Kanboard.BoardCollapsedMode.prototype.toggle = function() { Kanboard.BoardCollapsedMode.prototype.toggle = function() {
var self = this; var self = this;
this.app.showLoadingIcon(); this.app.showLoadingIcon();

View File

@@ -17,16 +17,6 @@ Kanboard.BoardHorizontalScrolling.prototype.listen = function() {
}); });
}; };
Kanboard.BoardHorizontalScrolling.prototype.keyboardShortcuts = function() {
var self = this;
if (self.app.hasId("board")) {
Mousetrap.bind("c", function () {
self.toggle();
});
}
};
Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered = function() { Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered = function() {
this.render(); this.render();
}; };

View File

@@ -17,13 +17,3 @@ Kanboard.BoardTask.prototype.listen = function() {
} }
}); });
}; };
Kanboard.BoardTask.prototype.keyboardShortcuts = function() {
var self = this;
if (self.app.hasId("board")) {
Mousetrap.bind("n", function () {
self.app.get("Popover").open($("#board").data("task-creation-url"));
});
}
};

View File

@@ -38,60 +38,3 @@ Kanboard.Search.prototype.listen = function() {
$("form.search").submit(); $("form.search").submit();
}); });
}; };
Kanboard.Search.prototype.goToView = function(label) {
var link = $(label);
if (link.length) {
window.location = link.attr('href');
}
};
Kanboard.Search.prototype.keyboardShortcuts = function() {
var self = this;
// Switch view mode for projects: go to the overview page
Mousetrap.bind("v o", function() {
self.goToView(".view-overview");
});
// Switch view mode for projects: go to the board
Mousetrap.bind("v b", function() {
self.goToView(".view-board");
});
// Switch view mode for projects: go to the calendar
Mousetrap.bind("v c", function() {
self.goToView(".view-calendar");
});
// Switch view mode for projects: go to the listing
Mousetrap.bind("v l", function() {
self.goToView(".view-listing");
});
// Switch view mode for projects: go to the gantt chart
Mousetrap.bind("v g", function() {
self.goToView(".view-gantt");
});
// Focus to the search field
Mousetrap.bind("f", function(e) {
e.preventDefault();
var input = document.getElementById("form-search");
if (input) {
input.focus();
}
});
// Reset to the search field
Mousetrap.bind("r", function(e) {
e.preventDefault();
var reset = $(".filter-reset").data("filter");
var input = $("#form-search");
input.val(reset);
$("form.search").submit();
});
};

View File

@@ -2,29 +2,6 @@ Kanboard.Task = function(app) {
this.app = app; this.app = app;
}; };
Kanboard.Task.prototype.keyboardShortcuts = function() {
var taskView = $("#task-view");
var self = this;
if (this.app.hasId("task-view")) {
Mousetrap.bind("e", function() {
self.app.get("Popover").open(taskView.data("edit-url"));
});
Mousetrap.bind("c", function() {
self.app.get("Popover").open(taskView.data("comment-url"));
});
Mousetrap.bind("s", function() {
self.app.get("Popover").open(taskView.data("subtask-url"));
});
Mousetrap.bind("l", function() {
self.app.get("Popover").open(taskView.data("internal-link-url"));
});
}
};
Kanboard.Task.prototype.onPopoverOpened = function() { Kanboard.Task.prototype.onPopoverOpened = function() {
var self = this; var self = this;
var reloadingProjectId = 0; var reloadingProjectId = 0;

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,6 @@
"jquery-ui": "^1.11.4", "jquery-ui": "^1.11.4",
"jqueryui-touch-punch": "*", "jqueryui-touch-punch": "*",
"jqueryui-timepicker-addon": "^1.6.3", "jqueryui-timepicker-addon": "^1.6.3",
"mousetrap": "^1.5.3",
"font-awesome": "fontawesome#^4.7.0", "font-awesome": "fontawesome#^4.7.0",
"d3": "~3.5.0", "d3": "~3.5.0",
"isMobile": "0.4.0", "isMobile": "0.4.0",

View File

@@ -48,8 +48,6 @@ var vendor = {
'bower_components/moment/min/moment.min.js', 'bower_components/moment/min/moment.min.js',
'bower_components/fullcalendar/dist/fullcalendar.min.js', 'bower_components/fullcalendar/dist/fullcalendar.min.js',
'bower_components/fullcalendar/dist/locale-all.js', 'bower_components/fullcalendar/dist/locale-all.js',
'bower_components/mousetrap/mousetrap.min.js',
'bower_components/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js',
'bower_components/d3/d3.min.js', 'bower_components/d3/d3.min.js',
'bower_components/c3/c3.min.js', 'bower_components/c3/c3.min.js',
'bower_components/isMobile/isMobile.min.js', 'bower_components/isMobile/isMobile.min.js',