zulip/static/js/ui_init.js

346 lines
12 KiB
JavaScript
Raw Normal View History

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_edit.is_topic_editable(message)) {
2017-03-18 20:29:55 +01:00
// The actions and reactions icon hover logic is handled entirely by CSS
return;
}
2017-09-26 20:55:18 +02:00
// But the message edit hover icon is determined by whether the message is still editable
if (message_edit.get_editability(message) === message_edit.editability_types.FULL &&
2017-03-18 20:29:55 +01:00
!message.status_message) {
message_row.find(".edit_content").html('<i class="fa fa-pencil edit_content_button" aria-hidden="true" title="Edit (e)"></i>');
2017-03-18 20:29:55 +01:00
} else {
message_row.find(".edit_content").html('<i class="fa fa-file-text-o edit_content_button" aria-hidden="true" title="View source (e)" data-msgid="' + id + '"></i>');
2017-03-18 20:29:55 +01:00
}
}
exports.initialize_kitchen_sink_stuff = function () {
// TODO:
// This function is a historical dumping ground
// for lots of miscellaneous setup. Almost all of
// the code here can probably be moved to more
// specific-purpose modules like message_viewport.js.
var throttled_mousewheelhandler = _.throttle(function (e, delta) {
2017-03-18 20:29:55 +01:00
// 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.
if (delta < 0) {
2017-03-18 20:29:55 +01:00
if (message_viewport.at_top()) {
navigate.up();
}
} 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;
}, 50);
2017-03-18 20:29:55 +01:00
message_viewport.message_pane.on('wheel', function (e) {
var delta = e.originalEvent.deltaY;
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(resize.handler, 50));
2017-03-18 20:29:55 +01: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 (?).
$('.modal-body, .scrolling_list, input, textarea').on('wheel', function (e) {
var self = ui.get_scroll_element($(this));
2017-03-18 20:29:55 +01:00
var scroll = self.scrollTop();
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 = self.prop("scrollHeight") - self.innerHeight() - 1;
2017-03-18 20:29:55 +01:00
e.stopPropagation();
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.
$('#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");
}
if (page_params.high_contrast_mode) {
$("body").addClass("high-contrast");
}
if (!page_params.dense_mode) {
$("body").addClass("less_dense_mode");
} else {
$("body").addClass("more_dense_mode");
}
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", ".sender_info_hover", function () {
2017-03-18 20:29:55 +01:00
var row = $(this).closest(".message_row");
row.addClass("sender_name_hovered");
});
$("#main_div").on("mouseout", ".sender_info_hover", function () {
2017-03-18 20:29:55 +01:00
var row = $(this).closest(".message_row");
row.removeClass("sender_name_hovered");
});
$("#main_div").on("mouseenter", ".youtube-video a", function () {
$(this).addClass("fa fa-play");
});
$("#main_div").on("mouseleave", ".youtube-video a", function () {
$(this).removeClass("fa fa-play");
});
2017-03-18 20:29:55 +01:00
$("#subscriptions_table").on("mouseover", ".subscription_header", function () {
$(this).addClass("active");
});
$("#subscriptions_table").on("mouseout", ".subscription_header", function () {
$(this).removeClass("active");
});
$("#stream_message_recipient_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);
var messages = event.msg_list.all_messages();
2017-03-18 20:29:55 +01:00
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: messages.indexOf(event.msg_list.get(event.id)),
2017-03-18 20:29:55 +01:00
render_start: event.msg_list.view._render_win_start,
render_end: event.msg_list.view._render_win_end,
selected_id_from_idx: messages[event.msg_list.selected_idx()].id,
2017-03-18 20:29:55 +01:00
msg_list_sorted: _.isEqual(
_.pluck(messages, 'id'),
_.chain(current_msg_list.all_messages()).pluck('id').clone().value().sort()
2017-03-18 20:29:55 +01:00
),
found_in_dom: row_from_dom.length,
});
}
if (event.target_scroll_offset !== undefined) {
current_msg_list.view.set_message_offset(event.target_scroll_offset);
2017-03-18 20:29:55 +01:00
} 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});
2017-03-18 20:29:55 +01:00
}
}
});
$("#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});
2017-03-18 20:29:55 +01:00
$('#streams_header i[data-toggle="tooltip"]').tooltip({placement: 'left',
animation: false});
2017-03-18 20:29:55 +01:00
$('#userlist-header #userlist-title').tooltip({placement: 'right',
animation: false});
$('#userlist-header #user_filter_icon').tooltip({placement: 'left',
animation: false});
2017-03-18 20:29:55 +01:00
$('.message_failed i[data-toggle="tooltip"]').tooltip();
$('.copy_message[data-toggle="tooltip"]').tooltip();
$('#keyboard-icon').tooltip();
$("body").on("mouseover", ".message_edit_content", function () {
$(this).closest(".message_row").find(".copy_message").show();
});
$("body").on("mouseout", ".message_edit_content", function () {
$(this).closest(".message_row").find(".copy_message").hide();
});
$("body").on("mouseenter", ".copy_message", function () {
$(this).show();
$(this).tooltip('show');
});
$("body").on("mouseleave", ".copy_message", function () {
$(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();
}
};
2017-03-18 20:29:55 +01:00
exports.initialize_everything = function () {
2017-03-18 20:29:55 +01:00
// initialize other stuff
people.initialize();
scroll_bar.initialize();
message_viewport.initialize();
exports.initialize_kitchen_sink_stuff();
echo.initialize();
stream_color.initialize();
stream_edit.initialize();
stream_data.initialize();
muting.initialize();
subs.initialize();
stream_list.initialize();
condense.initialize();
lightbox.initialize();
click_handlers.initialize();
copy_and_paste.initialize();
overlays.initialize();
invite.initialize();
timerender.initialize();
if (!page_params.search_pills_enabled) {
tab_bar.initialize();
}
server_events.initialize();
user_status.initialize();
compose_pm_pill.initialize();
search_pill_widget.initialize();
reload.initialize();
user_groups.initialize();
unread.initialize();
bot_data.initialize(); // Must happen after people.initialize()
message_fetch.initialize();
message_scroll.initialize();
emoji.initialize();
markdown.initialize(); // Must happen after emoji.initialize()
compose.initialize();
composebox_typeahead.initialize(); // Must happen after compose.initialize()
2017-03-18 20:29:55 +01:00
search.initialize();
tutorial.initialize();
notifications.initialize();
gear_menu.initialize();
settings_panel_menu.initialize();
settings_sections.initialize();
settings_toggle.initialize();
2017-03-18 20:29:55 +01:00
hashchange.initialize();
pointer.initialize();
unread_ui.initialize();
activity.initialize();
emoji_picker.initialize();
2017-05-24 22:30:32 +02:00
compose_fade.initialize();
pm_list.initialize();
topic_list.initialize();
topic_zoom.initialize();
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();
hotspots.initialize();
ui.initialize();
panels.initialize();
typing.initialize();
starred_messages.initialize();
user_status_ui.initialize();
};
$(function () {
exports.initialize_everything();
2017-03-18 20:29:55 +01:00
});
}());