2012-11-16 16:45:39 +01:00
|
|
|
var ui = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2013-07-16 20:00:47 +02:00
|
|
|
var actively_scrolling = false;
|
|
|
|
|
2016-04-10 14:15:47 +02:00
|
|
|
exports.have_scrolled_away_from_top = true;
|
|
|
|
|
2013-07-16 20:00:47 +02:00
|
|
|
exports.actively_scrolling = function () {
|
|
|
|
return actively_scrolling;
|
|
|
|
};
|
|
|
|
|
2012-12-03 20:54:29 +01:00
|
|
|
// What, if anything, obscures the home tab?
|
|
|
|
exports.home_tab_obscured = function () {
|
2013-08-01 17:47:48 +02:00
|
|
|
if ($('.modal:visible').length > 0) {
|
2012-12-03 20:54:29 +01:00
|
|
|
return 'modal';
|
2013-08-01 17:47:48 +02:00
|
|
|
}
|
|
|
|
if (! $('#home').hasClass('active')) {
|
2012-12-03 20:54:29 +01:00
|
|
|
return 'other_tab';
|
2013-08-01 17:47:48 +02:00
|
|
|
}
|
2012-12-03 20:54:29 +01:00
|
|
|
return false;
|
2012-11-30 18:28:23 +01:00
|
|
|
};
|
|
|
|
|
2012-12-19 21:19:29 +01:00
|
|
|
exports.change_tab_to = function (tabname) {
|
2013-01-16 21:42:35 +01:00
|
|
|
$('#gear-menu a[href="' + tabname + '"]').tab('show');
|
2012-12-19 21:19:29 +01:00
|
|
|
};
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
exports.focus_on = function (field_id) {
|
2012-10-09 17:41:34 +02:00
|
|
|
// Call after autocompleting on a field, to advance the focus to
|
|
|
|
// the next input field.
|
|
|
|
|
|
|
|
// Bootstrap's typeahead does not expose a callback for when an
|
|
|
|
// autocomplete selection has been made, so we have to do this
|
|
|
|
// manually.
|
|
|
|
$("#" + field_id).focus();
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-09 17:41:34 +02:00
|
|
|
|
2016-12-14 23:25:49 +01:00
|
|
|
exports.blur_active_element = function () {
|
|
|
|
// this blurs anything that may perhaps be actively focused on.
|
|
|
|
document.activeElement.blur();
|
|
|
|
};
|
|
|
|
|
2013-05-10 21:06:40 +02:00
|
|
|
function amount_to_paginate() {
|
|
|
|
// Some day we might have separate versions of this function
|
|
|
|
// for Page Up vs. Page Down, but for now it's the same
|
|
|
|
// strategy in either direction.
|
2013-06-06 16:10:12 +02:00
|
|
|
var info = viewport.message_viewport_info();
|
2013-05-20 22:26:20 +02:00
|
|
|
var page_size = info.visible_height;
|
2013-05-10 21:06:40 +02:00
|
|
|
|
2013-08-06 21:32:15 +02:00
|
|
|
// We don't want to page up a full page, because Zulip users
|
2013-05-10 21:06:40 +02:00
|
|
|
// are especially worried about missing messages, so we want
|
|
|
|
// a little bit of the old page to stay on the screen. The
|
|
|
|
// value chosen here is roughly 2 or 3 lines of text, but there
|
|
|
|
// is nothing sacred about it, and somebody more anal than me
|
|
|
|
// might wish to tie this to the size of some particular DOM
|
|
|
|
// element.
|
|
|
|
var overlap_amount = 55;
|
|
|
|
|
|
|
|
var delta = page_size - overlap_amount;
|
|
|
|
|
|
|
|
// If the user has shrunk their browser a whole lot, pagination
|
|
|
|
// is not going to be very pleasant, but we can at least
|
|
|
|
// ensure they go in the right direction.
|
2013-08-01 17:47:48 +02:00
|
|
|
if (delta < 1) {
|
|
|
|
delta = 1;
|
|
|
|
}
|
2013-05-10 21:06:40 +02:00
|
|
|
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.page_up_the_right_amount = function () {
|
|
|
|
// This function's job is to scroll up the right amount,
|
|
|
|
// after the user hits Page Up. We do this ourselves
|
|
|
|
// because we can't rely on the browser to account for certain
|
|
|
|
// page elements, like the compose box, that sit in fixed
|
|
|
|
// positions above the message pane. For other scrolling
|
|
|
|
// related adjustements, try to make those happen in the
|
|
|
|
// scroll handlers, not here.
|
|
|
|
var delta = amount_to_paginate();
|
|
|
|
viewport.scrollTop(viewport.scrollTop() - delta);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.page_down_the_right_amount = function () {
|
|
|
|
// see also: page_up_the_right_amount
|
|
|
|
var delta = amount_to_paginate();
|
|
|
|
viewport.scrollTop(viewport.scrollTop() + delta);
|
|
|
|
};
|
|
|
|
|
2013-07-23 00:25:25 +02:00
|
|
|
exports.replace_emoji_with_text = function (element) {
|
|
|
|
element.find(".emoji").replaceWith(function () {
|
|
|
|
return $(this).attr("alt");
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2012-10-03 21:44:07 +02:00
|
|
|
/* 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. */
|
|
|
|
|
2013-02-09 04:33:06 +01:00
|
|
|
var current_message_hover;
|
|
|
|
function message_unhover() {
|
2013-08-01 17:47:48 +02:00
|
|
|
if (current_message_hover === undefined) {
|
2013-02-09 04:33:06 +01:00
|
|
|
return;
|
2013-08-01 17:47:48 +02:00
|
|
|
}
|
2016-11-16 21:26:41 +01:00
|
|
|
current_message_hover.find('span.edit_content').html("");
|
2013-02-09 04:33:06 +01:00
|
|
|
current_message_hover.removeClass('message_hovered');
|
|
|
|
current_message_hover = undefined;
|
2012-10-03 21:44:07 +02:00
|
|
|
}
|
|
|
|
|
2013-02-09 04:33:06 +01:00
|
|
|
function message_hover(message_row) {
|
2013-08-15 23:43:16 +02:00
|
|
|
var message;
|
2016-11-09 01:26:15 +01:00
|
|
|
|
2016-11-11 00:41:00 +01:00
|
|
|
var id = parseInt(message_row.attr("zid"), 10);
|
2013-08-15 23:43:16 +02:00
|
|
|
if (current_message_hover && message_row && current_message_hover.attr("zid") === message_row.attr("zid")) {
|
|
|
|
return;
|
|
|
|
}
|
2014-01-24 18:01:12 +01:00
|
|
|
// Don't allow on-hover editing for local-only messages
|
|
|
|
if (message_row.hasClass('local')) {
|
|
|
|
return;
|
|
|
|
}
|
2013-08-15 23:43:16 +02:00
|
|
|
message = current_msg_list.get(rows.id(message_row));
|
2013-02-09 04:33:06 +01:00
|
|
|
message_unhover();
|
|
|
|
message_row.addClass('message_hovered');
|
2016-12-31 06:38:23 +01:00
|
|
|
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
|
2016-12-02 15:16:33 +01:00
|
|
|
if ((message_edit.get_editability(message) === message_edit.editability_types.FULL) &&
|
|
|
|
!message.status_message) {
|
2016-11-11 00:41:00 +01:00
|
|
|
message_row.find(".edit_content").html('<i class="icon-vector-pencil edit_content_button"></i>');
|
|
|
|
} else {
|
2016-12-31 06:38:23 +01:00
|
|
|
message_row.find(".edit_content").html('<i class="icon-vector-file-text-alt edit_content_button" data-msgid="' + id + '"></i>');
|
2013-08-15 23:43:16 +02:00
|
|
|
}
|
2012-10-03 21:44:07 +02:00
|
|
|
}
|
|
|
|
|
2016-03-15 18:22:20 +01:00
|
|
|
/* Arguments used in the report_* functions are,
|
|
|
|
response- response that we want to display
|
|
|
|
status_box- element being used to display the response
|
|
|
|
cls- class that we want to add/remove to/from the status_box
|
|
|
|
type- used to define more complex logic for special cases (currently being
|
|
|
|
used only for subscriptions-status) */
|
|
|
|
|
|
|
|
exports.report_message = function (response, status_box, cls, type) {
|
2013-08-01 17:47:48 +02:00
|
|
|
if (cls === undefined) {
|
2012-10-31 23:38:34 +01:00
|
|
|
cls = 'alert';
|
2013-08-01 17:47:48 +02:00
|
|
|
}
|
2012-10-31 23:38:34 +01:00
|
|
|
|
2016-03-15 18:22:20 +01:00
|
|
|
if (type === undefined) {
|
|
|
|
type = ' ';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type === 'subscriptions-status') {
|
|
|
|
status_box.removeClass(status_classes).addClass(cls).children('#response')
|
|
|
|
.text(response).stop(true).fadeTo(0, 1);
|
|
|
|
} else {
|
|
|
|
status_box.removeClass(status_classes).addClass(cls)
|
2012-10-31 23:38:34 +01:00
|
|
|
.text(response).stop(true).fadeTo(0, 1);
|
2016-03-15 18:22:20 +01:00
|
|
|
}
|
|
|
|
|
2012-10-31 23:38:34 +01:00
|
|
|
status_box.show();
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-31 23:38:34 +01:00
|
|
|
|
2016-03-15 18:22:20 +01:00
|
|
|
exports.report_error = function (response, xhr, status_box, type) {
|
2015-08-21 01:23:53 +02:00
|
|
|
if (xhr && xhr.status.toString().charAt(0) === "4") {
|
2012-10-03 21:44:07 +02:00
|
|
|
// Only display the error response for 4XX, where we've crafted
|
|
|
|
// a nice response.
|
2016-08-24 23:56:23 +02:00
|
|
|
response += ": " + JSON.parse(xhr.responseText).msg;
|
2012-10-03 21:44:07 +02:00
|
|
|
}
|
|
|
|
|
2016-03-15 18:22:20 +01:00
|
|
|
ui.report_message(response, status_box, 'alert-error', type);
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-03 21:44:07 +02:00
|
|
|
|
2016-03-15 18:22:20 +01:00
|
|
|
exports.report_success = function (response, status_box, type) {
|
|
|
|
ui.report_message(response, status_box, 'alert-success', type);
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-17 20:43:20 +02:00
|
|
|
|
2013-12-19 17:03:08 +01:00
|
|
|
function update_message_in_all_views(message_id, callback) {
|
2016-04-25 23:45:25 +02:00
|
|
|
_.each([message_list.all, home_msg_list, message_list.narrowed], function (list) {
|
2013-12-19 17:03:08 +01:00
|
|
|
if (list === undefined) {
|
2013-03-27 18:30:02 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-12-19 17:03:08 +01:00
|
|
|
var row = list.get_row(message_id);
|
2013-03-27 18:30:02 +01:00
|
|
|
if (row === undefined) {
|
2013-12-19 17:03:08 +01:00
|
|
|
// The row may not exist, e.g. if you do an action on a message in
|
|
|
|
// a narrowed view
|
2013-03-27 18:30:02 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-12-19 17:03:08 +01:00
|
|
|
callback(row);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-03-14 15:30:29 +01:00
|
|
|
exports.find_message = function (message_id) {
|
2014-01-28 22:44:34 +01:00
|
|
|
// Try to find the message object. It might be in the narrow list
|
2016-04-21 22:49:23 +02:00
|
|
|
// (if it was loaded when narrowed), or only in the message_list.all
|
2014-01-28 22:44:34 +01:00
|
|
|
// (if received from the server while in a different narrow)
|
|
|
|
var message;
|
2016-04-25 23:45:25 +02:00
|
|
|
_.each([message_list.all, home_msg_list, message_list.narrowed], function (msg_list) {
|
2014-01-28 22:44:34 +01:00
|
|
|
if (msg_list !== undefined && message === undefined) {
|
|
|
|
message = msg_list.get(message_id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return message;
|
2014-03-14 15:30:29 +01:00
|
|
|
};
|
2014-01-28 22:44:34 +01:00
|
|
|
|
2013-12-19 17:03:08 +01:00
|
|
|
exports.update_starred = function (message_id, starred) {
|
|
|
|
// Update the message object pointed to by the various message
|
|
|
|
// lists.
|
2014-03-14 15:30:29 +01:00
|
|
|
var message = exports.find_message(message_id);
|
2013-12-19 17:03:08 +01:00
|
|
|
|
2014-01-31 17:44:21 +01:00
|
|
|
unread.mark_message_as_read(message);
|
2013-12-19 17:03:08 +01:00
|
|
|
|
2014-01-30 18:08:02 +01:00
|
|
|
message.starred = starred;
|
2013-12-19 17:03:08 +01:00
|
|
|
|
|
|
|
// Avoid a full re-render, but update the star in each message
|
|
|
|
// table in which it is visible.
|
|
|
|
update_message_in_all_views(message_id, function update_row(row) {
|
2014-01-23 17:52:28 +01:00
|
|
|
var elt = row.find(".message_star");
|
|
|
|
if (starred) {
|
|
|
|
elt.addClass("icon-vector-star").removeClass("icon-vector-star-empty").removeClass("empty-star");
|
|
|
|
} else {
|
|
|
|
elt.removeClass("icon-vector-star").addClass("icon-vector-star-empty").addClass("empty-star");
|
|
|
|
}
|
2013-04-14 05:04:47 +02:00
|
|
|
var title_state = message.starred ? "Unstar" : "Star";
|
2014-01-23 17:52:28 +01:00
|
|
|
elt.attr("title", title_state + " this message");
|
2013-03-27 18:30:02 +01:00
|
|
|
});
|
2014-01-23 17:52:28 +01:00
|
|
|
};
|
2013-03-27 18:30:02 +01:00
|
|
|
|
2013-12-19 17:03:08 +01:00
|
|
|
var local_messages_to_show = [];
|
|
|
|
var show_message_timestamps = _.throttle(function () {
|
|
|
|
_.each(local_messages_to_show, function (message_id) {
|
|
|
|
update_message_in_all_views(message_id, function update_row(row) {
|
|
|
|
row.find('.message_time').toggleClass('notvisible', false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
local_messages_to_show = [];
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
exports.show_local_message_arrived = function (message_id) {
|
|
|
|
local_messages_to_show.push(message_id);
|
|
|
|
show_message_timestamps();
|
|
|
|
};
|
|
|
|
|
2014-01-02 17:34:16 +01:00
|
|
|
exports.show_message_failed = function (message_id, failed_msg) {
|
2013-12-19 17:03:08 +01:00
|
|
|
// Failed to send message, so display inline retry/cancel
|
|
|
|
update_message_in_all_views(message_id, function update_row(row) {
|
2014-01-02 17:34:16 +01:00
|
|
|
var failed_div = row.find('.message_failed');
|
|
|
|
failed_div.toggleClass('notvisible', false);
|
|
|
|
failed_div.find('.failed_text').attr('title', failed_msg);
|
2013-12-19 17:03:08 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.show_failed_message_success = function (message_id) {
|
|
|
|
// Previously failed message succeeded
|
|
|
|
update_message_in_all_views(message_id, function update_row(row) {
|
|
|
|
row.find('.message_failed').toggleClass('notvisible', true);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-06-13 23:48:23 +02:00
|
|
|
exports.small_avatar_url = function (message) {
|
2013-08-02 21:14:08 +02:00
|
|
|
// Try to call this function in all places where we need 25px
|
2013-11-19 17:15:48 +01:00
|
|
|
// avatar images, so that the browser can help
|
2013-06-13 23:48:23 +02:00
|
|
|
// us avoid unnecessary network trips. (For user-uploaded avatars,
|
2013-08-02 21:14:08 +02:00
|
|
|
// the s=25 parameter is essentially ignored, but it's harmless.)
|
2013-06-13 23:48:23 +02:00
|
|
|
//
|
2013-08-02 21:14:08 +02:00
|
|
|
// We actually request these at s=50, so that we look better
|
|
|
|
// on retina displays.
|
2013-06-13 23:48:23 +02:00
|
|
|
if (message.avatar_url) {
|
2013-08-02 21:14:08 +02:00
|
|
|
var url = message.avatar_url + "&s=50";
|
2013-06-14 17:46:01 +02:00
|
|
|
if (message.sent_by_me) {
|
2013-11-19 17:15:48 +01:00
|
|
|
url += "&stamp=" + settings.avatar_stamp;
|
2013-06-13 23:48:23 +02:00
|
|
|
}
|
|
|
|
return url;
|
|
|
|
}
|
2016-12-02 21:34:35 +01:00
|
|
|
return "";
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
|
|
|
|
2016-10-17 22:02:25 +02:00
|
|
|
exports.lightbox = function (data) {
|
|
|
|
switch (data.type) {
|
|
|
|
case "photo":
|
|
|
|
exports.lightbox_photo(data.image, data.user);
|
|
|
|
break;
|
|
|
|
case "youtube":
|
|
|
|
exports.youtube_video(data.id);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$("#overlay").addClass("show");
|
2017-01-03 20:59:53 +01:00
|
|
|
popovers.hide_all();
|
2016-10-17 22:02:25 +02:00
|
|
|
};
|
|
|
|
|
2016-09-22 02:05:24 +02:00
|
|
|
exports.lightbox_photo = function (image, user) {
|
|
|
|
// image should be an Image Object in JavaScript.
|
2016-12-02 17:09:31 +01:00
|
|
|
var url = $(image).attr("src");
|
|
|
|
var title = $(image).parent().attr("title");
|
2016-09-22 02:05:24 +02:00
|
|
|
|
2016-10-17 22:02:25 +02:00
|
|
|
$("#overlay .player-container").hide();
|
|
|
|
$("#overlay .image-actions, .image-description, .download").show();
|
|
|
|
|
2016-10-25 23:26:28 +02:00
|
|
|
var img = new Image();
|
|
|
|
img.src = url;
|
|
|
|
$("#overlay .image-preview").html("").show()
|
|
|
|
.append(img);
|
2016-09-22 02:05:24 +02:00
|
|
|
|
2016-10-17 22:02:25 +02:00
|
|
|
$(".image-description .title").text(title || "N/A");
|
|
|
|
$(".image-description .user").text(user);
|
2016-09-22 02:05:24 +02:00
|
|
|
|
|
|
|
$(".image-actions .open, .image-actions .download").attr("href", url);
|
|
|
|
};
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
exports.exit_lightbox_photo = function () {
|
2016-09-22 02:05:24 +02:00
|
|
|
$("#overlay").removeClass("show");
|
2016-10-17 22:02:25 +02:00
|
|
|
$(".player-container iframe").remove();
|
2016-10-25 23:26:28 +02:00
|
|
|
document.activeElement.blur();
|
2016-10-17 22:02:25 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.youtube_video = function (id) {
|
|
|
|
$("#overlay .image-preview, .image-description, .download").hide();
|
|
|
|
|
|
|
|
var iframe = document.createElement("iframe");
|
|
|
|
iframe.width = window.innerWidth;
|
|
|
|
iframe.height = window.innerWidth * 0.5625;
|
|
|
|
iframe.src = "https://www.youtube.com/embed/" + id;
|
|
|
|
iframe.setAttribute("frameborder", 0);
|
|
|
|
iframe.setAttribute("allowfullscreen", true);
|
|
|
|
|
|
|
|
$("#overlay .player-container").html("").show().append(iframe);
|
|
|
|
$(".image-actions .open").attr("href", "https://youtu.be/" + id);
|
2016-09-22 02:05:24 +02:00
|
|
|
};
|
2016-10-17 22:02:25 +02:00
|
|
|
// k3O01EfM5fU
|
2016-09-22 02:05:24 +02:00
|
|
|
|
2013-01-16 19:50:18 +01:00
|
|
|
var loading_more_messages_indicator_showing = false;
|
|
|
|
exports.show_loading_more_messages_indicator = function () {
|
|
|
|
if (! loading_more_messages_indicator_showing) {
|
2014-03-13 15:03:01 +01:00
|
|
|
loading.make_indicator($('#loading_more_messages_indicator'),
|
2013-07-12 18:31:26 +02:00
|
|
|
{abs_positioned: true});
|
2013-01-16 19:50:18 +01:00
|
|
|
loading_more_messages_indicator_showing = true;
|
2014-03-13 21:14:33 +01:00
|
|
|
floating_recipient_bar.hide();
|
2012-11-28 22:17:57 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-16 19:50:18 +01:00
|
|
|
exports.hide_loading_more_messages_indicator = function () {
|
|
|
|
if (loading_more_messages_indicator_showing) {
|
2014-03-13 15:03:01 +01:00
|
|
|
loading.destroy_indicator($("#loading_more_messages_indicator"));
|
2013-01-16 19:50:18 +01:00
|
|
|
loading_more_messages_indicator_showing = false;
|
2012-11-28 22:17:57 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-04 17:54:45 +01:00
|
|
|
/* EXPERIMENTS */
|
|
|
|
|
2013-12-05 20:23:02 +01:00
|
|
|
/* This method allows an advanced user to use the console
|
|
|
|
* to switch the application to span full width of the browser.
|
|
|
|
*/
|
|
|
|
exports.switchToFullWidth = function () {
|
|
|
|
$("#full-width-style").remove();
|
|
|
|
$('head').append('<style id="full-width-style" type="text/css">' +
|
|
|
|
'#home .alert-bar, .recipient-bar-content, #compose-container, .app-main, .header-main { max-width: none; }' +
|
|
|
|
'</style>');
|
|
|
|
return ("Switched to full width");
|
|
|
|
};
|
|
|
|
|
2013-12-04 17:54:45 +01:00
|
|
|
/* END OF EXPERIMENTS */
|
|
|
|
|
2012-10-03 21:44:07 +02:00
|
|
|
$(function () {
|
2012-11-30 18:50:16 +01:00
|
|
|
var throttled_mousewheelhandler = $.throttle(50, function (e, delta) {
|
2012-10-16 03:49:51 +02: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.
|
2013-08-02 17:41:00 +02:00
|
|
|
|
|
|
|
if (delta > 0) {
|
|
|
|
if (viewport.at_top()) {
|
|
|
|
navigate.up();
|
|
|
|
}
|
|
|
|
} else if (delta < 0) {
|
|
|
|
if (viewport.at_bottom()) {
|
|
|
|
navigate.down();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-25 13:02:38 +02:00
|
|
|
viewport.last_movement_direction = delta;
|
2012-10-16 03:49:51 +02:00
|
|
|
});
|
2012-11-30 18:28:23 +01:00
|
|
|
|
2014-01-29 19:04:51 +01:00
|
|
|
viewport.message_pane.mousewheel(function (e, delta) {
|
2012-11-30 18:28:23 +01:00
|
|
|
// Ignore mousewheel events if a modal is visible. It's weird if the
|
2017-01-14 03:21:53 +01:00
|
|
|
// user can scroll the main view by wheeling over the grayed-out area.
|
2012-11-30 18:28:23 +01:00
|
|
|
// Similarly, ignore events on settings page etc.
|
|
|
|
//
|
|
|
|
// We don't handle the compose box here, because it *should* work to
|
|
|
|
// select the compose box and then wheel over the message stream.
|
2012-12-03 20:55:17 +01:00
|
|
|
var obscured = exports.home_tab_obscured();
|
|
|
|
if (!obscured) {
|
2012-11-30 18:28:23 +01:00
|
|
|
throttled_mousewheelhandler(e, delta);
|
2012-12-03 20:55:17 +01:00
|
|
|
} else if (obscured === 'modal') {
|
|
|
|
// The modal itself has a handler invoked before this one (see below).
|
|
|
|
// preventDefault here so that the tab behind the modal doesn't scroll.
|
|
|
|
//
|
|
|
|
// This needs to include the events that would be ignored by throttling.
|
|
|
|
// That's why this code can't be moved into throttled_mousewheelhandler.
|
2012-11-30 18:28:23 +01:00
|
|
|
e.preventDefault();
|
|
|
|
}
|
2012-12-03 20:55:17 +01:00
|
|
|
// If on another tab, we neither handle the event nor preventDefault, allowing
|
|
|
|
// the tab to scroll normally.
|
2012-11-30 18:28:23 +01:00
|
|
|
});
|
2012-10-16 03:49:51 +02:00
|
|
|
|
2014-03-13 19:03:31 +01:00
|
|
|
$(window).resize($.throttle(50, resize.handler));
|
2012-10-11 22:34:57 +02:00
|
|
|
|
2013-02-12 16:31:55 +01:00
|
|
|
// Scrolling in modals, input boxes, and other elements that
|
|
|
|
// 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').mousewheel(function (e, delta) {
|
2012-11-30 18:28:23 +01:00
|
|
|
var self = $(this);
|
|
|
|
var scroll = self.scrollTop();
|
2013-11-13 23:27:08 +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;
|
|
|
|
|
2012-11-30 18:28:23 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
if ( ((delta > 0) && (scroll <= 0))
|
2013-11-13 23:27:08 +01:00
|
|
|
|| ((delta < 0) && (scroll >= max_scroll))) {
|
2012-11-30 18:28:23 +01:00
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-10-15 22:34:30 +02:00
|
|
|
// Override the #compose mousewheel prevention below just for the emoji box
|
|
|
|
$('.emoji_popover').mousewheel(function (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
2012-11-30 18:28:23 +01:00
|
|
|
// Ignore wheel events in the compose area which weren't already handled above.
|
|
|
|
$('#compose').mousewheel(function (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
2013-02-28 19:04:58 +01:00
|
|
|
// 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.
|
2014-03-13 19:03:31 +01:00
|
|
|
resize.handler();
|
2012-10-12 17:26:04 +02:00
|
|
|
|
2015-08-20 23:59:44 +02:00
|
|
|
if (!page_params.left_side_userlist) {
|
2013-10-09 00:37:07 +02:00
|
|
|
$("#navbar-buttons").addClass("right-userlist");
|
|
|
|
}
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#main_div").on("mouseover", ".message_row", function () {
|
2013-08-15 23:43:16 +02:00
|
|
|
var row = $(this).closest(".message_row");
|
2013-02-09 04:33:06 +01:00
|
|
|
message_hover(row);
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#main_div").on("mouseleave", ".message_row", function () {
|
2013-02-09 04:33:06 +01:00
|
|
|
message_unhover();
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#main_div").on("mouseover", ".message_sender", function () {
|
2012-11-14 23:33:13 +01:00
|
|
|
var row = $(this).closest(".message_row");
|
2013-07-11 23:06:58 +02:00
|
|
|
row.addClass("sender_name_hovered");
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#main_div").on("mouseout", ".message_sender", function () {
|
2012-11-14 23:33:13 +01:00
|
|
|
var row = $(this).closest(".message_row");
|
2013-07-11 23:06:58 +02:00
|
|
|
row.removeClass("sender_name_hovered");
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#subscriptions_table").on("mouseover", ".subscription_header", function () {
|
2013-01-04 21:05:18 +01:00
|
|
|
$(this).addClass("active");
|
|
|
|
});
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
$("#subscriptions_table").on("mouseout", ".subscription_header", function () {
|
2013-01-04 21:05:18 +01:00
|
|
|
$(this).removeClass("active");
|
|
|
|
});
|
2013-01-15 18:48:53 +01:00
|
|
|
|
2013-01-15 22:19:38 +01:00
|
|
|
$("#stream").on('blur', function () { compose.decorate_stream_bar(this.value); });
|
2013-01-22 01:34:44 +01:00
|
|
|
|
2013-02-12 20:52:33 +01:00
|
|
|
$(window).on('blur', function () {
|
|
|
|
$(document.body).addClass('window_blurred');
|
|
|
|
});
|
|
|
|
|
|
|
|
$(window).on('focus', function () {
|
|
|
|
$(document.body).removeClass('window_blurred');
|
|
|
|
});
|
2013-02-20 20:49:49 +01:00
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('message_selected.zulip', function (event) {
|
2013-02-20 20:49:49 +01:00
|
|
|
if (current_msg_list !== event.msg_list) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-30 00:10:10 +02:00
|
|
|
if (event.id === -1) {
|
|
|
|
// If the message list is empty, don't do anything
|
|
|
|
return;
|
|
|
|
}
|
2013-08-14 22:00:32 +02:00
|
|
|
var row = event.msg_list.get_row(event.id);
|
2013-02-20 20:49:49 +01:00
|
|
|
$('.selected_message').removeClass('selected_message');
|
2013-06-19 17:10:49 +02:00
|
|
|
row.addClass('selected_message');
|
2013-02-20 20:49:49 +01:00
|
|
|
|
|
|
|
if (event.then_scroll) {
|
2013-10-31 18:01:52 +01:00
|
|
|
if (row.length === 0) {
|
2014-03-10 23:07:17 +01:00
|
|
|
var row_from_dom = current_msg_list.get_row(event.id);
|
2013-10-31 18:01:52 +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: event.msg_list._items.indexOf(event.msg_list.get(event.id)),
|
|
|
|
render_start: event.msg_list.view._render_win_start,
|
2014-02-04 13:48:03 +01:00
|
|
|
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()
|
|
|
|
),
|
2017-01-12 00:17:43 +01:00
|
|
|
found_in_dom: row_from_dom.length,
|
2013-10-31 18:01:52 +01:00
|
|
|
});
|
|
|
|
}
|
2014-02-04 22:02:26 +01:00
|
|
|
if (event.target_scroll_offset !== undefined) {
|
|
|
|
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
|
2016-05-25 13:42:20 +02:00
|
|
|
viewport.recenter_view(row, {from_scroll: event.from_scroll,
|
2014-02-04 22:02:26 +01:00
|
|
|
force_center: event.previously_selected === -1});
|
|
|
|
}
|
2013-02-20 20:49:49 +01:00
|
|
|
}
|
|
|
|
});
|
2013-03-05 00:18:04 +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);
|
|
|
|
});
|
2013-03-07 20:12:27 +01:00
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
$('#streams_header h4').tooltip({ placement: 'right',
|
2013-07-12 17:00:32 +02:00
|
|
|
animation: false });
|
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
$('#streams_header i[data-toggle="tooltip"]').tooltip({ placement: 'left',
|
|
|
|
animation: false });
|
|
|
|
|
2016-11-22 07:26:38 +01:00
|
|
|
$('.message_failed i[data-toggle="tooltip"]').tooltip();
|
2016-06-13 22:06:12 +02:00
|
|
|
|
2016-06-21 21:34:41 +02:00
|
|
|
if (!page_params.realm_allow_message_editing) {
|
2013-11-30 22:03:12 +01:00
|
|
|
$("#edit-message-hotkey-help").hide();
|
|
|
|
}
|
|
|
|
|
2016-07-27 02:09:10 +02:00
|
|
|
if (page_params.presence_disabled) {
|
2013-11-28 22:13:01 +01:00
|
|
|
$("#user-list").hide();
|
|
|
|
$("#group-pm-list").hide();
|
|
|
|
}
|
|
|
|
|
2013-12-05 20:23:02 +01:00
|
|
|
if (feature_flags.full_width) {
|
|
|
|
exports.switchToFullWidth();
|
|
|
|
}
|
|
|
|
|
2013-05-03 22:12:58 +02:00
|
|
|
// initialize other stuff
|
2014-03-03 23:32:17 +01:00
|
|
|
reload.initialize();
|
2013-05-03 22:12:58 +02:00
|
|
|
composebox_typeahead.initialize();
|
|
|
|
search.initialize();
|
2015-10-26 17:44:15 +01:00
|
|
|
tutorial.initialize();
|
2013-05-03 22:12:58 +02:00
|
|
|
notifications.initialize();
|
2015-09-20 07:47:43 +02:00
|
|
|
gear_menu.initialize();
|
2013-05-03 22:12:58 +02:00
|
|
|
hashchange.initialize();
|
|
|
|
invite.initialize();
|
2016-11-05 18:52:07 +01:00
|
|
|
pointer.initialize();
|
2016-11-05 18:49:34 +01:00
|
|
|
unread.initialize();
|
2013-05-03 22:12:58 +02:00
|
|
|
activity.initialize();
|
2015-10-15 22:34:30 +02:00
|
|
|
emoji.initialize();
|
2012-10-03 21:44:07 +02:00
|
|
|
});
|
2012-11-05 23:51:27 +01:00
|
|
|
|
2013-08-02 23:41:40 +02:00
|
|
|
|
|
|
|
function scroll_finished() {
|
|
|
|
actively_scrolling = false;
|
|
|
|
|
|
|
|
if ($('#home').hasClass('active')) {
|
2016-04-12 17:38:47 +02:00
|
|
|
if (!pointer.suppress_scroll_pointer_update) {
|
2016-04-16 14:22:03 +02:00
|
|
|
pointer.keep_pointer_in_view();
|
2013-08-02 23:41:40 +02:00
|
|
|
} else {
|
2016-04-12 17:38:47 +02:00
|
|
|
pointer.suppress_scroll_pointer_update = false;
|
2013-08-02 23:41:40 +02:00
|
|
|
}
|
2014-03-13 21:14:33 +01:00
|
|
|
floating_recipient_bar.update();
|
2013-08-02 23:41:40 +02:00
|
|
|
if (viewport.scrollTop() === 0 &&
|
2016-04-10 14:15:47 +02:00
|
|
|
ui.have_scrolled_away_from_top) {
|
|
|
|
ui.have_scrolled_away_from_top = false;
|
2014-01-31 16:27:24 +01:00
|
|
|
message_store.load_more_messages(current_msg_list);
|
2016-04-10 14:15:47 +02:00
|
|
|
} else if (!ui.have_scrolled_away_from_top) {
|
|
|
|
ui.have_scrolled_away_from_top = true;
|
2013-08-02 23:41:40 +02:00
|
|
|
}
|
|
|
|
// When the window scrolls, it may cause some messages to
|
|
|
|
// enter the screen and become read. Calling
|
2014-01-31 17:44:21 +01:00
|
|
|
// unread.process_visible will update necessary
|
2013-08-02 23:41:40 +02:00
|
|
|
// data structures and DOM elements.
|
2014-01-31 17:44:21 +01:00
|
|
|
setTimeout(unread.process_visible, 0);
|
2013-08-02 23:41:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var scroll_timer;
|
|
|
|
function scroll_finish() {
|
|
|
|
actively_scrolling = true;
|
|
|
|
clearTimeout(scroll_timer);
|
|
|
|
scroll_timer = setTimeout(scroll_finished, 100);
|
|
|
|
}
|
|
|
|
|
2013-02-18 08:16:57 +01:00
|
|
|
// Save the compose content cursor position and restore when we
|
|
|
|
// shift-tab back in (see hotkey.js).
|
|
|
|
var saved_compose_cursor = 0;
|
|
|
|
|
|
|
|
$(function () {
|
2016-12-02 14:06:06 +01:00
|
|
|
viewport.message_pane.scroll($.throttle(50, function () {
|
2014-01-31 17:44:21 +01:00
|
|
|
unread.process_visible();
|
2014-01-31 00:00:33 +01:00
|
|
|
scroll_finish();
|
|
|
|
}));
|
|
|
|
|
2013-02-18 08:16:57 +01:00
|
|
|
$('#new_message_content').blur(function () {
|
2016-06-14 22:49:41 +02:00
|
|
|
saved_compose_cursor = $(this).caret();
|
2013-02-18 08:16:57 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
exports.restore_compose_cursor = function () {
|
|
|
|
$('#new_message_content')
|
|
|
|
.focus()
|
2016-06-14 22:49:41 +02:00
|
|
|
.caret(saved_compose_cursor);
|
2013-02-18 08:16:57 +01:00
|
|
|
};
|
2013-07-24 19:43:45 +02:00
|
|
|
|
2013-08-09 21:46:34 +02:00
|
|
|
$(function () {
|
|
|
|
if (window.bridge !== undefined) {
|
2013-08-27 22:07:59 +02:00
|
|
|
// Disable "spellchecking" in our desktop app. The "spellchecking"
|
|
|
|
// in our Mac app is actually autocorrect, and frustrates our
|
|
|
|
// users.
|
2013-08-09 21:46:34 +02:00
|
|
|
$("#new_message_content").attr('spellcheck', 'false');
|
2013-08-27 22:07:59 +02:00
|
|
|
// Modify the zephyr mirroring error message in our desktop
|
|
|
|
// app, since it doesn't work from the desktop version.
|
|
|
|
$("#webathena_login_menu").hide();
|
|
|
|
$("#normal-zephyr-mirror-error-text").addClass("notdisplayed");
|
|
|
|
$("#desktop-zephyr-mirror-error-text").removeClass("notdisplayed");
|
2013-08-09 21:46:34 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
return exports;
|
|
|
|
}());
|
2016-12-29 12:23:07 +01:00
|
|
|
|
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = ui;
|
|
|
|
}
|