2017-03-18 20:29:55 +01:00
|
|
|
(function () {
|
|
|
|
|
|
|
|
// This is where most of our initialization takes place.
|
|
|
|
// TODO: Organize it a lot better. In particular, move bigger
|
|
|
|
// functions to other modules.
|
|
|
|
|
|
|
|
/* We use 'visibility' rather than 'display' and jQuery's show() / hide(),
|
|
|
|
because we want to reserve space for the email address. This avoids
|
|
|
|
things jumping around slightly when the email address is shown. */
|
|
|
|
|
|
|
|
var current_message_hover;
|
|
|
|
function message_unhover() {
|
|
|
|
if (current_message_hover === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
current_message_hover.find('span.edit_content').html("");
|
|
|
|
current_message_hover.removeClass('message_hovered');
|
|
|
|
current_message_hover = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function message_hover(message_row) {
|
|
|
|
var message;
|
|
|
|
|
|
|
|
var id = parseInt(message_row.attr("zid"), 10);
|
|
|
|
if (current_message_hover && message_row && current_message_hover.attr("zid") === message_row.attr("zid")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Don't allow on-hover editing for local-only messages
|
|
|
|
if (message_row.hasClass('local')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
message = current_msg_list.get(rows.id(message_row));
|
|
|
|
message_unhover();
|
|
|
|
message_row.addClass('message_hovered');
|
|
|
|
current_message_hover = message_row;
|
|
|
|
|
|
|
|
if (!message.sent_by_me) {
|
|
|
|
// The actions and reactions icon hover logic is handled entirely by CSS
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// But the message edit hover icon is determined by whether the message is still editablex
|
|
|
|
if ((message_edit.get_editability(message) === message_edit.editability_types.FULL) &&
|
|
|
|
!message.status_message) {
|
|
|
|
message_row.find(".edit_content").html('<i class="icon-vector-pencil edit_content_button"></i>');
|
|
|
|
} else {
|
|
|
|
message_row.find(".edit_content").html('<i class="icon-vector-file-text-alt edit_content_button" data-msgid="' + id + '"></i>');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
var throttled_mousewheelhandler = $.throttle(50, function (e, delta) {
|
|
|
|
// Most of the mouse wheel's work will be handled by the
|
|
|
|
// scroll handler, but when we're at the top or bottom of the
|
|
|
|
// page, the pointer may still need to move.
|
|
|
|
|
2017-07-03 01:53:36 +02:00
|
|
|
if (delta < 0) {
|
2017-03-18 20:29:55 +01:00
|
|
|
if (message_viewport.at_top()) {
|
|
|
|
navigate.up();
|
|
|
|
}
|
2017-07-03 01:53:36 +02:00
|
|
|
} else if (delta > 0) {
|
2017-03-18 20:29:55 +01:00
|
|
|
if (message_viewport.at_bottom()) {
|
|
|
|
navigate.down();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
message_viewport.last_movement_direction = delta;
|
|
|
|
});
|
|
|
|
|
2017-07-03 01:53:36 +02:00
|
|
|
message_viewport.message_pane.on('wheel', function (e) {
|
|
|
|
var delta = e.originalEvent.deltaY;
|
2017-05-27 15:40:54 +02:00
|
|
|
if (!overlays.is_active()) {
|
2017-03-18 20:29:55 +01:00
|
|
|
// In the message view, we use a throttled mousewheel handler.
|
|
|
|
throttled_mousewheelhandler(e, delta);
|
|
|
|
}
|
|
|
|
// If in a modal, we neither handle the event nor
|
|
|
|
// preventDefault, allowing the modal to scroll normally.
|
|
|
|
});
|
|
|
|
|
|
|
|
$(window).resize($.throttle(50, resize.handler));
|
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
// Scrolling in overlays. input boxes, and other elements that
|
2017-03-18 20:29:55 +01:00
|
|
|
// explicitly scroll should not scroll the main view. Stop
|
|
|
|
// propagation in all cases. Also, ignore the event if the
|
|
|
|
// element is already at the top or bottom. Otherwise we get a
|
|
|
|
// new scroll event on the parent (?).
|
2017-07-03 01:53:36 +02:00
|
|
|
$('.modal-body, .scrolling_list, input, textarea').on('wheel', function (e) {
|
2017-03-18 20:29:55 +01:00
|
|
|
var self = $(this);
|
|
|
|
var scroll = self.scrollTop();
|
2017-07-03 01:53:36 +02:00
|
|
|
var delta = e.originalEvent.deltaY;
|
2017-03-18 20:29:55 +01:00
|
|
|
|
|
|
|
// The -1 fudge factor is important here due to rounding errors. Better
|
|
|
|
// to err on the side of not scrolling.
|
|
|
|
var max_scroll = this.scrollHeight - self.innerHeight() - 1;
|
|
|
|
|
|
|
|
e.stopPropagation();
|
2017-07-03 01:53:36 +02:00
|
|
|
if ( ((delta < 0) && (scroll <= 0))
|
|
|
|
|| ((delta > 0) && (scroll >= max_scroll))) {
|
2017-03-18 20:29:55 +01:00
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Ignore wheel events in the compose area which weren't already handled above.
|
2017-07-03 01:53:36 +02:00
|
|
|
$('#compose').on('wheel', function (e) {
|
2017-03-18 20:29:55 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
|
|
|
// A little hackish, because it doesn't seem to totally get us the
|
|
|
|
// exact right width for the floating_recipient_bar and compose
|
|
|
|
// box, but, close enough for now.
|
|
|
|
resize.handler();
|
|
|
|
|
|
|
|
if (!page_params.left_side_userlist) {
|
|
|
|
$("#navbar-buttons").addClass("right-userlist");
|
|
|
|
}
|
|
|
|
|
2017-07-07 18:15:10 +02:00
|
|
|
if (page_params.high_contrast_mode) {
|
|
|
|
$("body").addClass("high-contrast");
|
|
|
|
}
|
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
$("#main_div").on("mouseover", ".message_row", function () {
|
|
|
|
var row = $(this).closest(".message_row");
|
|
|
|
message_hover(row);
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseleave", ".message_row", function () {
|
|
|
|
message_unhover();
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseover", ".message_sender", function () {
|
|
|
|
var row = $(this).closest(".message_row");
|
|
|
|
row.addClass("sender_name_hovered");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseout", ".message_sender", function () {
|
|
|
|
var row = $(this).closest(".message_row");
|
|
|
|
row.removeClass("sender_name_hovered");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#subscriptions_table").on("mouseover", ".subscription_header", function () {
|
|
|
|
$(this).addClass("active");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#subscriptions_table").on("mouseout", ".subscription_header", function () {
|
|
|
|
$(this).removeClass("active");
|
|
|
|
});
|
|
|
|
|
2017-04-14 16:26:00 +02:00
|
|
|
$("#stream").on('blur', function () { compose_actions.decorate_stream_bar(this.value); });
|
2017-03-18 20:29:55 +01:00
|
|
|
|
|
|
|
$(window).on('blur', function () {
|
|
|
|
$(document.body).addClass('window_blurred');
|
|
|
|
});
|
|
|
|
|
|
|
|
$(window).on('focus', function () {
|
|
|
|
$(document.body).removeClass('window_blurred');
|
|
|
|
});
|
|
|
|
|
|
|
|
$(document).on('message_selected.zulip', function (event) {
|
|
|
|
if (current_msg_list !== event.msg_list) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event.id === -1) {
|
|
|
|
// If the message list is empty, don't do anything
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var row = event.msg_list.get_row(event.id);
|
|
|
|
$('.selected_message').removeClass('selected_message');
|
|
|
|
row.addClass('selected_message');
|
|
|
|
|
|
|
|
if (event.then_scroll) {
|
|
|
|
if (row.length === 0) {
|
|
|
|
var row_from_dom = current_msg_list.get_row(event.id);
|
|
|
|
blueslip.debug("message_selected missing selected row", {
|
|
|
|
previously_selected: event.previously_selected,
|
|
|
|
selected_id: event.id,
|
|
|
|
selected_idx: event.msg_list.selected_idx(),
|
|
|
|
selected_idx_exact: event.msg_list._items.indexOf(event.msg_list.get(event.id)),
|
|
|
|
render_start: event.msg_list.view._render_win_start,
|
|
|
|
render_end: event.msg_list.view._render_win_end,
|
|
|
|
selected_id_from_idx: event.msg_list._items[event.msg_list.selected_idx()].id,
|
|
|
|
msg_list_sorted: _.isEqual(
|
|
|
|
_.pluck(event.msg_list._items, 'id'),
|
|
|
|
_.chain(current_msg_list._items).pluck('id').clone().value().sort()
|
|
|
|
),
|
|
|
|
found_in_dom: row_from_dom.length,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (event.target_scroll_offset !== undefined) {
|
|
|
|
message_viewport.set_message_offset(event.target_scroll_offset);
|
|
|
|
} else {
|
|
|
|
// Scroll to place the message within the current view;
|
|
|
|
// but if this is the initial placement of the pointer,
|
|
|
|
// just place it in the very center
|
|
|
|
message_viewport.recenter_view(row, {from_scroll: event.from_scroll,
|
|
|
|
force_center: event.previously_selected === -1});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseenter", ".message_time", function (e) {
|
|
|
|
var time_elem = $(e.target);
|
|
|
|
var row = time_elem.closest(".message_row");
|
|
|
|
var message = current_msg_list.get(rows.id(row));
|
|
|
|
timerender.set_full_datetime(message, time_elem);
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#streams_header h4').tooltip({ placement: 'right',
|
|
|
|
animation: false });
|
|
|
|
|
|
|
|
$('#streams_header i[data-toggle="tooltip"]').tooltip({ placement: 'left',
|
|
|
|
animation: false });
|
|
|
|
|
|
|
|
$('.message_failed i[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
2017-01-17 20:51:30 +01:00
|
|
|
$('.copy_message[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
2017-04-26 05:14:33 +02:00
|
|
|
$("body").on("mouseover", ".message_edit_content", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).closest(".message_row").find(".copy_message").show();
|
|
|
|
});
|
|
|
|
|
2017-04-26 05:14:33 +02:00
|
|
|
$("body").on("mouseout", ".message_edit_content", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).closest(".message_row").find(".copy_message").hide();
|
|
|
|
});
|
|
|
|
|
2017-03-26 12:15:50 +02:00
|
|
|
$("body").on("mouseenter", ".copy_message", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).show();
|
|
|
|
$(this).tooltip('show');
|
|
|
|
});
|
|
|
|
|
2017-03-26 12:15:50 +02:00
|
|
|
$("body").on("mouseleave", ".copy_message", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).tooltip('hide');
|
|
|
|
});
|
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
if (!page_params.realm_allow_message_editing) {
|
|
|
|
$("#edit-message-hotkey-help").hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_params.realm_presence_disabled) {
|
|
|
|
$("#user-list").hide();
|
|
|
|
$("#group-pm-list").hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (feature_flags.full_width) {
|
|
|
|
ui.switchToFullWidth();
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize other stuff
|
|
|
|
reload.initialize();
|
2017-07-04 03:14:24 +02:00
|
|
|
server_events.initialize();
|
2017-05-24 04:15:52 +02:00
|
|
|
people.initialize();
|
2017-05-25 02:50:50 +02:00
|
|
|
bot_data.initialize(); // Must happen after people.initialize()
|
2017-07-04 01:30:47 +02:00
|
|
|
message_fetch.initialize();
|
2017-08-16 22:00:19 +02:00
|
|
|
emoji.initialize();
|
|
|
|
markdown.initialize(); // Must happen after emoji.initialize()
|
2017-03-18 20:29:55 +01:00
|
|
|
composebox_typeahead.initialize();
|
|
|
|
search.initialize();
|
|
|
|
tutorial.initialize();
|
|
|
|
notifications.initialize();
|
|
|
|
gear_menu.initialize();
|
2017-05-09 06:58:49 +02:00
|
|
|
settings_sections.initialize();
|
2017-03-18 20:29:55 +01:00
|
|
|
hashchange.initialize();
|
|
|
|
pointer.initialize();
|
|
|
|
unread_ui.initialize();
|
|
|
|
activity.initialize();
|
2017-08-16 23:54:26 +02:00
|
|
|
emoji_picker.initialize();
|
2017-05-24 22:30:32 +02:00
|
|
|
compose_fade.initialize();
|
2017-06-14 19:58:51 +02:00
|
|
|
pm_list.initialize();
|
2017-06-02 00:36:28 +02:00
|
|
|
stream_list.initialize();
|
2017-06-02 23:12:15 +02:00
|
|
|
drafts.initialize();
|
sending messages: Extract sent_messages.js.
This commit extract send_messages.js to clean up code related
to the following things:
* sending data to /json/report_send_time
* restarting the event loop if events don't arrive on time
The code related to /json/report changes the following ways:
* We track the state almost completely in the new
send_messages.js module, with other modules just
making one-line calls.
* We no longer send "displayed" times to the servers, since
we were kind of lying about them anyway.
* We now explicitly track the state of each single sent
message in its own object.
* We now look up data related to the messages by local_id,
instead of message_id. The problem with message_id was
that is was mutable. Now we use local_id, and we extend
the local_id concept to messages that don't get rendered
client side. We no longer need to react to the
'message_id_changed' event to change our hash key.
* The code used to live in many places:
* various big chunks were scattered among compose.js,
and those were all moved or reduced to one-line
calls into the new module
* echo.js continues to make basically one-line calls,
but it no longer calls compose.report_as_received(),
nor does it set the "start" time.
* message_util.js used to report received events, but
only when they finally got drawn in the home view;
this code is gone now
The code related to restarting the event loop if events don't arrive
changes as follows:
* The timer now gets set up from within
send_messages.message_state.report_server_ack,
where we can easily inspect the current state of the
possibly-still-in-flight message.
* The code to confirm that an event was received happens now
in server_events.js, rather than later, so that we don't
falsely blame the event loop for a downstream bug. (Plus
it's easier to just do it one place.)
This change removes a fair amount of code from our node tests. Some
of the removal is good stuff related to us completing killing off
unnecessary code. Other removals are more expediency-driven, and
we should make another sweep at ramping up our coverage on compose.js,
with possibly a little more mocking of the new `send_messages` code
layer, since it's now abstracted better.
There is also some minor cleanup to echo.resend_message() in this
commit.
See #5968 for a detailed breakdown of the changes.
2017-07-30 12:56:46 +02:00
|
|
|
sent_messages.initialize();
|
2017-06-26 21:29:08 +02:00
|
|
|
compose.initialize();
|
2017-08-08 02:16:35 +02:00
|
|
|
ui.initialize();
|
2017-03-18 20:29:55 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}());
|