mirror of
https://github.com/wekan/wekan.git
synced 2026-01-23 17:56:09 +01:00
Merge pull request #6082 from harryadel/mousetrap-migration
Replace mousetrap
This commit is contained in:
commit
8586fa8ce0
7 changed files with 236 additions and 147 deletions
|
|
@ -55,9 +55,6 @@ http@2.0.0! # force new http package
|
|||
# UI components
|
||||
ostrio:i18n
|
||||
reactive-var@1.0.12
|
||||
mousetrap:mousetrap
|
||||
mquandalle:jquery-textcomplete
|
||||
mquandalle:mousetrap-bindglobal
|
||||
templates:tabs
|
||||
meteor-autosize
|
||||
shell-server@0.5.0
|
||||
|
|
|
|||
|
|
@ -80,13 +80,10 @@ mongo-decimal@0.1.3
|
|||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.8
|
||||
mongo-livedata@1.0.12
|
||||
mousetrap:mousetrap@1.4.6_1
|
||||
mquandalle:autofocus@1.0.0
|
||||
mquandalle:collection-mutations@0.1.0
|
||||
mquandalle:jade@0.4.9
|
||||
mquandalle:jade-compiler@0.4.5
|
||||
mquandalle:jquery-textcomplete@0.8.0_1
|
||||
mquandalle:mousetrap-bindglobal@0.0.1
|
||||
msavin:usercache@1.8.0
|
||||
npm-mongo@4.17.2
|
||||
oauth@2.2.1
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
// Pressing `Escape` should close the last opened “element” and only the last
|
||||
const hotkeys = require('hotkeys-js').default;
|
||||
|
||||
// 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
|
||||
|
|
@ -119,9 +121,9 @@ EscapeActions = {
|
|||
},
|
||||
};
|
||||
|
||||
// Pressing escape to execute one escape action. We use `bindGloabal` vecause
|
||||
// the shortcut sould work on textarea and inputs as well.
|
||||
Mousetrap.bindGlobal('esc', () => {
|
||||
// Pressing escape to execute one escape action. ESC is allowed globally
|
||||
// in the hotkeys filter (keyboard.js) so it works in textarea and inputs.
|
||||
hotkeys('escape', () => {
|
||||
EscapeActions.executeLowest();
|
||||
Sidebar.hide();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,42 @@
|
|||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
|
||||
const hotkeys = require('hotkeys-js').default;
|
||||
|
||||
// XXX There is no reason to define these shortcuts globally, they should be
|
||||
// attached to a template (most of them will go in the `board` template).
|
||||
|
||||
// Configure hotkeys filter (replaces Mousetrap.stopCallback)
|
||||
// CRITICAL: Return values are INVERTED from Mousetrap's stopCallback
|
||||
// hotkeys filter: true = ALLOW shortcut, false = STOP shortcut
|
||||
hotkeys.filter = (event) => {
|
||||
// Are shortcuts enabled for the user?
|
||||
if (ReactiveCache.getCurrentUser() && !ReactiveCache.getCurrentUser().isKeyboardShortcuts())
|
||||
return false;
|
||||
|
||||
// Always handle escape
|
||||
if (event.keyCode === 27)
|
||||
return true;
|
||||
|
||||
// Make sure there are no selected characters
|
||||
if (window.getSelection().type === "Range")
|
||||
return false;
|
||||
|
||||
// Decide what the current element is
|
||||
const currentElement = event.target || document.activeElement;
|
||||
|
||||
// If the current element is editable, we don't want to trigger an event
|
||||
if (currentElement.isContentEditable)
|
||||
return false;
|
||||
|
||||
// Make sure we are not in an input element
|
||||
if (currentElement instanceof HTMLInputElement || currentElement instanceof HTMLSelectElement || currentElement instanceof HTMLTextAreaElement)
|
||||
return false;
|
||||
|
||||
// We can trigger events!
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle non-Latin keyboards
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// Only handle event if coming from body
|
||||
if (e.target !== document.body) return;
|
||||
|
|
@ -11,39 +44,19 @@ window.addEventListener('keydown', (e) => {
|
|||
// Only handle event if it's in another language
|
||||
if (String.fromCharCode(e.which).toLowerCase() === e.key) return;
|
||||
|
||||
// Trigger the corresponding action
|
||||
Mousetrap.handleKey(String.fromCharCode(e.which).toLowerCase(), [], {type: "keypress"});
|
||||
// Trigger the corresponding action by dispatching a new event with the ASCII key
|
||||
const key = String.fromCharCode(e.which).toLowerCase();
|
||||
// Create a synthetic event for hotkeys to handle
|
||||
const syntheticEvent = new KeyboardEvent('keydown', {
|
||||
key: key,
|
||||
keyCode: e.which,
|
||||
which: e.which,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
document.dispatchEvent(syntheticEvent);
|
||||
});
|
||||
|
||||
// Overwrite the stopCallback to allow for more keyboard shortcut customizations
|
||||
Mousetrap.stopCallback = (event, element) => {
|
||||
// Are shortcuts enabled for the user?
|
||||
if (ReactiveCache.getCurrentUser() && !ReactiveCache.getCurrentUser().isKeyboardShortcuts())
|
||||
return true;
|
||||
|
||||
// Always handle escape
|
||||
if (event.keyCode === 27)
|
||||
return false;
|
||||
|
||||
// Make sure there are no selected characters
|
||||
if (window.getSelection().type === "Range")
|
||||
return true;
|
||||
|
||||
// Decide what the current element is
|
||||
const currentElement = event.target || document.activeElement;
|
||||
|
||||
// If the current element is editable, we don't want to trigger an event
|
||||
if (currentElement.isContentEditable)
|
||||
return true;
|
||||
|
||||
// Make sure we are not in an input element
|
||||
if (currentElement instanceof HTMLInputElement || currentElement instanceof HTMLSelectElement || currentElement instanceof HTMLTextAreaElement)
|
||||
return true;
|
||||
|
||||
// We can trigger events!
|
||||
return false;
|
||||
}
|
||||
|
||||
function getHoveredCardId() {
|
||||
const card = $('.js-minicard:hover').get(0);
|
||||
if (!card) return null;
|
||||
|
|
@ -54,11 +67,13 @@ function getSelectedCardId() {
|
|||
return Session.get('currentCard') || Session.get('selectedCard') || getHoveredCardId();
|
||||
}
|
||||
|
||||
Mousetrap.bind('?', () => {
|
||||
hotkeys('?', (event) => {
|
||||
event.preventDefault();
|
||||
FlowRouter.go('shortcuts');
|
||||
});
|
||||
|
||||
Mousetrap.bind('w', () => {
|
||||
hotkeys('w', (event) => {
|
||||
event.preventDefault();
|
||||
if (Sidebar.isOpen() && Sidebar.getView() === 'home') {
|
||||
Sidebar.toggle();
|
||||
} else {
|
||||
|
|
@ -66,7 +81,8 @@ Mousetrap.bind('w', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('q', () => {
|
||||
hotkeys('q', (event) => {
|
||||
event.preventDefault();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentBoardId && currentUserId) {
|
||||
|
|
@ -74,7 +90,8 @@ Mousetrap.bind('q', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('a', () => {
|
||||
hotkeys('a', (event) => {
|
||||
event.preventDefault();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentBoardId && currentUserId) {
|
||||
|
|
@ -82,13 +99,15 @@ Mousetrap.bind('a', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('x', () => {
|
||||
hotkeys('x', (event) => {
|
||||
event.preventDefault();
|
||||
if (Filter.isActive()) {
|
||||
Filter.reset();
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('f', () => {
|
||||
hotkeys('f', (event) => {
|
||||
event.preventDefault();
|
||||
if (Sidebar.isOpen() && Sidebar.getView() === 'filter') {
|
||||
Sidebar.toggle();
|
||||
} else {
|
||||
|
|
@ -96,7 +115,8 @@ Mousetrap.bind('f', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('/', () => {
|
||||
hotkeys('/', (event) => {
|
||||
event.preventDefault();
|
||||
if (Sidebar.isOpen() && Sidebar.getView() === 'search') {
|
||||
Sidebar.toggle();
|
||||
} else {
|
||||
|
|
@ -104,12 +124,13 @@ Mousetrap.bind('/', () => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind(['down', 'up'], (evt, key) => {
|
||||
hotkeys('down,up', (event, handler) => {
|
||||
event.preventDefault();
|
||||
if (!Utils.getCurrentCardId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextFunc = key === 'down' ? 'next' : 'prev';
|
||||
const nextFunc = handler.key === 'down' ? 'next' : 'prev';
|
||||
const nextCard = $('.js-minicard.is-selected')
|
||||
[nextFunc]('.js-minicard')
|
||||
.get(0);
|
||||
|
|
@ -119,49 +140,47 @@ Mousetrap.bind(['down', 'up'], (evt, key) => {
|
|||
}
|
||||
});
|
||||
|
||||
numbArray = _.range(1,10).map(x => 'shift+'+String(x))
|
||||
Mousetrap.bind(numbArray, (evt, key) => {
|
||||
num = parseInt(key.substr(6, key.length));
|
||||
// Shift + number keys to remove labels in multiselect
|
||||
const shiftNums = _.range(1, 10).map(x => `shift+${x}`).join(',');
|
||||
hotkeys(shiftNums, (event, handler) => {
|
||||
event.preventDefault();
|
||||
const num = parseInt(handler.key.split('+')[1]);
|
||||
const currentUserId = Meteor.userId();
|
||||
if (currentUserId === null) {
|
||||
return;
|
||||
}
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
board = ReactiveCache.getBoard(currentBoardId);
|
||||
labels = board.labels;
|
||||
if(MultiSelection.isActive())
|
||||
{
|
||||
const board = ReactiveCache.getBoard(currentBoardId);
|
||||
const labels = board.labels;
|
||||
if (MultiSelection.isActive()) {
|
||||
const cardIds = MultiSelection.getSelectedCardIds();
|
||||
for (const cardId of cardIds)
|
||||
{
|
||||
card = Cards.findOne(cardId);
|
||||
if(num <= board.labels.length)
|
||||
{
|
||||
card.removeLabel(labels[num-1]["_id"]);
|
||||
for (const cardId of cardIds) {
|
||||
const card = Cards.findOne(cardId);
|
||||
if (num <= board.labels.length) {
|
||||
card.removeLabel(labels[num - 1]["_id"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
numArray = _.range(1,10).map(x => String(x))
|
||||
Mousetrap.bind(numArray, (evt, key) => {
|
||||
num = parseInt(key);
|
||||
// Number keys to toggle labels
|
||||
const nums = _.range(1, 10).join(',');
|
||||
hotkeys(nums, (event, handler) => {
|
||||
event.preventDefault();
|
||||
const num = parseInt(handler.key);
|
||||
const currentUserId = Meteor.userId();
|
||||
const currentBoardId = Session.get('currentBoard');
|
||||
if (currentUserId === null) {
|
||||
return;
|
||||
}
|
||||
board = ReactiveCache.getBoard(currentBoardId);
|
||||
labels = board.labels;
|
||||
if(MultiSelection.isActive() && ReactiveCache.getCurrentUser().isBoardMember())
|
||||
{
|
||||
const board = ReactiveCache.getBoard(currentBoardId);
|
||||
const labels = board.labels;
|
||||
if (MultiSelection.isActive() && ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const cardIds = MultiSelection.getSelectedCardIds();
|
||||
for (const cardId of cardIds)
|
||||
{
|
||||
card = Cards.findOne(cardId);
|
||||
if(num <= board.labels.length)
|
||||
{
|
||||
card.addLabel(labels[num-1]["_id"]);
|
||||
for (const cardId of cardIds) {
|
||||
const card = Cards.findOne(cardId);
|
||||
if (num <= board.labels.length) {
|
||||
card.addLabel(labels[num - 1]["_id"]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
|
@ -173,14 +192,16 @@ Mousetrap.bind(numArray, (evt, key) => {
|
|||
}
|
||||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const card = Cards.findOne(cardId);
|
||||
if(num <= board.labels.length)
|
||||
{
|
||||
card.toggleLabel(labels[num-1]["_id"]);
|
||||
if (num <= board.labels.length) {
|
||||
card.toggleLabel(labels[num - 1]["_id"]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind(_.range(1, 10).map(x => `ctrl+alt+${x}`), (evt, key) => {
|
||||
// Ctrl+Alt + number keys to toggle assignees
|
||||
const ctrlAltNums = _.range(1, 10).map(x => `ctrl+alt+${x}`).join(',');
|
||||
hotkeys(ctrlAltNums, (event, handler) => {
|
||||
event.preventDefault();
|
||||
// Make sure the current user is defined
|
||||
if (!ReactiveCache.getCurrentUser())
|
||||
return;
|
||||
|
|
@ -189,7 +210,7 @@ Mousetrap.bind(_.range(1, 10).map(x => `ctrl+alt+${x}`), (evt, key) => {
|
|||
if (!ReactiveCache.getCurrentUser().isBoardMember())
|
||||
return;
|
||||
|
||||
const memberIndex = parseInt(key.split("+").pop()) - 1;
|
||||
const memberIndex = parseInt(handler.key.split("+").pop()) - 1;
|
||||
const currentBoard = Utils.getCurrentBoard();
|
||||
const validBoardMembers = currentBoard.memberUsers().filter(member => member.isBoardMember());
|
||||
|
||||
|
|
@ -211,7 +232,8 @@ Mousetrap.bind(_.range(1, 10).map(x => `ctrl+alt+${x}`), (evt, key) => {
|
|||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('m', evt => {
|
||||
hotkeys('m', (event) => {
|
||||
event.preventDefault();
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
|
|
@ -225,13 +247,11 @@ Mousetrap.bind('m', evt => {
|
|||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const card = Cards.findOne(cardId);
|
||||
card.toggleAssignee(currentUserId);
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
Mousetrap.bind('space', evt => {
|
||||
hotkeys('space', (event) => {
|
||||
event.preventDefault();
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
|
|
@ -245,13 +265,11 @@ Mousetrap.bind('space', evt => {
|
|||
if (ReactiveCache.getCurrentUser().isBoardMember()) {
|
||||
const card = Cards.findOne(cardId);
|
||||
card.toggleMember(currentUserId);
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
const archiveCard = evt => {
|
||||
const archiveCard = (event) => {
|
||||
event.preventDefault();
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
|
|
@ -265,21 +283,19 @@ const archiveCard = evt => {
|
|||
if (Utils.canModifyBoard()) {
|
||||
const card = Cards.findOne(cardId);
|
||||
card.archive();
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
// Archive card has multiple shortcuts
|
||||
Mousetrap.bind('c', archiveCard);
|
||||
Mousetrap.bind('-', archiveCard);
|
||||
hotkeys('c', archiveCard);
|
||||
hotkeys('-', archiveCard);
|
||||
|
||||
// Same as above, this time for Persian keyboard.
|
||||
// https://github.com/wekan/wekan/pull/5589#issuecomment-2516776519
|
||||
Mousetrap.bind('÷', archiveCard);
|
||||
hotkeys('\xf7', archiveCard);
|
||||
|
||||
Mousetrap.bind('n', evt => {
|
||||
hotkeys('n', (event) => {
|
||||
event.preventDefault();
|
||||
const cardId = getSelectedCardId();
|
||||
if (!cardId) {
|
||||
return;
|
||||
|
|
@ -296,10 +312,6 @@ Mousetrap.bind('n', evt => {
|
|||
|
||||
// Find the button and click it
|
||||
$(`#js-list-${card.listId} .list-body .minicards .open-minicard-composer`).click();
|
||||
|
||||
// We should prevent scrolling in card when spacebar is clicked
|
||||
// This should do it according to Mousetrap docs, but it doesn't
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -354,7 +366,7 @@ Template.keyboardShortcuts.helpers({
|
|||
action: 'shortcut-assign-self',
|
||||
},
|
||||
{
|
||||
keys: ['c', '÷', '-'],
|
||||
keys: ['c', '\xf7', '-'],
|
||||
action: 'archive-card',
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,54 +1,80 @@
|
|||
// We “inherit” the jquery-textcomplete plugin to integrate with our
|
||||
// EscapeActions system. You should always use `escapeableTextComplete` instead
|
||||
// of the vanilla `textcomplete`.
|
||||
// We use @textcomplete packages to integrate with our EscapeActions system.
|
||||
// You should always use `createEscapeableTextComplete` or the jQuery extension
|
||||
// `escapeableTextComplete` instead of the vanilla textcomplete.
|
||||
import { Textcomplete } from '@textcomplete/core';
|
||||
import { TextareaEditor } from '@textcomplete/textarea';
|
||||
import { ContenteditableEditor } from '@textcomplete/contenteditable';
|
||||
|
||||
let dropdownMenuIsOpened = false;
|
||||
|
||||
$.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
|
||||
// When the autocomplete menu is shown we want both a press of both `Tab`
|
||||
// or `Enter` to validation the auto-completion. We also need to stop the
|
||||
// event propagation to prevent EscapeActions side effect, for instance the
|
||||
// minicard submission (on `Enter`) or going on the next column (on `Tab`).
|
||||
options = {
|
||||
onKeydown(evt, commands) {
|
||||
if (evt.keyCode === 9 || evt.keyCode === 13) {
|
||||
evt.stopPropagation();
|
||||
return commands.KEY_ENTER;
|
||||
}
|
||||
return null;
|
||||
/**
|
||||
* Create an escapeable textcomplete instance for a textarea or contenteditable element
|
||||
* @param {HTMLTextAreaElement|HTMLElement} element - The target element
|
||||
* @param {Array} strategies - Array of strategy objects
|
||||
* @param {Object} options - Additional options
|
||||
* @returns {Textcomplete} The textcomplete instance
|
||||
*/
|
||||
export function createEscapeableTextComplete(element, strategies, options = {}) {
|
||||
// Determine the appropriate editor based on element type
|
||||
const isContentEditable = element.isContentEditable || element.contentEditable === 'true';
|
||||
const Editor = isContentEditable ? ContenteditableEditor : TextareaEditor;
|
||||
|
||||
const editor = new Editor(element);
|
||||
|
||||
// Merge default options
|
||||
const mergedOptions = {
|
||||
dropdown: {
|
||||
className: 'textcomplete-dropdown',
|
||||
maxCount: 10,
|
||||
placement: 'bottom',
|
||||
...options.dropdown,
|
||||
},
|
||||
...options,
|
||||
};
|
||||
|
||||
// Proxy to the vanilla jQuery component
|
||||
this.textcomplete(strategies, options, ...otherArgs);
|
||||
const textcomplete = new Textcomplete(editor, strategies, mergedOptions);
|
||||
|
||||
// Since commit d474017 jquery-textComplete automatically closes a potential
|
||||
// opened dropdown menu when the user press Escape. This behavior conflicts
|
||||
// with our EscapeActions system, but it's too complicated and hacky to
|
||||
// monkey-pach textComplete to disable it -- I tried. Instead we listen to
|
||||
// 'open' and 'hide' events, and create a ghost escapeAction when the dropdown
|
||||
// is opened (and rely on textComplete to execute the actual action).
|
||||
this.on({
|
||||
'textComplete:show'() {
|
||||
dropdownMenuIsOpened = true;
|
||||
},
|
||||
'textComplete:select'() {
|
||||
EscapeActions.preventNextClick();
|
||||
},
|
||||
'textComplete:hide'() {
|
||||
Tracker.afterFlush(() => {
|
||||
// XXX Hack. We unfortunately need to set a setTimeout here to make the
|
||||
// `noClickEscapeOn` work bellow, otherwise clicking on a autocomplete
|
||||
// item will close both the autocomplete menu (as expected) but also the
|
||||
// next item in the stack (for example the minicard editor) which we
|
||||
// don't want.
|
||||
setTimeout(() => {
|
||||
dropdownMenuIsOpened = false;
|
||||
}, 100);
|
||||
});
|
||||
},
|
||||
// When the autocomplete menu is shown we want both a press of both `Tab`
|
||||
// or `Enter` to validate the auto-completion. We also need to stop the
|
||||
// event propagation to prevent EscapeActions side effect, for instance the
|
||||
// minicard submission (on `Enter`) or going on the next column (on `Tab`).
|
||||
element.addEventListener('keydown', (evt) => {
|
||||
if (dropdownMenuIsOpened && (evt.keyCode === 9 || evt.keyCode === 13)) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
// Track dropdown state for EscapeActions integration
|
||||
// Since @textcomplete automatically closes when Escape is pressed, we
|
||||
// integrate with our EscapeActions system by tracking open/close state.
|
||||
textcomplete.on('show', () => {
|
||||
dropdownMenuIsOpened = true;
|
||||
});
|
||||
|
||||
textcomplete.on('selected', () => {
|
||||
EscapeActions.preventNextClick();
|
||||
});
|
||||
|
||||
textcomplete.on('hidden', () => {
|
||||
Tracker.afterFlush(() => {
|
||||
// XXX Hack. We unfortunately need to set a setTimeout here to make the
|
||||
// `noClickEscapeOn` work below, otherwise clicking on a autocomplete
|
||||
// item will close both the autocomplete menu (as expected) but also the
|
||||
// next item in the stack (for example the minicard editor) which we
|
||||
// don't want.
|
||||
setTimeout(() => {
|
||||
dropdownMenuIsOpened = false;
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
return textcomplete;
|
||||
}
|
||||
|
||||
// jQuery extension for backward compatibility
|
||||
$.fn.escapeableTextComplete = function(strategies, options = {}) {
|
||||
return this.each(function() {
|
||||
createEscapeableTextComplete(this, strategies, options);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
EscapeActions.register(
|
||||
|
|
|
|||
51
package-lock.json
generated
51
package-lock.json
generated
|
|
@ -118,6 +118,37 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@textcomplete/contenteditable": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@textcomplete/contenteditable/-/contenteditable-0.1.13.tgz",
|
||||
"integrity": "sha512-O2BNqtvP0I1lL8WIwJ/ilCVi6rEJu2Jtj7Nnx8+XSN66aoBV5pdl0c1IXFfNvGU5kJh+6EOxkDEmm2NhYCIXlw==",
|
||||
"requires": {
|
||||
"@textcomplete/utils": "^0.1.13"
|
||||
}
|
||||
},
|
||||
"@textcomplete/core": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@textcomplete/core/-/core-0.1.13.tgz",
|
||||
"integrity": "sha512-C4S+ihQU5HsKQ/TbsmS0e7hfPZtLZbEXj5NDUgRnhu/1Nezpu892bjNZGeErZm+R8iyDIT6wDu6EgIhng4M8eQ==",
|
||||
"requires": {
|
||||
"eventemitter3": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"@textcomplete/textarea": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@textcomplete/textarea/-/textarea-0.1.13.tgz",
|
||||
"integrity": "sha512-GNathnXpV361YuZrBVXvVqFYZ5NQZsjGC7Bt2sCUA/RTWlIgxHxC0ruDChYyRDx4siQZiZZOO5pWz+z1x8pZFQ==",
|
||||
"requires": {
|
||||
"@textcomplete/utils": "^0.1.13",
|
||||
"textarea-caret": "^3.1.0",
|
||||
"undate": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"@textcomplete/utils": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@textcomplete/utils/-/utils-0.1.13.tgz",
|
||||
"integrity": "sha512-5UW9Ee0WEX1s9K8MFffo5sfUjYm3YVhtqRhAor/ih7p0tnnpaMB7AwMRDKwhSIQL6O+g1fmEkxCeO8WqjPzjUA=="
|
||||
},
|
||||
"@tokenizer/token": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||
|
|
@ -724,6 +755,11 @@
|
|||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="
|
||||
},
|
||||
"events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
|
|
@ -907,6 +943,11 @@
|
|||
"function-bind": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"hotkeys-js": {
|
||||
"version": "3.13.15",
|
||||
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.13.15.tgz",
|
||||
"integrity": "sha512-gHh8a/cPTCpanraePpjRxyIlxDFrIhYqjuh01UHWEwDpglJKCnvLW8kqSx5gQtOuSsJogNZXLhOdbSExpgUiqg=="
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
|
||||
|
|
@ -2812,6 +2853,11 @@
|
|||
"readable-stream": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"textarea-caret": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
|
||||
"integrity": "sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q=="
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
|
|
@ -2884,6 +2930,11 @@
|
|||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||
},
|
||||
"undate": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undate/-/undate-0.3.0.tgz",
|
||||
"integrity": "sha512-ssH8QTNBY6B+2fRr3stSQ+9m2NT8qTaun3ExTx5ibzYQvP7yX4+BnX0McNxFCvh6S5ia/DYu6bsCKQx/U4nb/Q=="
|
||||
},
|
||||
"unzipper": {
|
||||
"version": "0.10.14",
|
||||
"resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz",
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@
|
|||
"@mapbox/node-pre-gyp": "^2.0.3",
|
||||
"@meteorjs/reify": "^0.25.4",
|
||||
"@rwap/jquery-ui-touch-punch": "^1.0.11",
|
||||
"@textcomplete/contenteditable": "^0.1.13",
|
||||
"@textcomplete/core": "^0.1.13",
|
||||
"@textcomplete/textarea": "^0.1.13",
|
||||
"@wekanteam/dragscroll": "^0.0.9",
|
||||
"@wekanteam/exceljs": "^4.6.0",
|
||||
"@wekanteam/html-to-markdown": "^1.0.2",
|
||||
"@wekanteam/meteor-globals": "^1.1.6",
|
||||
"@wekanteam/meteor-reactive-cache": "^1.0.7",
|
||||
"meteor-node-stubs": "npm:@wekanteam/meteor-node-stubs@^1.2.7",
|
||||
"ajv": "^6.12.6",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bson": "^4.7.2",
|
||||
|
|
@ -37,6 +39,7 @@
|
|||
"fibers": "^5.0.3",
|
||||
"file-type": "^16.5.4",
|
||||
"filesize": "^8.0.7",
|
||||
"hotkeys-js": "^3.13.15",
|
||||
"i18next": "^21.10.0",
|
||||
"i18next-sprintf-postprocessor": "^0.2.2",
|
||||
"jquery": "^3.7.1",
|
||||
|
|
@ -47,6 +50,7 @@
|
|||
"markdown-it-emoji": "^2.0.0",
|
||||
"markdown-it-mathjax3": "^4.3.2",
|
||||
"meteor-accounts-t9n": "^2.6.0",
|
||||
"meteor-node-stubs": "npm:@wekanteam/meteor-node-stubs@^1.2.7",
|
||||
"os": "^0.1.2",
|
||||
"papaparse": "^5.5.3",
|
||||
"pretty-ms": "^7.0.1",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue