2020-02-06 07:07:10 +01:00
|
|
|
const emoji_codes = require("../generated/emoji/emoji_codes.json");
|
|
|
|
|
2013-06-11 04:06:16 +02:00
|
|
|
function do_narrow_action(action) {
|
|
|
|
action(current_msg_list.selected_id(), {trigger: 'hotkey'});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-12 11:52:00 +02:00
|
|
|
// For message actions and user profile menu.
|
2019-11-02 00:06:25 +01:00
|
|
|
const menu_dropdown_hotkeys = [
|
2013-07-11 23:06:58 +02:00
|
|
|
'down_arrow',
|
|
|
|
'up_arrow',
|
|
|
|
'vim_up',
|
|
|
|
'vim_down',
|
2017-01-12 00:17:43 +01:00
|
|
|
'enter',
|
2013-07-11 23:06:58 +02:00
|
|
|
];
|
|
|
|
|
2016-06-14 00:08:02 +02:00
|
|
|
// Note that multiple keys can map to the same event_name, which
|
|
|
|
// we'll do in cases where they have the exact same semantics.
|
|
|
|
// DON'T FORGET: update keyboard_shortcuts.html
|
|
|
|
|
2018-02-14 18:34:32 +01:00
|
|
|
// The `message_view_only` property is a convenient and performant way
|
|
|
|
// to express a common case of which hotkeys do something in which
|
2018-06-02 21:08:45 +02:00
|
|
|
// views. It is set for hotkeys (like `Ctrl + s`) that only have an effect
|
2018-02-14 18:34:32 +01:00
|
|
|
// in the main message view with a selected message.
|
|
|
|
// `message_view_only` hotkeys, as a group, are not processed if any
|
|
|
|
// overlays are open (e.g. settings, streams, etc.).
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keydown_shift_mappings = {
|
2016-06-14 00:08:02 +02:00
|
|
|
// these can be triggered by shift + key only
|
|
|
|
9: {name: 'shift_tab', message_view_only: false}, // tab
|
2017-03-09 00:56:18 +01:00
|
|
|
32: {name: 'shift_spacebar', message_view_only: true}, // space bar
|
2016-06-14 00:08:02 +02:00
|
|
|
};
|
2017-03-13 21:41:28 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keydown_unshift_mappings = {
|
2016-06-14 00:08:02 +02:00
|
|
|
// these can be triggered by key only (without shift)
|
|
|
|
9: {name: 'tab', message_view_only: false}, // tab
|
2017-03-13 21:41:28 +01:00
|
|
|
27: {name: 'escape', message_view_only: false}, // escape
|
2017-03-09 00:56:18 +01:00
|
|
|
32: {name: 'spacebar', message_view_only: true}, // space bar
|
2016-06-14 00:08:02 +02:00
|
|
|
33: {name: 'page_up', message_view_only: true}, // page up
|
|
|
|
34: {name: 'page_down', message_view_only: true}, // page down
|
|
|
|
35: {name: 'end', message_view_only: true}, // end
|
|
|
|
36: {name: 'home', message_view_only: true}, // home
|
2017-03-19 01:51:20 +01:00
|
|
|
37: {name: 'left_arrow', message_view_only: false}, // left arrow
|
|
|
|
39: {name: 'right_arrow', message_view_only: false}, // right arrow
|
2017-04-04 21:04:59 +02:00
|
|
|
38: {name: 'up_arrow', message_view_only: false}, // up arrow
|
|
|
|
40: {name: 'down_arrow', message_view_only: false}, // down arrow
|
2016-06-14 00:08:02 +02:00
|
|
|
};
|
2017-03-13 21:41:28 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keydown_ctrl_mappings = {
|
2018-05-24 17:29:53 +02:00
|
|
|
219: {name: 'escape', message_view_only: false}, // '['
|
|
|
|
};
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keydown_cmd_or_ctrl_mappings = {
|
2019-03-26 10:18:29 +01:00
|
|
|
67: {name: 'copy_with_c', message_view_only: false}, // 'C'
|
2018-05-07 04:30:31 +02:00
|
|
|
75: {name: 'search_with_k', message_view_only: false}, // 'K'
|
2018-06-01 14:44:23 +02:00
|
|
|
83: {name: 'star_message', message_view_only: true}, // 's'
|
2018-11-30 00:20:10 +01:00
|
|
|
190: {name: 'narrow_to_compose_target', message_view_only: true}, // '.'
|
2017-03-19 19:04:14 +01:00
|
|
|
};
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keydown_either_mappings = {
|
2016-06-14 00:08:02 +02:00
|
|
|
// these can be triggered by key or shift + key
|
|
|
|
// Note that codes for letters are still case sensitive!
|
2017-03-13 21:41:28 +01:00
|
|
|
//
|
|
|
|
// We may want to revisit both of these. For backspace, we don't
|
|
|
|
// have any specific mapping behavior; we are just trying to disable
|
|
|
|
// the normal browser features for certain OSes when we are in the
|
|
|
|
// compose box, and the little bit of backspace-related code here is
|
|
|
|
// dubious, but may apply to shift-backspace.
|
|
|
|
// For enter, there is some possibly that shift-enter is intended to
|
|
|
|
// have special behavior for folks that are used to shift-enter behavior
|
|
|
|
// in other apps, but that's also slightly dubious.
|
2016-06-14 00:08:02 +02:00
|
|
|
8: {name: 'backspace', message_view_only: true}, // backspace
|
|
|
|
13: {name: 'enter', message_view_only: false}, // enter
|
2018-03-07 21:27:55 +01:00
|
|
|
46: {name: 'delete', message_view_only: false}, // delete
|
2017-03-13 21:41:28 +01:00
|
|
|
};
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const keypress_mappings = {
|
2018-06-02 15:48:15 +02:00
|
|
|
42: {name: 'star_deprecated', message_view_only: true}, // '*'
|
2017-03-20 17:46:26 +01:00
|
|
|
43: {name: 'thumbs_up_emoji', message_view_only: true}, // '+'
|
2017-06-16 06:46:46 +02:00
|
|
|
45: {name: 'toggle_message_collapse', message_view_only: true}, // '-'
|
2016-06-14 00:08:02 +02:00
|
|
|
47: {name: 'search', message_view_only: false}, // '/'
|
2017-10-05 08:02:46 +02:00
|
|
|
58: {name: 'toggle_reactions_popover', message_view_only: true}, // ':'
|
2018-01-31 04:23:02 +01:00
|
|
|
62: {name: 'compose_quote_reply', message_view_only: true}, // '>'
|
2016-06-14 00:08:02 +02:00
|
|
|
63: {name: 'show_shortcuts', message_view_only: false}, // '?'
|
2016-12-29 04:50:17 +01:00
|
|
|
64: {name: 'compose_reply_with_mention', message_view_only: true}, // '@'
|
2016-06-14 00:08:02 +02:00
|
|
|
65: {name: 'stream_cycle_backward', message_view_only: true}, // 'A'
|
2018-02-08 15:17:26 +01:00
|
|
|
67: {name: 'C_deprecated', message_view_only: true}, // 'C'
|
2016-06-14 00:08:02 +02:00
|
|
|
68: {name: 'stream_cycle_forward', message_view_only: true}, // 'D'
|
2017-03-23 08:11:00 +01:00
|
|
|
71: {name: 'G_end', message_view_only: true}, // 'G'
|
2017-03-09 01:02:28 +01:00
|
|
|
74: {name: 'vim_page_down', message_view_only: true}, // 'J'
|
|
|
|
75: {name: 'vim_page_up', message_view_only: true}, // 'K'
|
2017-03-23 04:25:01 +01:00
|
|
|
77: {name: 'toggle_mute', message_view_only: true}, // 'M'
|
2017-03-19 19:41:02 +01:00
|
|
|
80: {name: 'narrow_private', message_view_only: true}, // 'P'
|
2016-06-14 00:08:02 +02:00
|
|
|
82: {name: 'respond_to_author', message_view_only: true}, // 'R'
|
2018-11-13 16:11:42 +01:00
|
|
|
83: {name: 'narrow_by_topic', message_view_only: true}, //'S'
|
2017-03-23 06:41:31 +01:00
|
|
|
86: {name: 'view_selected_stream', message_view_only: false}, //'V'
|
2016-06-14 00:08:02 +02:00
|
|
|
99: {name: 'compose', message_view_only: true}, // 'c'
|
2017-10-20 19:21:08 +02:00
|
|
|
100: {name: 'open_drafts', message_view_only: true}, // 'd'
|
2019-04-06 15:57:48 +02:00
|
|
|
101: {name: 'edit_message', message_view_only: true}, // 'e'
|
2017-03-18 20:30:20 +01:00
|
|
|
103: {name: 'gear_menu', message_view_only: true}, // 'g'
|
2016-06-14 00:08:02 +02:00
|
|
|
105: {name: 'message_actions', message_view_only: true}, // 'i'
|
|
|
|
106: {name: 'vim_down', message_view_only: true}, // 'j'
|
|
|
|
107: {name: 'vim_up', message_view_only: true}, // 'k'
|
2017-03-23 13:23:49 +01:00
|
|
|
110: {name: 'n_key', message_view_only: false}, // 'n'
|
2018-02-16 15:56:25 +01:00
|
|
|
112: {name: 'p_key', message_view_only: false}, // 'p'
|
2020-02-03 15:33:15 +01:00
|
|
|
113: {name: 'query_streams', message_view_only: true}, // 'q'
|
2016-06-14 00:08:02 +02:00
|
|
|
114: {name: 'reply_message', message_view_only: true}, // 'r'
|
|
|
|
115: {name: 'narrow_by_recipient', message_view_only: true}, // 's'
|
2017-05-23 20:01:24 +02:00
|
|
|
117: {name: 'show_sender_info', message_view_only: true}, // 'u'
|
2017-03-18 21:01:16 +01:00
|
|
|
118: {name: 'show_lightbox', message_view_only: true}, // 'v'
|
2020-02-03 15:33:15 +01:00
|
|
|
119: {name: 'query_users', message_view_only: true}, // 'w'
|
2018-02-08 15:17:26 +01:00
|
|
|
120: {name: 'compose_private_message', message_view_only: true}, // 'x'
|
2016-06-14 00:08:02 +02:00
|
|
|
};
|
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
exports.get_keydown_hotkey = function (e) {
|
2018-03-29 22:29:10 +02:00
|
|
|
if (e.altKey) {
|
2017-03-13 21:41:28 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let hotkey;
|
2018-08-14 00:05:36 +02:00
|
|
|
|
2018-06-21 01:30:19 +02:00
|
|
|
if (e.ctrlKey && !e.shiftKey) {
|
2018-05-24 17:29:53 +02:00
|
|
|
hotkey = keydown_ctrl_mappings[e.which];
|
|
|
|
if (hotkey) {
|
|
|
|
return hotkey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const isCmdOrCtrl = common.has_mac_keyboard() ? e.metaKey : e.ctrlKey;
|
2018-06-20 18:43:01 +02:00
|
|
|
if (isCmdOrCtrl && !e.shiftKey) {
|
2018-03-29 22:29:10 +02:00
|
|
|
hotkey = keydown_cmd_or_ctrl_mappings[e.which];
|
2017-03-19 19:04:14 +01:00
|
|
|
if (hotkey) {
|
|
|
|
return hotkey;
|
|
|
|
}
|
|
|
|
return;
|
2018-03-29 22:29:10 +02:00
|
|
|
} else if (e.metaKey || e.ctrlKey) {
|
|
|
|
return;
|
2017-03-19 19:04:14 +01:00
|
|
|
}
|
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
if (e.shiftKey) {
|
|
|
|
hotkey = keydown_shift_mappings[e.which];
|
|
|
|
if (hotkey) {
|
|
|
|
return hotkey;
|
|
|
|
}
|
2013-06-11 04:06:16 +02:00
|
|
|
}
|
2012-09-21 23:51:31 +02:00
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
if (!e.shiftKey) {
|
|
|
|
hotkey = keydown_unshift_mappings[e.which];
|
|
|
|
if (hotkey) {
|
|
|
|
return hotkey;
|
|
|
|
}
|
2012-12-12 20:34:47 +01:00
|
|
|
}
|
2013-06-11 04:06:16 +02:00
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
return keydown_either_mappings[e.which];
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_keypress_hotkey = function (e) {
|
|
|
|
if (e.metaKey || e.ctrlKey || e.altKey) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return keypress_mappings[e.which];
|
|
|
|
};
|
2012-10-23 20:55:24 +02:00
|
|
|
|
2020-01-28 22:15:13 +01:00
|
|
|
exports.processing_text = function () {
|
|
|
|
const $focused_elt = $(":focus");
|
|
|
|
return $focused_elt.is("input") ||
|
|
|
|
$focused_elt.is("select") ||
|
|
|
|
$focused_elt.is("textarea") ||
|
|
|
|
$focused_elt.hasClass("editable-section") ||
|
2020-01-29 13:43:57 +01:00
|
|
|
$focused_elt.parents(".pill-container").length >= 1 ||
|
2020-01-28 22:15:13 +01:00
|
|
|
$focused_elt.attr("id") === "compose-send-button";
|
|
|
|
};
|
2017-03-12 02:43:00 +01:00
|
|
|
|
2020-01-29 20:22:07 +01:00
|
|
|
exports.in_content_editable_widget = function (e) {
|
2017-03-10 22:57:25 +01:00
|
|
|
return $(e.target).is(".editable-section");
|
|
|
|
};
|
|
|
|
|
2017-03-12 16:22:55 +01:00
|
|
|
// Returns true if we handled it, false if the browser should.
|
|
|
|
exports.process_escape_key = function (e) {
|
2020-01-29 20:22:07 +01:00
|
|
|
if (exports.in_content_editable_widget(e)) {
|
2017-03-12 16:22:55 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-21 16:18:08 +01:00
|
|
|
if (feedback_widget.is_open()) {
|
|
|
|
feedback_widget.dismiss();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-29 19:05:30 +02:00
|
|
|
if (overlays.is_modal_open()) {
|
|
|
|
overlays.close_active_modal();
|
2017-08-28 18:45:15 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.is_active()) {
|
|
|
|
overlays.close_active();
|
2017-03-12 16:22:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-22 04:28:52 +02:00
|
|
|
if (gear_menu.is_open()) {
|
|
|
|
gear_menu.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-12 16:22:55 +01:00
|
|
|
if (exports.processing_text()) {
|
|
|
|
if (activity.searching()) {
|
|
|
|
activity.escape_search();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream_list.searching()) {
|
|
|
|
stream_list.escape_search();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-01 00:13:47 +02:00
|
|
|
// Emoji picker goes before compose so compose emoji picker is closed properly.
|
|
|
|
if (emoji_picker.reactions_popped()) {
|
|
|
|
emoji_picker.hide_emoji_popover();
|
2017-03-12 16:22:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-01 00:13:47 +02:00
|
|
|
if (compose_state.composing()) {
|
2017-12-03 16:55:33 +01:00
|
|
|
// Check for errors in compose box; close errors if they exist
|
|
|
|
if ($("#compose-send-status").css('display') !== 'none') {
|
|
|
|
$("#compose-send-status").hide();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-01 00:13:47 +02:00
|
|
|
// If the user hit the escape key, cancel the current compose
|
|
|
|
compose_actions.cancel();
|
2017-03-21 07:51:45 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-23 19:47:52 +02:00
|
|
|
if (page_params.search_pills_enabled && $('#searchbox').has(':focus')) {
|
|
|
|
$('#searchbox .pill').blur();
|
|
|
|
$('#searchbox #search_query').blur();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-12 16:22:55 +01:00
|
|
|
// We pressed Esc and something was focused, and the composebox
|
|
|
|
// wasn't open. In that case, we should blur the input.
|
|
|
|
$("input:focus,textarea:focus").blur();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popovers.any_active()) {
|
|
|
|
popovers.hide_all();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-18 18:48:43 +01:00
|
|
|
if (compose_state.composing()) {
|
2017-03-18 17:55:11 +01:00
|
|
|
compose_actions.cancel();
|
2017-03-12 16:22:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-11 13:04:57 +02:00
|
|
|
if (topic_zoom.is_zoomed_in()) {
|
|
|
|
topic_zoom.zoom_out();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Simplify narrow/search interactions.
Before this change, if you hit ESC, then hotkey
code would call search.clear_search, which would
call narrow.deactivate(), which would then use
`$('#search_query')` to clear a value, but then
let search.clear_search blur the input and
disable the exit button. It was all confusing.
Things are a bit more organized now.
Now the code works like this:
hotkey.process_escape_key
Just call narrow.deactivate.
$('#search_exit').on('click', ...):
Just call narrow.deactivate.
narrow.deactivate:
Just call search.clear_search_form
search.clear_search_form:
Just do simple jquery stuff. Don't
change the entire user's narrow, not
even indirectly!
There's still a two-way interaction between
the narrow.js module and the search.js module,
but in each direction it's a one-liner.
The guiding principle here is that we only
want one top-level API, which is narrow.deactivate,
and that does the whole "kitchen sink" of
clearing searches, closing popovers, switching
in views, etc. And then all the functions it
calls out to tend to have much smaller jobs to
do.
This commit can mostly be considered a refactoring, but the
order of operations changes slightly. Basically, as
soon as you hit ESC or click on the search "X", we
clear the search widget. Most users won't notice
any difference, because we don't have to hit the
server to populate the home view. And it's arguably
an improvement to give more immediate feedback.
2018-09-10 19:36:58 +02:00
|
|
|
narrow.deactivate();
|
2017-03-12 16:22:55 +01:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2017-03-13 17:01:09 +01:00
|
|
|
// Returns true if we handled it, false if the browser should.
|
|
|
|
exports.process_enter_key = function (e) {
|
2017-08-28 01:30:49 +02:00
|
|
|
if ($(".dropdown.open").length && $(e.target).attr("role") === "menuitem") {
|
2017-03-18 20:30:57 +01:00
|
|
|
// on #gear-menu li a[tabindex] elements, force a click and prevent default.
|
|
|
|
// this is because these links do not have an href and so don't force a
|
|
|
|
// default action.
|
|
|
|
e.target.click();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-02 04:46:56 +02:00
|
|
|
if (hotspots.is_open()) {
|
|
|
|
$(e.target).find('.hotspot.overlay.show .hotspot-confirm').click();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-27 07:27:25 +02:00
|
|
|
if (emoji_picker.reactions_popped()) {
|
2017-05-10 19:46:20 +02:00
|
|
|
if (emoji_picker.is_composition(e.target)) {
|
|
|
|
e.target.click();
|
|
|
|
} else {
|
2017-05-30 00:50:45 +02:00
|
|
|
emoji_picker.toggle_selected_emoji();
|
2017-05-10 19:46:20 +02:00
|
|
|
}
|
2017-04-19 07:49:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-29 20:22:07 +01:00
|
|
|
if (exports.in_content_editable_widget(e)) {
|
2017-03-13 17:01:09 +01:00
|
|
|
$(e.target).parent().find(".checkmark").click();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popovers.actions_popped()) {
|
|
|
|
popovers.actions_menu_handle_keyboard('enter');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.settings_open()) {
|
2017-03-13 17:01:09 +01:00
|
|
|
// On the settings page just let the browser handle
|
|
|
|
// the enter key for things like submitting forms.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.streams_open()) {
|
2017-05-16 00:08:09 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-13 17:01:09 +01:00
|
|
|
if (exports.processing_text()) {
|
|
|
|
if (stream_list.searching()) {
|
|
|
|
// This is sort of funny behavior, but I think
|
|
|
|
// the intention is that we want it super easy
|
|
|
|
// to close stream search.
|
|
|
|
stream_list.clear_and_hide_search();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-18 21:26:24 +01:00
|
|
|
// This handles when pressing enter while looking at drafts.
|
|
|
|
// It restores draft that is focused.
|
2017-10-03 00:41:43 +02:00
|
|
|
if (overlays.drafts_open()) {
|
2017-04-14 08:57:41 +02:00
|
|
|
drafts.drafts_handle_events(e, "enter");
|
2017-03-18 21:26:24 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-13 17:01:09 +01:00
|
|
|
// If we're on a button or a link and have pressed enter, let the
|
|
|
|
// browser handle the keypress
|
|
|
|
//
|
|
|
|
// This is subtle and here's why: Suppose you have the focus on a
|
|
|
|
// stream name in your left sidebar. j and k will still move your
|
|
|
|
// cursor up and down, but Enter won't reply -- it'll just trigger
|
|
|
|
// the link on the sidebar! So you keep pressing enter over and
|
|
|
|
// over again. Until you click somewhere or press r.
|
|
|
|
if ($('a:focus,button:focus').length > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($("#preview_message_area").is(":visible")) {
|
|
|
|
compose.enter_with_preview_open();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got this far, then we're presumably in the message
|
2017-04-21 09:33:05 +02:00
|
|
|
// view, so in that case "enter" is the hotkey to respond to a message.
|
|
|
|
// Note that "r" has same effect, but that is handled in process_hotkey().
|
2017-04-14 19:27:12 +02:00
|
|
|
compose_actions.respond_to_message({trigger: 'hotkey enter'});
|
2017-03-13 17:01:09 +01:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2017-03-14 23:01:08 +01:00
|
|
|
exports.process_tab_key = function () {
|
|
|
|
// Returns true if we handled it, false if the browser should.
|
|
|
|
// TODO: See if browsers like Safari can now handle tabbing correctly
|
|
|
|
// without our intervention.
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let message_edit_form;
|
2017-03-14 23:01:08 +01:00
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const focused_message_edit_content = $(".message_edit_content").filter(":focus");
|
2017-03-14 23:01:08 +01:00
|
|
|
if (focused_message_edit_content.length > 0) {
|
|
|
|
message_edit_form = focused_message_edit_content.closest(".message_edit_form");
|
2018-12-09 15:48:40 +01:00
|
|
|
// Open message edit forms either have a save button or a close button, but not both.
|
|
|
|
message_edit_form.find(".message_edit_save,.message_edit_close").focus();
|
2017-03-14 23:01:08 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const focused_message_edit_save = $(".message_edit_save").filter(":focus");
|
2017-03-14 23:01:08 +01:00
|
|
|
if (focused_message_edit_save.length > 0) {
|
|
|
|
message_edit_form = focused_message_edit_save.closest(".message_edit_form");
|
|
|
|
message_edit_form.find(".message_edit_cancel").focus();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-19 00:28:56 +02:00
|
|
|
if (emoji_picker.reactions_popped()) {
|
|
|
|
return emoji_picker.navigate('tab');
|
|
|
|
}
|
|
|
|
|
2017-03-14 23:01:08 +01:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.process_shift_tab_key = function () {
|
|
|
|
// Returns true if we handled it, false if the browser should.
|
|
|
|
// TODO: See if browsers like Safari can now handle tabbing correctly
|
|
|
|
// without our intervention.
|
|
|
|
|
|
|
|
if ($('#compose-send-button').is(':focus')) {
|
|
|
|
// Shift-Tab: go back to content textarea and restore
|
|
|
|
// cursor position.
|
|
|
|
ui.restore_compose_cursor();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shift-tabbing from the edit message cancel button takes you to save.
|
|
|
|
if ($(".message_edit_cancel").filter(":focus").length > 0) {
|
|
|
|
$(".message_edit_save").focus();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shift-tabbing from the edit message save button takes you to the content.
|
2019-11-02 00:06:25 +01:00
|
|
|
const focused_message_edit_save = $(".message_edit_save").filter(":focus");
|
2017-03-14 23:01:08 +01:00
|
|
|
if (focused_message_edit_save.length > 0) {
|
|
|
|
focused_message_edit_save.closest(".message_edit_form")
|
2018-05-06 21:43:17 +02:00
|
|
|
.find(".message_edit_content").focus();
|
2017-03-14 23:01:08 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-07-19 00:28:56 +02:00
|
|
|
// Shift-tabbing from emoji catalog/search results takes you back to search textbox.
|
|
|
|
if (emoji_picker.reactions_popped()) {
|
|
|
|
return emoji_picker.navigate('shift_tab');
|
|
|
|
}
|
|
|
|
|
2017-03-14 23:01:08 +01:00
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2012-11-29 19:55:08 +01:00
|
|
|
// Process a keydown or keypress event.
|
|
|
|
//
|
|
|
|
// Returns true if we handled it, false if the browser should.
|
2017-03-13 21:41:28 +01:00
|
|
|
exports.process_hotkey = function (e, hotkey) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const event_name = hotkey.name;
|
2017-03-03 17:28:16 +01:00
|
|
|
|
2017-03-14 23:01:08 +01:00
|
|
|
// We handle the most complex keys in their own functions.
|
|
|
|
switch (event_name) {
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'escape':
|
|
|
|
return exports.process_escape_key(e);
|
|
|
|
case 'enter':
|
|
|
|
return exports.process_enter_key(e);
|
|
|
|
case 'tab':
|
|
|
|
return exports.process_tab_key();
|
|
|
|
case 'shift_tab':
|
|
|
|
return exports.process_shift_tab_key();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: break out specific handlers for up_arrow,
|
|
|
|
// down_arrow, and backspace
|
2017-04-04 20:48:08 +02:00
|
|
|
switch (event_name) {
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'up_arrow':
|
|
|
|
case 'down_arrow':
|
|
|
|
case 'backspace':
|
|
|
|
case 'delete':
|
|
|
|
if (overlays.drafts_open()) {
|
|
|
|
drafts.drafts_handle_events(e, event_name);
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-18 21:26:24 +01:00
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (hotkey.message_view_only && overlays.is_active()) {
|
2017-05-24 20:15:51 +02:00
|
|
|
if (exports.processing_text()) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-11-13 16:11:42 +01:00
|
|
|
if (event_name === 'narrow_by_topic' && overlays.streams_open()) {
|
2017-05-24 20:15:51 +02:00
|
|
|
subs.keyboard_sub();
|
|
|
|
return true;
|
|
|
|
}
|
2017-10-03 00:41:13 +02:00
|
|
|
if (event_name === 'show_lightbox' && overlays.lightbox_open()) {
|
|
|
|
overlays.close_overlay('lightbox');
|
2017-09-14 01:32:35 +02:00
|
|
|
return true;
|
|
|
|
}
|
2017-10-20 19:21:08 +02:00
|
|
|
if (event_name === 'open_drafts' && overlays.drafts_open()) {
|
|
|
|
overlays.close_overlay('drafts');
|
|
|
|
return true;
|
|
|
|
}
|
2017-05-24 20:15:51 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-03 21:13:55 +01:00
|
|
|
|
|
|
|
if (hotkey.message_view_only && gear_menu.is_open()) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-24 20:15:51 +02:00
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.settings_open()) {
|
2017-04-04 19:59:04 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-27 07:27:25 +02:00
|
|
|
if (emoji_picker.reactions_popped()) {
|
2017-07-19 00:28:56 +02:00
|
|
|
return emoji_picker.navigate(event_name);
|
2017-04-19 07:37:03 +02:00
|
|
|
}
|
|
|
|
|
2017-08-02 04:46:56 +02:00
|
|
|
if (hotspots.is_open()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.info_overlay_open()) {
|
2017-09-07 03:01:17 +02:00
|
|
|
if (event_name === 'show_shortcuts') {
|
|
|
|
overlays.close_active();
|
|
|
|
return true;
|
|
|
|
}
|
2017-04-12 20:58:31 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
if ((event_name === 'up_arrow' || event_name === 'down_arrow') && overlays.streams_open()) {
|
2017-04-05 10:49:19 +02:00
|
|
|
return subs.switch_rows(event_name);
|
2017-04-04 21:04:59 +02:00
|
|
|
}
|
2012-11-30 00:43:41 +01:00
|
|
|
|
2020-01-29 20:22:07 +01:00
|
|
|
if (exports.in_content_editable_widget(e)) {
|
2017-03-13 17:01:09 +01:00
|
|
|
// We handle the enter key in process_enter_key().
|
|
|
|
// We ignore all other keys.
|
2016-12-17 03:44:15 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-03 17:41:41 +01:00
|
|
|
if (event_name === "up_arrow") {
|
2017-04-04 18:14:27 +02:00
|
|
|
if (list_util.inside_list(e)) {
|
|
|
|
list_util.go_up(e);
|
2016-09-28 01:19:20 +02:00
|
|
|
return true;
|
2017-03-03 17:41:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event_name === "down_arrow") {
|
2017-04-04 18:14:27 +02:00
|
|
|
if (list_util.inside_list(e)) {
|
|
|
|
list_util.go_down(e);
|
2016-09-28 01:19:20 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
js: Convert a.indexOf(…) !== -1 to a.includes(…).
Babel polyfills this for us for Internet Explorer.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import K from "ast-types/gen/kinds";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
recast.visit(ast, {
visitBinaryExpression(path) {
const { operator, left, right } = path.node;
if (
n.CallExpression.check(left) &&
n.MemberExpression.check(left.callee) &&
!left.callee.computed &&
n.Identifier.check(left.callee.property) &&
left.callee.property.name === "indexOf" &&
left.arguments.length === 1 &&
checkExpression(left.arguments[0]) &&
((["===", "!==", "==", "!=", ">", "<="].includes(operator) &&
n.UnaryExpression.check(right) &&
right.operator == "-" &&
n.Literal.check(right.argument) &&
right.argument.value === 1) ||
([">=", "<"].includes(operator) &&
n.Literal.check(right) &&
right.value === 0))
) {
const test = b.callExpression(
b.memberExpression(left.callee.object, b.identifier("includes")),
[left.arguments[0]]
);
path.replace(
["!==", "!=", ">", ">="].includes(operator)
? test
: b.unaryExpression("!", test)
);
changed = true;
}
this.traverse(path);
},
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-08 04:55:06 +01:00
|
|
|
if (menu_dropdown_hotkeys.includes(event_name)) {
|
2018-06-12 12:29:34 +02:00
|
|
|
if (popovers.actions_popped()) {
|
|
|
|
popovers.actions_menu_handle_keyboard(event_name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popovers.message_info_popped()) {
|
|
|
|
popovers.user_info_popover_handle_keyboard(event_name);
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-11 23:06:58 +02:00
|
|
|
}
|
|
|
|
|
2017-03-11 22:05:11 +01:00
|
|
|
// The next two sections date back to 00445c84 and are Mac/Chrome-specific,
|
|
|
|
// and they should possibly be eliminated in favor of keeping standard
|
|
|
|
// browser behavior.
|
|
|
|
if (event_name === 'backspace') {
|
|
|
|
if ($('#compose-send-button').is(':focus')) {
|
2013-02-18 08:16:57 +01:00
|
|
|
// Ignore backspace; don't navigate back a page.
|
|
|
|
return true;
|
2017-03-11 22:05:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-30 00:20:10 +01:00
|
|
|
if (event_name === 'narrow_to_compose_target') {
|
|
|
|
narrow.to_compose_target();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-12 15:02:14 +01:00
|
|
|
// Process hotkeys specially when in an input, select, textarea, or send button
|
2017-03-12 02:43:00 +01:00
|
|
|
if (exports.processing_text()) {
|
2017-03-13 17:01:09 +01:00
|
|
|
// Note that there is special handling for enter/escape too, but
|
|
|
|
// we handle this in other functions.
|
2014-01-14 17:57:34 +01:00
|
|
|
|
2017-09-18 21:54:06 +02:00
|
|
|
if (event_name === 'left_arrow' && compose_state.focus_in_empty_compose()) {
|
2016-11-23 05:06:34 +01:00
|
|
|
message_edit.edit_last_sent_message();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-18 21:54:06 +02:00
|
|
|
if ((event_name === 'up_arrow' || event_name === 'down_arrow') && compose_state.focus_in_empty_compose()) {
|
2017-03-18 17:55:11 +01:00
|
|
|
compose_actions.cancel();
|
2016-11-23 05:06:34 +01:00
|
|
|
// don't return, as we still want it to be picked up by the code below
|
2017-03-08 23:05:32 +01:00
|
|
|
} else if (event_name === "page_up") {
|
2018-12-30 00:56:17 +01:00
|
|
|
$(":focus").caret(0).animate({ scrollTop: 0 }, "fast");
|
2017-03-08 23:05:32 +01:00
|
|
|
return true;
|
|
|
|
} else if (event_name === "page_down") {
|
2018-12-30 00:56:17 +01:00
|
|
|
// so that it always goes to the end of the text box.
|
2019-11-02 00:06:25 +01:00
|
|
|
const height = $(":focus")[0].scrollHeight;
|
2018-12-30 00:56:17 +01:00
|
|
|
$(":focus").caret(Infinity).animate({ scrollTop: height }, "fast");
|
2017-03-08 23:05:32 +01:00
|
|
|
return true;
|
2018-05-07 04:30:31 +02:00
|
|
|
} else if (event_name === "search_with_k") {
|
|
|
|
// Do nothing; this allows one to use ctrl+k inside compose.
|
2018-08-12 02:03:45 +02:00
|
|
|
} else if (event_name === "star_message") {
|
|
|
|
// Do nothing; this allows one to use ctrl+s inside compose.
|
2013-06-07 18:37:18 +02:00
|
|
|
} else {
|
|
|
|
// Let the browser handle the key normally.
|
|
|
|
return false;
|
|
|
|
}
|
2012-10-09 19:50:39 +02:00
|
|
|
}
|
|
|
|
|
2016-11-23 05:06:34 +01:00
|
|
|
if (event_name === 'left_arrow') {
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.lightbox_open()) {
|
2017-03-19 01:51:20 +01:00
|
|
|
lightbox.prev();
|
|
|
|
return true;
|
2017-05-27 15:40:54 +02:00
|
|
|
} else if (overlays.streams_open()) {
|
2017-03-23 06:02:01 +01:00
|
|
|
subs.toggle_view(event_name);
|
|
|
|
return true;
|
2017-03-19 01:51:20 +01:00
|
|
|
}
|
|
|
|
|
2016-11-23 05:06:34 +01:00
|
|
|
message_edit.edit_last_sent_message();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-19 01:51:20 +01:00
|
|
|
if (event_name === 'right_arrow') {
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.lightbox_open()) {
|
2017-03-19 01:51:20 +01:00
|
|
|
lightbox.next();
|
|
|
|
return true;
|
2017-05-27 15:40:54 +02:00
|
|
|
} else if (overlays.streams_open()) {
|
2017-03-23 06:02:01 +01:00
|
|
|
subs.toggle_view(event_name);
|
|
|
|
return true;
|
2017-03-19 01:51:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-21 19:21:44 +02:00
|
|
|
// Prevent navigation in the background when the overlays are active.
|
|
|
|
if (overlays.is_active()) {
|
|
|
|
if (event_name === 'view_selected_stream' && overlays.streams_open()) {
|
|
|
|
subs.view_stream();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (event_name === 'n_key' && overlays.streams_open() && page_params.can_create_streams) {
|
2018-12-01 21:18:20 +01:00
|
|
|
subs.open_create_stream();
|
2017-10-21 19:21:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-08 00:03:28 +02:00
|
|
|
// Shortcuts that don't require a message
|
2013-06-19 17:10:42 +02:00
|
|
|
switch (event_name) {
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'compose': // 'c': compose
|
|
|
|
compose_actions.start('stream', {trigger: "compose_hotkey"});
|
|
|
|
return true;
|
|
|
|
case 'compose_private_message':
|
|
|
|
compose_actions.start('private', {trigger: "compose_hotkey"});
|
|
|
|
return true;
|
|
|
|
case 'narrow_private':
|
|
|
|
return do_narrow_action(function (target, opts) {
|
|
|
|
narrow.by('is', 'private', opts);
|
|
|
|
});
|
|
|
|
case 'query_streams':
|
|
|
|
stream_list.initiate_search();
|
|
|
|
return true;
|
|
|
|
case 'query_users':
|
|
|
|
activity.initiate_search();
|
|
|
|
return true;
|
|
|
|
case 'search':
|
|
|
|
case 'search_with_k':
|
|
|
|
search.initiate_search();
|
|
|
|
return true;
|
|
|
|
case 'gear_menu':
|
|
|
|
gear_menu.open();
|
|
|
|
return true;
|
|
|
|
case 'show_shortcuts': // Show keyboard shortcuts page
|
|
|
|
info_overlay.maybe_show_keyboard_shortcuts();
|
|
|
|
return true;
|
|
|
|
case 'stream_cycle_backward':
|
|
|
|
narrow.stream_cycle_backward();
|
|
|
|
return true;
|
|
|
|
case 'stream_cycle_forward':
|
|
|
|
narrow.stream_cycle_forward();
|
|
|
|
return true;
|
|
|
|
case 'n_key':
|
|
|
|
narrow.narrow_to_next_topic();
|
|
|
|
return true;
|
|
|
|
case 'p_key':
|
|
|
|
narrow.narrow_to_next_pm_string();
|
|
|
|
return true;
|
|
|
|
case 'open_drafts':
|
|
|
|
drafts.launch();
|
|
|
|
return true;
|
|
|
|
case 'reply_message': // 'r': respond to message
|
|
|
|
// Note that you can "enter" to respond to messages as well,
|
|
|
|
// but that is handled in process_enter_key().
|
|
|
|
compose_actions.respond_to_message({trigger: 'hotkey'});
|
|
|
|
return true;
|
|
|
|
case 'C_deprecated':
|
|
|
|
ui.maybe_show_deprecation_notice('C');
|
|
|
|
return true;
|
2018-06-02 15:48:15 +02:00
|
|
|
case 'star_deprecated':
|
|
|
|
ui.maybe_show_deprecation_notice('*');
|
|
|
|
return true;
|
2019-03-26 10:18:29 +01:00
|
|
|
case 'copy_with_c':
|
|
|
|
copy_and_paste.copy_handler();
|
|
|
|
return true;
|
2012-09-21 22:35:32 +02:00
|
|
|
}
|
|
|
|
|
2013-07-24 22:51:13 +02:00
|
|
|
if (current_msg_list.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-08 00:03:28 +02:00
|
|
|
// Navigation shortcuts
|
2013-07-24 22:51:13 +02:00
|
|
|
switch (event_name) {
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'down_arrow':
|
|
|
|
case 'vim_down':
|
|
|
|
navigate.down(true); // with_centering
|
|
|
|
return true;
|
|
|
|
case 'up_arrow':
|
|
|
|
case 'vim_up':
|
|
|
|
navigate.up();
|
|
|
|
return true;
|
|
|
|
case 'home':
|
|
|
|
navigate.to_home();
|
|
|
|
return true;
|
|
|
|
case 'end':
|
|
|
|
case 'G_end':
|
|
|
|
navigate.to_end();
|
|
|
|
return true;
|
|
|
|
case 'page_up':
|
|
|
|
case 'vim_page_up':
|
|
|
|
case 'shift_spacebar':
|
|
|
|
navigate.page_up();
|
|
|
|
return true;
|
|
|
|
case 'page_down':
|
|
|
|
case 'vim_page_down':
|
|
|
|
case 'spacebar':
|
|
|
|
navigate.page_down();
|
|
|
|
return true;
|
2013-07-24 22:51:13 +02:00
|
|
|
}
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
const msg = current_msg_list.selected_message();
|
2013-08-08 00:03:28 +02:00
|
|
|
// Shortcuts that operate on a message
|
|
|
|
switch (event_name) {
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'message_actions':
|
|
|
|
return popovers.open_message_menu(msg);
|
|
|
|
case 'star_message':
|
2018-08-01 20:09:12 +02:00
|
|
|
message_flags.toggle_starred_and_update_server(msg);
|
2018-06-01 14:44:23 +02:00
|
|
|
return true;
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'narrow_by_recipient':
|
|
|
|
return do_narrow_action(narrow.by_recipient);
|
2018-11-13 16:11:42 +01:00
|
|
|
case 'narrow_by_topic':
|
2018-11-13 16:13:41 +01:00
|
|
|
return do_narrow_action(narrow.by_topic);
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'respond_to_author': // 'R': respond to author
|
|
|
|
compose_actions.respond_to_message({reply_type: "personal", trigger: 'hotkey pm'});
|
|
|
|
return true;
|
|
|
|
case 'compose_reply_with_mention': // '@': respond to message with mention to author
|
|
|
|
compose_actions.reply_with_mention({trigger: 'hotkey'});
|
|
|
|
return true;
|
|
|
|
case 'show_lightbox':
|
|
|
|
lightbox.show_from_selected_message();
|
|
|
|
return true;
|
|
|
|
case 'show_sender_info':
|
|
|
|
popovers.show_sender_info();
|
|
|
|
return true;
|
|
|
|
case 'toggle_reactions_popover': // ':': open reactions to message
|
|
|
|
reactions.open_reactions_popover();
|
|
|
|
return true;
|
2019-10-25 23:55:37 +02:00
|
|
|
case 'thumbs_up_emoji': { // '+': reacts with thumbs up emoji on selected message
|
2018-07-30 20:30:06 +02:00
|
|
|
// Use canonical name.
|
2019-11-02 00:06:25 +01:00
|
|
|
const thumbs_up_emoji_code = '1f44d';
|
|
|
|
const canonical_name = emoji_codes.codepoint_to_name[thumbs_up_emoji_code];
|
2018-07-30 20:30:06 +02:00
|
|
|
reactions.toggle_emoji_reaction(msg.id, canonical_name);
|
2018-05-07 01:38:14 +02:00
|
|
|
return true;
|
2019-10-25 23:55:37 +02:00
|
|
|
}
|
2018-05-07 01:38:14 +02:00
|
|
|
case 'toggle_mute':
|
|
|
|
muting_ui.toggle_mute(msg);
|
|
|
|
return true;
|
|
|
|
case 'toggle_message_collapse':
|
|
|
|
condense.toggle_collapse(msg);
|
|
|
|
return true;
|
|
|
|
case 'compose_quote_reply': // > : respond to selected message with quote
|
|
|
|
compose_actions.quote_and_reply({trigger: 'hotkey'});
|
|
|
|
return true;
|
2019-10-25 23:55:37 +02:00
|
|
|
case 'edit_message': {
|
2019-11-02 00:06:25 +01:00
|
|
|
const row = current_msg_list.get_row(msg.id);
|
2019-04-06 15:57:48 +02:00
|
|
|
message_edit.start(row);
|
|
|
|
return true;
|
2013-08-08 00:03:28 +02:00
|
|
|
}
|
2019-10-25 23:55:37 +02:00
|
|
|
}
|
2013-08-08 00:03:28 +02:00
|
|
|
|
2012-09-21 22:35:32 +02:00
|
|
|
return false;
|
2017-03-10 22:29:07 +01:00
|
|
|
};
|
2012-09-21 22:35:32 +02:00
|
|
|
|
2012-10-03 17:04:43 +02:00
|
|
|
/* We register both a keydown and a keypress function because
|
|
|
|
we want to intercept pgup/pgdn, escape, etc, and process them
|
|
|
|
as they happen on the keyboard. However, if we processed
|
|
|
|
letters/numbers in keydown, we wouldn't know what the case of
|
|
|
|
the letters were.
|
|
|
|
|
|
|
|
We want case-sensitive hotkeys (such as in the case of r vs R)
|
|
|
|
so we bail in .keydown if the event is a letter or number and
|
|
|
|
instead just let keypress go for it. */
|
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
exports.process_keydown = function (e) {
|
|
|
|
activity.new_user_input = true;
|
2019-11-02 00:06:25 +01:00
|
|
|
const hotkey = exports.get_keydown_hotkey(e);
|
2017-03-13 21:41:28 +01:00
|
|
|
if (!hotkey) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return exports.process_hotkey(e, hotkey);
|
|
|
|
};
|
|
|
|
|
2012-11-11 22:48:51 +01:00
|
|
|
$(document).keydown(function (e) {
|
2017-03-13 21:41:28 +01:00
|
|
|
if (exports.process_keydown(e)) {
|
2017-03-14 02:28:33 +01:00
|
|
|
// TODO: We should really move this resize code
|
|
|
|
// so it only executes as part of navigation actions.
|
|
|
|
resize.resize_bottom_whitespace();
|
2017-03-13 21:41:28 +01:00
|
|
|
e.preventDefault();
|
2012-11-29 19:55:08 +01:00
|
|
|
}
|
2012-10-03 17:04:43 +02:00
|
|
|
});
|
|
|
|
|
2017-03-13 21:41:28 +01:00
|
|
|
exports.process_keypress = function (e) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const hotkey = exports.get_keypress_hotkey(e);
|
2017-03-13 21:41:28 +01:00
|
|
|
if (!hotkey) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return exports.process_hotkey(e, hotkey);
|
|
|
|
};
|
|
|
|
|
2012-11-11 22:48:51 +01:00
|
|
|
$(document).keypress(function (e) {
|
2017-03-13 21:41:28 +01:00
|
|
|
if (exports.process_keypress(e)) {
|
|
|
|
e.preventDefault();
|
2012-11-29 19:55:08 +01:00
|
|
|
}
|
2012-09-21 22:35:32 +02:00
|
|
|
});
|
2012-10-18 19:58:10 +02:00
|
|
|
|
2019-10-25 09:45:13 +02:00
|
|
|
window.hotkey = exports;
|