wekan/client/lib/escapeActions.js
Maxime Quandalle 216887490e Fix card title edition
Closes #184
2015-06-11 12:35:08 +02:00

158 lines
4.2 KiB
JavaScript

// Pressing `Escape` should close the last opened “element” and only the last
// one. Components can register themselves using a label a condition, and an
// action. This is used by Popup or inlinedForm for instance. When we press
// escape we execute the action which have a valid condition and his the highest
// in the label hierarchy.
EscapeActions = {
_actions: [],
// Executed in order
hierarchy: [
'textcomplete',
'popup',
'inlinedForm',
'detailsPane',
'multiselection',
'sidebarView'
],
register: function(label, action, condition, options) {
condition = condition || function() { return true; };
options = options || {};
// XXX Rewrite this with ES6: .push({ priority, condition, action })
var priority = this.hierarchy.indexOf(label);
if (priority === -1) {
throw Error('You must define the label in the EscapeActions hierarchy');
}
this._actions.push({
priority: priority,
condition: condition,
action: action,
noClickEscapeOn: options.noClickEscapeOn
});
// XXX Rewrite this with ES6: => function
this._actions = _.sortBy(this._actions, function(a) { return a.priority; });
},
executeLowest: function() {
return this._execute({
multipleAction: false
});
},
executeAll: function() {
return this._execute({
multipleActions: true
});
},
executeUpTo: function(maxLabel) {
return this._execute({
maxLabel: maxLabel,
multipleActions: true
});
},
clickExecute: function(evt, maxLabel) {
return this._execute({
maxLabel: maxLabel,
multipleActions: false,
evt: evt
});
},
_stopClick: function(action, clickTarget) {
if (! _.isString(action.noClickEscapeOn))
return false;
else
return $(clickTarget).closest(action.noClickEscapeOn).length > 0;
},
_execute: function(options) {
var maxLabel = options.maxLabel;
var evt = options.evt || {};
var multipleActions = options.multipleActions;
var maxPriority, currentAction;
var executedAtLeastOne = false;
if (! maxLabel)
maxPriority = Infinity;
else
maxPriority = this.hierarchy.indexOf(maxLabel);
for (var i = 0; i < this._actions.length; i++) {
currentAction = this._actions[i];
if (currentAction.priority > maxPriority)
return executedAtLeastOne;
if (evt.type === 'click' && this._stopClick(currentAction, evt.target))
return executedAtLeastOne;
if (currentAction.condition()) {
currentAction.action(evt);
executedAtLeastOne = true;
if (! multipleActions)
return executedAtLeastOne;
}
}
return executedAtLeastOne;
}
};
// MouseTrap plugin bindGlobal plugin. Adds a bindGlobal method to Mousetrap
// that allows you to bind specific keyboard shortcuts that will still work
// inside a text input field.
//
// usage:
// Mousetrap.bindGlobal('ctrl+s', _saveChanges);
//
// source:
// https://github.com/ccampbell/mousetrap/tree/master/plugins/global-bind
var _globalCallbacks = {};
var _originalStopCallback = Mousetrap.stopCallback;
Mousetrap.stopCallback = function(e, element, combo, sequence) {
var self = this;
if (self.paused) {
return true;
}
if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
return false;
}
return _originalStopCallback.call(self, e, element, combo);
};
Mousetrap.bindGlobal = function(keys, callback, action) {
var self = this;
self.bind(keys, callback, action);
if (keys instanceof Array) {
for (var i = 0; i < keys.length; i++) {
_globalCallbacks[keys[i]] = true;
}
return;
}
_globalCallbacks[keys] = true;
};
// Pressing escape to execute one escape action. We use `bindGloabal` vecause
// the shortcut sould work on textarea and inputs as well.
Mousetrap.bindGlobal('esc', function() {
EscapeActions.executeLowest();
});
// On a left click on the document, we try to exectute one escape action (eg,
// close the popup). We don't execute any action if the user has clicked on a
// link or a button.
$(document).on('click', function(evt) {
if (evt.which === 1 &&
$(evt.target).closest('a,button,.is-editable').length === 0) {
EscapeActions.clickExecute(evt, 'detailsPane');
}
});