2012-11-16 16:45:39 +01:00
|
|
|
var ui = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2012-12-03 20:54:29 +01:00
|
|
|
// What, if anything, obscures the home tab?
|
|
|
|
exports.home_tab_obscured = function () {
|
|
|
|
if ($('.modal:visible').length > 0)
|
|
|
|
return 'modal';
|
|
|
|
if (! $('#home').hasClass('active'))
|
|
|
|
return 'other_tab';
|
|
|
|
return false;
|
2012-11-30 18:28:23 +01:00
|
|
|
};
|
|
|
|
|
2012-10-05 20:25:27 +02:00
|
|
|
// We want to remember how far we were scrolled on each 'tab'.
|
|
|
|
// To do so, we need to save away the old position of the
|
|
|
|
// scrollbar when we switch to a new tab (and restore it
|
|
|
|
// when we switch back.)
|
|
|
|
var scroll_positions = {};
|
2012-11-06 23:06:47 +01:00
|
|
|
var gravatar_stamp = 1;
|
2012-10-05 20:25:27 +02: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
|
|
|
|
2013-03-06 20:08:50 +01:00
|
|
|
function find_boundary_tr(initial_tr, iterate_row) {
|
|
|
|
var j, skip_same_td_check = false;
|
|
|
|
var tr = initial_tr;
|
|
|
|
|
|
|
|
// If the selection boundary is somewhere that does not have a
|
|
|
|
// parent tr, we should let the browser handle the copy-paste
|
|
|
|
// entirely on its own
|
|
|
|
if (tr.length === 0) {
|
2013-03-13 23:07:41 +01:00
|
|
|
return undefined;
|
2013-03-06 20:08:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the selection bounary is on a table row that does not have an
|
|
|
|
// associated message id (because the user clicked between messages),
|
|
|
|
// then scan downwards until we hit a table row with a message id.
|
|
|
|
// To ensure we can't enter an infinite loop, bail out (and let the
|
|
|
|
// browser handle the copy-paste on its own) if we don't hit what we
|
|
|
|
// are looking for within 10 rows.
|
|
|
|
for (j = 0; (!tr.is('.message_row')) && j < 10; j++) {
|
|
|
|
tr = iterate_row(tr);
|
|
|
|
}
|
|
|
|
if (j === 10) {
|
2013-03-13 23:07:41 +01:00
|
|
|
return undefined;
|
2013-03-06 20:08:50 +01:00
|
|
|
} else if (j !== 0) {
|
|
|
|
// If we updated tr, then we are not dealing with a selection
|
|
|
|
// that is entirely within one td, and we can skip the same td
|
|
|
|
// check (In fact, we need to because it won't work correctly
|
|
|
|
// in this case)
|
|
|
|
skip_same_td_check = true;
|
|
|
|
}
|
|
|
|
return [rows.id(tr), skip_same_td_check];
|
|
|
|
}
|
|
|
|
|
2013-01-09 23:42:43 +01:00
|
|
|
$(document).bind('copy', function (e) {
|
|
|
|
var selection = window.getSelection();
|
2013-03-06 20:08:50 +01:00
|
|
|
var i, range, ranges = [], startc, endc, initial_end_tr, start_id, end_id, row, message;
|
|
|
|
var start_data, end_data;
|
2013-01-09 23:42:43 +01:00
|
|
|
var skip_same_td_check = false;
|
|
|
|
var div = $('<div>'), p = $('<p>'), content;
|
|
|
|
for (i = 0; i < selection.rangeCount; i++) {
|
|
|
|
range = selection.getRangeAt(i);
|
|
|
|
ranges.push(range);
|
|
|
|
|
|
|
|
startc = $(range.startContainer);
|
2013-03-06 20:08:50 +01:00
|
|
|
start_data = find_boundary_tr($(startc.parents('tr')[0]), function(row) {
|
|
|
|
return row.next();
|
|
|
|
});
|
2013-03-13 23:07:41 +01:00
|
|
|
if (start_data === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-06 20:08:50 +01:00
|
|
|
start_id = start_data[0];
|
2013-01-09 23:42:43 +01:00
|
|
|
|
|
|
|
endc = $(range.endContainer);
|
|
|
|
// If the selection ends in the bottom whitespace, we should act as
|
|
|
|
// though the selection ends on the final message
|
|
|
|
if (endc.attr('id') === "bottom_whitespace") {
|
2013-03-06 20:08:50 +01:00
|
|
|
initial_end_tr = $("tr.message_row:last");
|
2013-01-09 23:42:43 +01:00
|
|
|
skip_same_td_check = true;
|
|
|
|
} else {
|
2013-03-06 20:08:50 +01:00
|
|
|
initial_end_tr = $(endc.parents('tr')[0]);
|
2013-01-09 23:42:43 +01:00
|
|
|
}
|
2013-03-06 20:08:50 +01:00
|
|
|
end_data = find_boundary_tr(initial_end_tr, function(row) {
|
|
|
|
return row.prev();
|
|
|
|
});
|
2013-03-13 23:07:41 +01:00
|
|
|
if (end_data === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2013-03-06 20:08:50 +01:00
|
|
|
end_id = end_data[0];
|
2013-01-09 23:42:43 +01:00
|
|
|
|
2013-03-06 20:08:50 +01:00
|
|
|
if (start_data[1] || end_data[1]) {
|
2013-01-09 23:42:43 +01:00
|
|
|
skip_same_td_check = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the selection starts and ends in the same td,
|
|
|
|
// we should let the browser handle the copy-paste entirely on its own
|
|
|
|
// (In this case, there is no need for our special copy code)
|
|
|
|
if (!skip_same_td_check &&
|
|
|
|
startc.parents('td')[0] === endc.parents('td')[0]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct a div for what we want to copy (div)
|
2013-03-06 20:08:50 +01:00
|
|
|
row = rows.get(start_id, current_msg_list.table_name);
|
|
|
|
for (0 /* for linter */; rows.id(row) <= end_id; row = rows.next_visible(row)) {
|
2013-01-09 23:42:43 +01:00
|
|
|
if (row.prev().hasClass("recipient_row")) {
|
|
|
|
div.append(p);
|
|
|
|
p = $('<p>');
|
|
|
|
content = $('<div>').text(row.prev().children(".right_part").text()
|
|
|
|
.replace(/\s+/g, " ")
|
|
|
|
.replace(/^\s/, "").replace(/\s$/, ""));
|
|
|
|
p.html(p.html() + "<b>" + content.text() + "</b>" + "<br>");
|
|
|
|
}
|
|
|
|
|
2013-02-12 20:01:24 +01:00
|
|
|
message = current_msg_list.get(rows.id(row));
|
2013-01-09 23:42:43 +01:00
|
|
|
|
|
|
|
content = $('<div>').text(message.sender_full_name + ": " +
|
|
|
|
$('<div/>').html(message.content).text()
|
|
|
|
.replace("\n", "<br>"));
|
|
|
|
p.html(p.html() + content.text());
|
|
|
|
p.html(p.html() + "<br>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
div.append(p);
|
|
|
|
|
|
|
|
// Select div so that the browser will copy it
|
|
|
|
// instead of copying the original selection
|
|
|
|
div.css({position: 'absolute', 'left': '-99999px'})
|
|
|
|
.attr('id', 'copytempdiv');
|
|
|
|
$('body').append(div);
|
|
|
|
selection.selectAllChildren(div[0]);
|
|
|
|
|
|
|
|
// After the copy has happened, delete the div and
|
|
|
|
// change the selection back to the original selection
|
|
|
|
window.setTimeout(function() {
|
|
|
|
selection = window.getSelection();
|
|
|
|
selection.removeAllRanges();
|
|
|
|
$.each(ranges, function (index, range) {
|
|
|
|
selection.addRange(range);
|
|
|
|
});
|
|
|
|
$('body').remove('#copytempdiv');
|
|
|
|
},0);
|
|
|
|
});
|
|
|
|
|
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() {
|
|
|
|
if (current_message_hover === undefined)
|
|
|
|
return;
|
|
|
|
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) {
|
|
|
|
message_unhover();
|
2012-11-14 23:33:13 +01:00
|
|
|
while (!message_row.hasClass('include-sender')) {
|
|
|
|
message_row = message_row.prev();
|
2012-11-05 23:30:20 +01:00
|
|
|
}
|
2013-02-09 04:33:06 +01:00
|
|
|
message_row.addClass('message_hovered');
|
|
|
|
current_message_hover = message_row;
|
2012-10-03 21:44:07 +02:00
|
|
|
}
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
exports.report_message = function (response, status_box, cls) {
|
2012-10-31 23:38:34 +01:00
|
|
|
if (cls === undefined)
|
|
|
|
cls = 'alert';
|
|
|
|
|
|
|
|
status_box.removeClass(status_classes).addClass(cls)
|
|
|
|
.text(response).stop(true).fadeTo(0, 1);
|
|
|
|
status_box.show();
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-31 23:38:34 +01:00
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
exports.report_error = function (response, xhr, status_box) {
|
2012-10-03 21:44:07 +02:00
|
|
|
if (xhr.status.toString().charAt(0) === "4") {
|
|
|
|
// Only display the error response for 4XX, where we've crafted
|
|
|
|
// a nice response.
|
|
|
|
response += ": " + $.parseJSON(xhr.responseText).msg;
|
|
|
|
}
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
ui.report_message(response, status_box, 'alert-error');
|
|
|
|
};
|
2012-10-03 21:44:07 +02:00
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
exports.report_success = function (response, status_box) {
|
|
|
|
ui.report_message(response, status_box, 'alert-success');
|
|
|
|
};
|
2012-10-17 20:43:20 +02:00
|
|
|
|
2012-10-03 21:44:07 +02:00
|
|
|
var clicking = false;
|
|
|
|
var mouse_moved = false;
|
|
|
|
|
2012-10-10 16:21:40 +02:00
|
|
|
function mousedown() {
|
2012-10-03 21:44:07 +02:00
|
|
|
mouse_moved = false;
|
|
|
|
clicking = true;
|
|
|
|
}
|
|
|
|
|
2012-10-10 16:21:40 +02:00
|
|
|
function mousemove() {
|
2012-10-03 21:44:07 +02:00
|
|
|
if (clicking) {
|
|
|
|
mouse_moved = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fix "resizing window breaks in Chrome" issue.
Ironically, I think this might've bee introduced by
commit ca35321c02d5e79e4f9c439a662805c016a333ed,
'Fix "resizing window breaks in Firefox" issue'.
Basically, when the window is 776px wide according to
window.innerWidth, that's the width not including the
scrollbar. However, in Chrome, the media query seems to ignore the
width of the scrollbar, so from the CSS's perspective, the window is
actually ~766px wide, so it goes into condensed mode.
But the rest of our code doesn't, which causes the break.
A bit more on this browser-specific difference at:
http://www.456bereastreet.com/archive/201101/media_queries_viewport_width_scrollbars_and_webkit_browsers/
So the issue we have is, to match the CSS's behavior:
* In Firefox, we should be listening to window.innerWidth
* In Chrome, we should be listening to window.width
We fix this hopefully once and for all by using window.matchMedia --
aka the exact same query that the CSS itself uses. As discussed in my
last commit, this feature is unavailable in IE<10, so we provide a
potentially more fragile fallback, i.e. what we did before this
commit.
(imported from commit d8e6425b81c90c8e0fdda28e7273988c9bfd67ec)
2012-12-04 20:17:56 +01:00
|
|
|
function need_skinny_mode() {
|
|
|
|
if (window.matchMedia !== undefined) {
|
|
|
|
return window.matchMedia("(max-width: 767px)").matches;
|
|
|
|
} else {
|
|
|
|
// IE<10 doesn't support window.matchMedia, so do this
|
|
|
|
// as best we can without it.
|
|
|
|
return window.innerWidth <= 767;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-11 22:34:57 +02:00
|
|
|
function resizehandler(e) {
|
|
|
|
var composebox = $("#compose");
|
2013-02-28 19:04:58 +01:00
|
|
|
var floating_recipient_bar = $("#floating_recipient_bar");
|
|
|
|
var desired_width;
|
|
|
|
if (exports.home_tab_obscured() === 'other_tab') {
|
|
|
|
desired_width = $("div.tab-pane.active").outerWidth();
|
2012-10-11 22:34:57 +02:00
|
|
|
} else {
|
2013-02-28 19:04:58 +01:00
|
|
|
desired_width = $("#main_div").outerWidth();
|
2012-10-11 22:34:57 +02:00
|
|
|
}
|
2013-02-28 19:04:58 +01:00
|
|
|
composebox.width(desired_width);
|
|
|
|
floating_recipient_bar.width(desired_width);
|
2012-10-12 06:12:46 +02:00
|
|
|
|
2013-01-09 21:29:44 +01:00
|
|
|
$("#bottom_whitespace").height(viewport.height() * 0.4);
|
2013-01-24 23:36:29 +01:00
|
|
|
$("#main_div").css('min-height', viewport.height() - $("#top_navbar").height());
|
2012-12-19 21:00:51 +01:00
|
|
|
|
2013-02-06 19:48:26 +01:00
|
|
|
/* total viewport - height of navbar - height of upper sidebar - padding*/
|
2013-02-12 16:18:22 +01:00
|
|
|
var bottom_sidebar_height = viewport.height() - $("#top_navbar").height() - $(".upper_sidebar").height() - 40;
|
|
|
|
$(".bottom_sidebar").height(bottom_sidebar_height);
|
2013-03-12 04:10:02 +01:00
|
|
|
/* viewport - navbar - notifications area - margin on the right-sidebar - padding on the notifications-bar */
|
|
|
|
var right_sidebar_height = viewport.height() - $("#top_navbar").height() - $("#notifications-area").height() - 14 - 10;
|
|
|
|
$("#right-sidebar").height(right_sidebar_height);
|
2013-02-12 16:18:22 +01:00
|
|
|
|
2013-02-21 03:49:18 +01:00
|
|
|
$("#stream_filters").css('max-height', bottom_sidebar_height * 0.75);
|
|
|
|
$("#user_presences").css('max-height', right_sidebar_height * 0.90);
|
2013-02-06 19:48:26 +01:00
|
|
|
|
2012-10-12 06:12:46 +02:00
|
|
|
// This function might run onReady (if we're in a narrow window),
|
|
|
|
// but before we've loaded in the messages; in that case, don't
|
|
|
|
// try to scroll to one.
|
2013-02-20 18:26:50 +01:00
|
|
|
if (current_msg_list.selected_id() !== -1) {
|
2012-10-12 06:12:46 +02:00
|
|
|
scroll_to_selected();
|
|
|
|
}
|
2013-01-30 21:49:56 +01:00
|
|
|
// When the screen resizes, it may cause some messages to go off the screen
|
|
|
|
notifications_bar.update();
|
2012-10-11 22:34:57 +02:00
|
|
|
}
|
|
|
|
|
2013-02-06 19:48:26 +01:00
|
|
|
$(function () {
|
|
|
|
// When the user's profile picture loads this can change the height of the sidebar
|
|
|
|
$("img.gravatar-profile").bind('load', resizehandler);
|
2013-02-08 17:58:08 +01:00
|
|
|
|
|
|
|
// We don't have a stream list at MIT.
|
2013-03-25 23:26:14 +01:00
|
|
|
if (page_params.domain === "mit.edu") {
|
2013-02-08 17:58:08 +01:00
|
|
|
$("#stream_filters").remove();
|
|
|
|
$("#stream_filters_sep").remove();
|
|
|
|
}
|
2013-02-06 19:48:26 +01:00
|
|
|
});
|
|
|
|
|
2012-10-16 03:33:43 +02:00
|
|
|
var is_floating_recipient_bar_showing = false;
|
2013-02-18 20:37:17 +01:00
|
|
|
|
|
|
|
function show_floating_recipient_bar() {
|
|
|
|
if (!is_floating_recipient_bar_showing) {
|
2013-02-28 19:04:58 +01:00
|
|
|
$("#floating_recipient_bar").css('visibility', 'visible');
|
2013-02-18 20:37:17 +01:00
|
|
|
is_floating_recipient_bar_showing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var old_label;
|
2013-02-13 21:30:17 +01:00
|
|
|
var disable_floating_recipient_bar = false;
|
2012-10-16 03:33:43 +02:00
|
|
|
function replace_floating_recipient_bar(desired_label) {
|
2012-11-21 20:41:57 +01:00
|
|
|
var new_label, other_label, header;
|
2012-10-16 01:29:03 +02:00
|
|
|
if (desired_label !== old_label) {
|
2012-10-17 22:03:00 +02:00
|
|
|
if (desired_label.children(".message_header_stream").length !== 0) {
|
2012-11-21 20:41:57 +01:00
|
|
|
new_label = $("#current_label_stream");
|
2012-12-03 19:49:12 +01:00
|
|
|
other_label = $("#current_label_private_message");
|
2012-11-21 20:41:57 +01:00
|
|
|
header = desired_label.children(".message_header_stream.right_part");
|
2012-12-01 04:37:52 +01:00
|
|
|
|
|
|
|
$("#current_label_stream td:first").css(
|
|
|
|
"background-color",
|
|
|
|
desired_label.children(".message_header_stream.right_part")
|
|
|
|
.css("background-color"));
|
2012-10-16 01:29:03 +02:00
|
|
|
} else {
|
2012-12-03 19:49:12 +01:00
|
|
|
new_label = $("#current_label_private_message");
|
2012-11-21 20:41:57 +01:00
|
|
|
other_label = $("#current_label_stream");
|
2012-12-03 19:49:12 +01:00
|
|
|
header = desired_label.children(".message_header_private_message.right_part");
|
2012-10-16 01:29:03 +02:00
|
|
|
}
|
2012-11-21 20:41:57 +01:00
|
|
|
new_label.find("td:last").replaceWith(header.clone());
|
|
|
|
other_label.css('display', 'none');
|
|
|
|
new_label.css('display', 'table-row');
|
2013-02-09 19:52:16 +01:00
|
|
|
new_label.attr("zid", rows.id(desired_label));
|
2012-11-21 20:41:57 +01:00
|
|
|
|
2012-10-16 01:29:03 +02:00
|
|
|
old_label = desired_label;
|
|
|
|
}
|
2013-02-18 20:37:17 +01:00
|
|
|
show_floating_recipient_bar();
|
2012-10-14 04:13:27 +02:00
|
|
|
}
|
|
|
|
|
2012-10-16 03:33:43 +02:00
|
|
|
function hide_floating_recipient_bar() {
|
|
|
|
if (is_floating_recipient_bar_showing) {
|
2013-02-28 19:04:58 +01:00
|
|
|
$("#floating_recipient_bar").css('visibility', 'hidden');
|
2012-10-16 03:33:43 +02:00
|
|
|
is_floating_recipient_bar_showing = false;
|
2012-10-16 01:29:03 +02:00
|
|
|
}
|
2012-10-14 04:13:27 +02:00
|
|
|
}
|
|
|
|
|
2013-02-13 21:30:17 +01:00
|
|
|
exports.disable_floating_recipient_bar = function () {
|
|
|
|
disable_floating_recipient_bar = true;
|
|
|
|
hide_floating_recipient_bar();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.enable_floating_recipient_bar = function () {
|
|
|
|
disable_floating_recipient_bar = false;
|
|
|
|
};
|
|
|
|
|
2012-12-07 20:52:39 +01:00
|
|
|
exports.update_floating_recipient_bar = function () {
|
2013-02-13 21:30:17 +01:00
|
|
|
if (disable_floating_recipient_bar) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-28 19:04:58 +01:00
|
|
|
var floating_recipient_bar = $("#floating_recipient_bar");
|
|
|
|
var floating_recipient_bar_top = floating_recipient_bar.offset().top;
|
|
|
|
var floating_recipient_bar_bottom = floating_recipient_bar_top + floating_recipient_bar.outerHeight();
|
2012-10-14 04:13:27 +02:00
|
|
|
|
2012-10-15 18:21:05 +02:00
|
|
|
// Find the last message where the top of the recipient
|
2012-10-16 00:14:04 +02:00
|
|
|
// row is at least partially occluded by our box.
|
|
|
|
// Start with the pointer's current location.
|
2013-02-14 23:48:37 +01:00
|
|
|
var candidate = current_msg_list.selected_row();
|
2012-10-26 21:02:04 +02:00
|
|
|
if (candidate === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2012-10-16 00:14:04 +02:00
|
|
|
while (true) {
|
|
|
|
candidate = candidate.prev();
|
|
|
|
if (candidate.length === 0) {
|
|
|
|
// We're at the top of the page and no labels are above us.
|
2012-10-16 03:33:43 +02:00
|
|
|
hide_floating_recipient_bar();
|
2012-10-16 00:14:04 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (candidate.is(".focused_table .recipient_row")) {
|
2013-02-28 19:04:58 +01:00
|
|
|
if (candidate.offset().top < floating_recipient_bar_bottom) {
|
2012-10-16 00:14:04 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-10-15 18:21:05 +02:00
|
|
|
}
|
2012-10-16 00:14:04 +02:00
|
|
|
var current_label = candidate;
|
2012-10-15 18:21:05 +02:00
|
|
|
|
|
|
|
// We now know what the floating stream/subject bar should say.
|
|
|
|
// Do we show it?
|
2012-10-14 04:13:27 +02:00
|
|
|
|
2012-10-15 18:21:05 +02:00
|
|
|
// Hide if the bottom of our floating stream/subject label is not
|
|
|
|
// lower than the bottom of current_label (since that means we're
|
|
|
|
// covering up a label that already exists).
|
2013-02-28 19:04:58 +01:00
|
|
|
if (floating_recipient_bar_bottom <=
|
2012-10-16 04:45:10 +02:00
|
|
|
(current_label.offset().top + current_label.outerHeight())) {
|
2012-10-16 03:33:43 +02:00
|
|
|
hide_floating_recipient_bar();
|
2012-10-14 04:13:27 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-10-15 18:21:05 +02:00
|
|
|
|
|
|
|
// Hide if our bottom is in our bookend (or one bookend-height
|
|
|
|
// above it). This means we're not showing any useful part of the
|
2012-11-02 22:04:56 +01:00
|
|
|
// message above us, so why bother showing the label?
|
2012-10-27 03:38:31 +02:00
|
|
|
var current_bookend = current_label.nextUntil(".bookend_tr")
|
|
|
|
.andSelf()
|
|
|
|
.next(".bookend_tr:first");
|
2012-10-15 18:21:05 +02:00
|
|
|
// (The last message currently doesn't have a bookend, which is why this might be 0).
|
2012-10-27 03:38:31 +02:00
|
|
|
if (current_bookend.length > 0) {
|
2013-02-28 19:04:58 +01:00
|
|
|
if (floating_recipient_bar_bottom >
|
2012-10-27 03:38:31 +02:00
|
|
|
(current_bookend.offset().top - current_bookend.outerHeight())) {
|
2012-10-16 03:33:43 +02:00
|
|
|
hide_floating_recipient_bar();
|
2012-10-15 18:21:05 +02:00
|
|
|
return;
|
2012-10-14 04:13:27 +02:00
|
|
|
}
|
|
|
|
}
|
2012-10-15 18:21:05 +02:00
|
|
|
|
2012-10-16 03:33:43 +02:00
|
|
|
replace_floating_recipient_bar(current_label);
|
2012-12-07 20:52:39 +01:00
|
|
|
};
|
|
|
|
|
2012-10-15 21:57:30 +02:00
|
|
|
function hack_for_floating_recipient_bar() {
|
|
|
|
// So, as of this writing, Firefox respects visibility: collapse,
|
|
|
|
// but WebKit does not (at least, my Chrome doesn't.) Instead it
|
|
|
|
// renders it basically as visibility: hidden, which leaves a
|
|
|
|
// slight gap that our messages peek through as they scroll
|
|
|
|
// by. This hack fixes this by programmatically measuring how big
|
|
|
|
// the gap is, and then moving our table up to compensate.
|
|
|
|
var gap = $("#floating_recipient_layout_row").outerHeight(true);
|
|
|
|
var floating_recipient = $(".floating_recipient");
|
|
|
|
var offset = floating_recipient.offset();
|
|
|
|
offset.top = offset.top - gap;
|
|
|
|
floating_recipient.offset(offset);
|
|
|
|
}
|
2012-10-14 04:13:27 +02:00
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
var current_actions_popover_elem;
|
|
|
|
function show_actions_popover(element, id) {
|
2013-02-09 20:20:35 +01:00
|
|
|
var last_popover_elem = current_actions_popover_elem;
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
if (last_popover_elem !== undefined
|
|
|
|
&& last_popover_elem.get()[0] === element) {
|
|
|
|
// We want it to be the case that a user can dismiss a popover
|
|
|
|
// by clicking on the same element that caused the popover.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-20 18:33:04 +01:00
|
|
|
current_msg_list.select_id(id);
|
2012-10-18 20:23:57 +02:00
|
|
|
var elt = $(element);
|
|
|
|
if (elt.data('popover') === undefined) {
|
2013-03-05 00:18:04 +01:00
|
|
|
timerender.set_full_datetime(current_msg_list.get(id),
|
|
|
|
elt.closest(".message_row").find(".message_time"));
|
|
|
|
|
2013-02-09 03:29:40 +01:00
|
|
|
var args = {
|
2013-02-12 20:01:24 +01:00
|
|
|
message: current_msg_list.get(id),
|
2013-02-09 03:29:40 +01:00
|
|
|
narrowed: narrow.active()
|
|
|
|
};
|
2013-02-11 23:15:39 +01:00
|
|
|
|
|
|
|
var ypos = elt.offset().top - viewport.scrollTop();
|
2013-02-09 03:29:40 +01:00
|
|
|
elt.popover({
|
2013-02-11 23:15:39 +01:00
|
|
|
placement: (ypos > (viewport.height() - 300)) ? 'top' : 'bottom',
|
2013-02-16 10:45:32 +01:00
|
|
|
title: templates.render('actions_popover_title', args),
|
|
|
|
content: templates.render('actions_popover_content', args),
|
2013-02-09 03:29:40 +01:00
|
|
|
trigger: "manual"
|
|
|
|
});
|
2012-10-18 20:23:57 +02:00
|
|
|
elt.popover("show");
|
2013-02-08 23:03:33 +01:00
|
|
|
current_actions_popover_elem = elt;
|
2012-11-16 02:30:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-27 18:30:02 +01:00
|
|
|
function change_message_star(message, starred) {
|
|
|
|
$.ajax({
|
|
|
|
type: 'POST',
|
|
|
|
url: '/json/update_message_flags',
|
|
|
|
data: {messages: JSON.stringify([message.id]),
|
|
|
|
op: starred ? 'add' : 'remove',
|
|
|
|
flag: 'starred'},
|
|
|
|
dataType: 'json'});
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggle_star(row_id) {
|
|
|
|
// Update the message object pointed to by the various message
|
|
|
|
// lists.
|
|
|
|
var message = current_msg_list.get(row_id);
|
|
|
|
if (message.starred === true) {
|
|
|
|
message.starred = false;
|
|
|
|
} else {
|
|
|
|
message.starred = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Avoid a full re-render, but update the star in each message
|
|
|
|
// table in which it is visible.
|
|
|
|
$.each([all_msg_list, home_msg_list, narrowed_msg_list], function () {
|
|
|
|
if (this === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var row = rows.get(row_id, this.table_name);
|
|
|
|
if (row === undefined) {
|
|
|
|
// The row may not exist, e.g. if you star a message in the all
|
|
|
|
// messages table from a stream that isn't in your home view.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var favorite_image = row.find("i");
|
|
|
|
favorite_image.toggleClass("icon-star-empty");
|
|
|
|
favorite_image.toggleClass("icon-star");
|
2013-04-14 05:04:47 +02:00
|
|
|
var title_state = message.starred ? "Unstar" : "Star";
|
|
|
|
favorite_image.attr("title", title_state + " this message");
|
2013-03-27 18:30:02 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
// Save the star change.
|
|
|
|
change_message_star(message, message.starred);
|
|
|
|
}
|
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
exports.hide_actions_popover = function () {
|
|
|
|
if (ui.actions_currently_popped()) {
|
|
|
|
current_actions_popover_elem.popover("destroy");
|
|
|
|
current_actions_popover_elem = undefined;
|
2012-10-18 20:23:57 +02:00
|
|
|
}
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-10-18 20:23:57 +02:00
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
exports.actions_currently_popped = function () {
|
|
|
|
return current_actions_popover_elem !== undefined;
|
2012-11-16 16:45:39 +01:00
|
|
|
};
|
2012-11-16 02:30:44 +01:00
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
function update_gravatars() {
|
2012-11-30 18:50:16 +01:00
|
|
|
$.each($(".gravatar-profile"), function (index, profile) {
|
2012-11-16 16:45:39 +01:00
|
|
|
$(this).attr('src', $(this).attr('src') + '?stamp=' + gravatar_stamp);
|
|
|
|
});
|
|
|
|
gravatar_stamp += 1;
|
2012-10-31 20:01:21 +01:00
|
|
|
}
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
function poll_for_gravatar_update(start_time, url) {
|
|
|
|
var updated = false;
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
type: "HEAD",
|
|
|
|
url: url,
|
|
|
|
async: false,
|
|
|
|
cache: false,
|
|
|
|
success: function (resp, statusText, xhr) {
|
|
|
|
if (new Date(xhr.getResponseHeader('Last-Modified')) > start_time) {
|
|
|
|
update_gravatars();
|
|
|
|
updated = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Give users 5 minutes to update their picture on gravatar.com,
|
|
|
|
// during which we try to auto-update their image on our site. If
|
|
|
|
// they take longer than that, we'll update when they press the
|
|
|
|
// save button.
|
|
|
|
if (!updated && (($.now() - start_time) < 1000 * 60 * 5)) {
|
2012-11-30 18:50:16 +01:00
|
|
|
setTimeout(function () {
|
2012-11-16 16:45:39 +01:00
|
|
|
poll_for_gravatar_update(start_time, url);
|
|
|
|
}, 1500);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.get_gravatar_stamp = function () {
|
|
|
|
return gravatar_stamp;
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.wait_for_gravatar = function () {
|
|
|
|
poll_for_gravatar_update($.now(), $(".gravatar-profile").attr("src"));
|
|
|
|
};
|
|
|
|
|
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) {
|
|
|
|
util.make_loading_indicator($('#loading_more_messages_indicator'));
|
|
|
|
loading_more_messages_indicator_showing = true;
|
2012-11-28 22:17:57 +01:00
|
|
|
hide_floating_recipient_bar();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-16 19:50:18 +01:00
|
|
|
exports.hide_loading_more_messages_indicator = function () {
|
|
|
|
if (loading_more_messages_indicator_showing) {
|
|
|
|
util.destroy_loading_indicator($("#loading_more_messages_indicator"));
|
|
|
|
loading_more_messages_indicator_showing = false;
|
2012-11-28 22:17:57 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-10-03 21:44:07 +02:00
|
|
|
$(function () {
|
|
|
|
// NB: This just binds to current elements, and won't bind to elements
|
|
|
|
// created after ready() is called.
|
2013-02-01 17:04:23 +01:00
|
|
|
$('#send-status .send-status-close').click(
|
|
|
|
function () { $('#send-status').stop(true).fadeOut(500); }
|
|
|
|
);
|
2012-10-03 21:44:07 +02:00
|
|
|
|
2013-03-15 15:51:25 +01:00
|
|
|
var scroll_start_message;
|
2013-03-15 15:23:28 +01:00
|
|
|
|
2013-02-18 20:37:17 +01:00
|
|
|
function scroll_finished() {
|
2012-10-05 19:22:43 +02:00
|
|
|
if ($('#home').hasClass('active')) {
|
2013-02-12 22:32:14 +01:00
|
|
|
if (!suppress_scroll_pointer_update) {
|
|
|
|
keep_pointer_in_view();
|
|
|
|
} else {
|
|
|
|
suppress_scroll_pointer_update = false;
|
|
|
|
}
|
2013-02-19 23:23:49 +01:00
|
|
|
exports.update_floating_recipient_bar();
|
2012-11-27 23:17:30 +01:00
|
|
|
if (viewport.scrollTop() === 0 &&
|
|
|
|
have_scrolled_away_from_top) {
|
|
|
|
have_scrolled_away_from_top = false;
|
2013-02-12 20:01:24 +01:00
|
|
|
load_more_messages(current_msg_list);
|
2012-11-27 23:17:30 +01:00
|
|
|
} else if (!have_scrolled_away_from_top) {
|
|
|
|
have_scrolled_away_from_top = true;
|
|
|
|
}
|
2013-03-19 21:53:49 +01:00
|
|
|
// When the window scrolls, it may cause some messages to
|
|
|
|
// enter the screen and become read
|
2013-01-30 21:49:56 +01:00
|
|
|
notifications_bar.update();
|
2013-03-19 21:53:49 +01:00
|
|
|
notifications.update_title_count();
|
2013-03-04 23:44:07 +01:00
|
|
|
|
2013-03-15 15:23:28 +01:00
|
|
|
var new_selected = current_msg_list.selected_id();
|
2013-03-15 15:51:25 +01:00
|
|
|
if (scroll_start_message === undefined) {
|
2013-03-28 22:33:45 +01:00
|
|
|
// blueslip.error("Got a scroll finish with no saved message from scroll start");
|
|
|
|
// This is being intermittently hit, and while not fatal or bothersome for the UI,
|
|
|
|
// it is something we want to trac down. To quiet emails, this is commented out
|
|
|
|
// for now.
|
|
|
|
// https://trac.humbughq.com/ticket/1138
|
|
|
|
//
|
|
|
|
// Make the block not empty to appease jslint
|
|
|
|
var ignored = true;
|
2013-03-15 15:23:28 +01:00
|
|
|
} else if (new_selected > scroll_start_message) {
|
|
|
|
var mark_as_read = [];
|
|
|
|
$.each(message_range(current_msg_list, scroll_start_message, new_selected),
|
|
|
|
function (idx, msg) {
|
|
|
|
if (message_unread(msg)) {
|
|
|
|
mark_as_read.push(msg);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
process_read_messages(mark_as_read);
|
|
|
|
}
|
2013-03-15 15:51:25 +01:00
|
|
|
scroll_start_message = undefined;
|
2013-03-15 15:23:28 +01:00
|
|
|
|
2013-03-04 23:44:07 +01:00
|
|
|
setTimeout(process_visible_unread_messages, 0);
|
2012-10-03 21:44:07 +02:00
|
|
|
}
|
2013-02-18 20:37:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var scroll_timer;
|
|
|
|
function scroll_finish() {
|
|
|
|
clearTimeout(scroll_timer);
|
|
|
|
scroll_timer = setTimeout(scroll_finished, 100);
|
|
|
|
}
|
|
|
|
|
2013-03-15 15:23:28 +01:00
|
|
|
$(window).scroll(function () {
|
2013-03-15 15:51:25 +01:00
|
|
|
if (scroll_start_message === undefined) {
|
2013-03-15 15:23:28 +01:00
|
|
|
scroll_start_message = current_msg_list.selected_id();
|
|
|
|
}
|
|
|
|
process_visible_unread_messages();
|
|
|
|
});
|
2013-03-14 19:56:15 +01:00
|
|
|
|
2013-02-18 20:37:17 +01:00
|
|
|
$(window).scroll($.throttle(50, function (e) {
|
|
|
|
scroll_finish();
|
2012-11-30 18:49:42 +01:00
|
|
|
}));
|
2012-10-03 21:44:07 +02:00
|
|
|
|
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.
|
2012-10-16 18:16:26 +02:00
|
|
|
move_pointer_at_page_top_and_bottom(delta);
|
2013-03-28 19:16:48 +01:00
|
|
|
last_viewport_movement_direction = delta;
|
2012-10-16 03:49:51 +02:00
|
|
|
});
|
2012-11-30 18:28:23 +01:00
|
|
|
|
|
|
|
$(window).mousewheel(function (e, delta) {
|
|
|
|
// Ignore mousewheel events if a modal is visible. It's weird if the
|
|
|
|
// user can scroll the main view by wheeling over the greyed-out area.
|
|
|
|
// 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
|
|
|
|
2012-11-30 18:49:42 +01:00
|
|
|
$(window).resize($.throttle(50, resizehandler));
|
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();
|
|
|
|
e.stopPropagation();
|
|
|
|
if ( ((delta > 0) && (scroll <= 0))
|
|
|
|
|| ((delta < 0) && (scroll >= (this.scrollHeight - self.innerHeight())))) {
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Ignore wheel events in the compose area which weren't already handled above.
|
|
|
|
$('#compose').mousewheel(function (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
2012-11-02 22:02:40 +01:00
|
|
|
function clear_password_change() {
|
|
|
|
// Clear the password boxes so that passwords don't linger in the DOM
|
|
|
|
// for an XSS attacker to find.
|
|
|
|
$('#old_password, #new_password, #confirm_password').val('');
|
|
|
|
}
|
|
|
|
|
2013-01-21 23:51:30 +01:00
|
|
|
// So, this is a rather inelegant hack that addresses two issues.
|
|
|
|
//
|
|
|
|
// The first issue goes something like this: we use Bootstrap's
|
|
|
|
// notion of tabs to show what pane you're in. Bootstrap likes to
|
|
|
|
// highlight the active tab. Since "Settings", etc. are in our
|
|
|
|
// dropdown, therefore the dropdown is the "active" tab, so we
|
|
|
|
// draw it as though it is pushed in! However, this is
|
|
|
|
// inappropriate for what we're trying to do. (we're trying to
|
|
|
|
// give you a menu, not indicate where you are; and undoing this
|
|
|
|
// and doing all the tab work by hand is just unnecessarily
|
|
|
|
// painful.)
|
|
|
|
//
|
|
|
|
// So to get around this, we take away the "active" status of
|
|
|
|
// gear-menu every single time a tab is shown.
|
|
|
|
$('#gear-menu a[data-toggle="tab"]').on('shown', function (e) {
|
|
|
|
$('#gear-menu').removeClass('active');
|
|
|
|
});
|
|
|
|
// Doing so ends up causing some other problem, though, where the
|
|
|
|
// little 'active' indicators get stuck on the menu sub-items, so
|
|
|
|
// we need to flush the old ones too once a new one is
|
|
|
|
// activated. (Otherwise, once you've been to a tab you can never
|
|
|
|
// go to it again).
|
|
|
|
//
|
|
|
|
// Incidentally, this also fixes a problem we have with
|
|
|
|
// e.relatedTarget; if you don't do the clearing as specified
|
|
|
|
// above, e.relatedTarget always ends up being the last link in
|
|
|
|
// our dropdown, as opposed to "the previously selected menu
|
|
|
|
// item."
|
|
|
|
$('#gear-menu a[data-toggle="tab"]').on('show', function (e) {
|
|
|
|
$('#gear-menu li').removeClass('active');
|
|
|
|
});
|
|
|
|
|
2013-01-16 21:42:35 +01:00
|
|
|
$('#gear-menu a[data-toggle="tab"]').on('show', function (e) {
|
2012-10-05 20:25:27 +02:00
|
|
|
// Save the position of our old tab away, before we switch
|
|
|
|
var old_tab = $(e.relatedTarget).attr('href');
|
|
|
|
scroll_positions[old_tab] = viewport.scrollTop();
|
|
|
|
});
|
2013-01-16 21:42:35 +01:00
|
|
|
$('#gear-menu a[data-toggle="tab"]').on('shown', function (e) {
|
2012-10-05 20:25:27 +02:00
|
|
|
var target_tab = $(e.target).attr('href');
|
2012-10-09 21:44:29 +02:00
|
|
|
|
|
|
|
// Hide all our error messages when switching tabs
|
|
|
|
$('.alert-error').hide();
|
|
|
|
$('.alert-success').hide();
|
|
|
|
$('.alert-info').hide();
|
|
|
|
$('.alert').hide();
|
2012-10-10 20:06:59 +02:00
|
|
|
|
2012-10-17 22:26:59 +02:00
|
|
|
$("#api_key_value").text("");
|
|
|
|
$("#get_api_key_box").hide();
|
|
|
|
$("#show_api_key_box").hide();
|
|
|
|
$("#api_key_button_box").show();
|
|
|
|
|
2012-11-02 22:02:40 +01:00
|
|
|
clear_password_change();
|
|
|
|
|
2012-10-10 20:06:59 +02:00
|
|
|
// Set the URL bar title to show the sub-page you're currently on.
|
2012-10-27 03:45:05 +02:00
|
|
|
var browser_url = target_tab;
|
2012-10-10 20:06:59 +02:00
|
|
|
if (browser_url === "#home") {
|
2012-12-19 21:19:29 +01:00
|
|
|
browser_url = "";
|
2012-10-10 20:06:59 +02:00
|
|
|
}
|
2012-12-19 21:19:29 +01:00
|
|
|
hashchange.changehash(browser_url);
|
2013-01-22 00:51:15 +01:00
|
|
|
|
|
|
|
// After we show the new tab, restore its old scroll position
|
|
|
|
// (we apparently have to do this after setting the hash,
|
|
|
|
// because otherwise that action may scroll us somewhere.)
|
|
|
|
if (scroll_positions.hasOwnProperty(target_tab)) {
|
|
|
|
viewport.scrollTop(scroll_positions[target_tab]);
|
|
|
|
} else {
|
2013-01-26 00:13:02 +01:00
|
|
|
if (target_tab === '#home') {
|
|
|
|
scroll_to_selected();
|
|
|
|
} else {
|
|
|
|
viewport.scrollTop(0);
|
|
|
|
}
|
2013-01-22 00:51:15 +01:00
|
|
|
}
|
2012-10-05 20:25:27 +02:00
|
|
|
});
|
|
|
|
|
2013-01-21 23:51:30 +01:00
|
|
|
// N.B. that subs.setup_page calls focus() on our stream textbox,
|
|
|
|
// which may cause the page to scroll away from where we used to
|
|
|
|
// have it (and instead to scroll to a weird place.)
|
2013-01-16 21:42:35 +01:00
|
|
|
$('#gear-menu a[href="#subscriptions"]').on('shown', subs.setup_page);
|
2012-10-03 22:35:35 +02:00
|
|
|
|
2013-04-08 20:03:21 +02:00
|
|
|
$('#pw_change_link').on('click', function (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
$('#pw_change_link').hide();
|
|
|
|
$('#pw_change_controls').show();
|
|
|
|
});
|
|
|
|
|
2013-04-04 00:55:36 +02:00
|
|
|
$('#new_password').on('change keyup', function () {
|
2013-04-08 20:21:20 +02:00
|
|
|
password_quality($('#new_password').val(), $('#pw_strength .bar'));
|
2013-04-04 00:55:36 +02:00
|
|
|
});
|
|
|
|
|
2012-10-03 22:35:35 +02:00
|
|
|
var settings_status = $('#settings-status');
|
2013-04-04 00:55:36 +02:00
|
|
|
|
|
|
|
function settings_change_error(message) {
|
2013-04-08 19:37:52 +02:00
|
|
|
// Scroll to the top so the error message is visible.
|
|
|
|
// We would scroll anyway if we end up submitting the form.
|
|
|
|
viewport.scrollTop(0);
|
2013-04-04 00:55:36 +02:00
|
|
|
settings_status.removeClass(status_classes)
|
|
|
|
.addClass('alert-error')
|
|
|
|
.text(message).stop(true).fadeTo(0,1);
|
|
|
|
}
|
|
|
|
|
2012-10-17 22:26:59 +02:00
|
|
|
$("#settings-change-box form").ajaxForm({
|
2012-10-03 22:35:35 +02:00
|
|
|
dataType: 'json', // This seems to be ignored. We still get back an xhr.
|
2013-04-04 00:55:36 +02:00
|
|
|
beforeSubmit: function (arr, form, options) {
|
|
|
|
// FIXME: Check that the two password fields match
|
|
|
|
// FIXME: Use the same jQuery validation plugin as the signup form?
|
|
|
|
var new_pw = $('#new_password').val();
|
|
|
|
if (new_pw !== '') {
|
2013-04-08 20:21:20 +02:00
|
|
|
var password_ok = password_quality(new_pw);
|
|
|
|
if (password_ok === undefined) {
|
2013-04-04 00:55:36 +02:00
|
|
|
// zxcvbn.js didn't load, for whatever reason.
|
|
|
|
settings_change_error(
|
|
|
|
'An internal error occurred; try reloading the page. ' +
|
|
|
|
'Sorry for the trouble!');
|
|
|
|
return false;
|
2013-04-08 20:21:20 +02:00
|
|
|
} else if (!password_ok) {
|
2013-04-04 00:55:36 +02:00
|
|
|
settings_change_error('New password is too weak');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2012-10-03 22:35:35 +02:00
|
|
|
success: function (resp, statusText, xhr, form) {
|
|
|
|
var message = "Updated settings!";
|
|
|
|
var result = $.parseJSON(xhr.responseText);
|
2012-11-05 23:51:27 +01:00
|
|
|
|
|
|
|
if (result.full_name !== undefined) {
|
|
|
|
$(".my_fullname").text(result.full_name);
|
2012-10-03 22:35:35 +02:00
|
|
|
}
|
2012-11-05 23:51:27 +01:00
|
|
|
update_gravatars();
|
|
|
|
|
2012-11-23 23:53:38 +01:00
|
|
|
if (result.enable_desktop_notifications !== undefined) {
|
2013-03-25 23:26:14 +01:00
|
|
|
page_params.desktop_notifications_enabled = result.enable_desktop_notifications;
|
2012-11-23 23:53:38 +01:00
|
|
|
}
|
|
|
|
|
2012-10-03 22:35:35 +02:00
|
|
|
settings_status.removeClass(status_classes)
|
|
|
|
.addClass('alert-success')
|
|
|
|
.text(message).stop(true).fadeTo(0,1);
|
|
|
|
// TODO: In theory we should auto-reload or something if
|
|
|
|
// you changed the email address or other fields that show
|
|
|
|
// up on all screens
|
|
|
|
},
|
|
|
|
error: function (xhr, error_type, xhn) {
|
|
|
|
var response = "Error changing settings";
|
|
|
|
if (xhr.status.toString().charAt(0) === "4") {
|
|
|
|
// Only display the error response for 4XX, where we've crafted
|
|
|
|
// a nice response.
|
|
|
|
response += ": " + $.parseJSON(xhr.responseText).msg;
|
|
|
|
}
|
2013-04-04 00:55:36 +02:00
|
|
|
settings_change_error(response);
|
2012-10-30 21:08:58 +01:00
|
|
|
},
|
|
|
|
complete: function (xhr, statusText) {
|
2012-11-02 22:02:40 +01:00
|
|
|
// Whether successful or not, clear the password boxes.
|
2012-10-30 21:08:58 +01:00
|
|
|
// TODO: Clear these earlier, while the request is still pending.
|
2012-11-02 22:02:40 +01:00
|
|
|
clear_password_change();
|
2012-10-03 23:22:51 +02:00
|
|
|
}
|
2012-10-03 22:35:35 +02:00
|
|
|
});
|
2012-10-12 00:48:37 +02:00
|
|
|
|
2012-10-17 22:26:59 +02:00
|
|
|
$("#get_api_key_box").hide();
|
|
|
|
$("#show_api_key_box").hide();
|
|
|
|
$("#get_api_key_box form").ajaxForm({
|
|
|
|
dataType: 'json', // This seems to be ignored. We still get back an xhr.
|
|
|
|
success: function (resp, statusText, xhr, form) {
|
|
|
|
var message = "Updated settings!";
|
|
|
|
var result = $.parseJSON(xhr.responseText);
|
|
|
|
$("#get_api_key_password").val("");
|
|
|
|
$("#api_key_value").text(result.api_key);
|
|
|
|
$("#show_api_key_box").show();
|
|
|
|
$("#get_api_key_box").hide();
|
|
|
|
settings_status.hide();
|
|
|
|
},
|
|
|
|
error: function (xhr, error_type, xhn) {
|
|
|
|
var response = "Error getting API key";
|
|
|
|
if (xhr.status.toString().charAt(0) === "4") {
|
|
|
|
// Only display the error response for 4XX, where we've crafted
|
|
|
|
// a nice response.
|
|
|
|
response += ": " + $.parseJSON(xhr.responseText).msg;
|
|
|
|
}
|
|
|
|
settings_status.removeClass(status_classes)
|
|
|
|
.addClass('alert-error')
|
|
|
|
.text(response).stop(true).fadeTo(0,1);
|
|
|
|
$("#show_api_key_box").hide();
|
|
|
|
$("#get_api_key_box").show();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
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.
|
2012-10-12 00:48:37 +02:00
|
|
|
resizehandler();
|
2012-10-15 21:57:30 +02:00
|
|
|
hack_for_floating_recipient_bar();
|
2012-10-12 17:26:04 +02:00
|
|
|
|
2013-03-25 23:26:14 +01:00
|
|
|
typeahead_helper.update_all_recipients(page_params.people_list);
|
2012-11-01 18:01:33 +01:00
|
|
|
composebox_typeahead.initialize();
|
2012-11-14 22:12:21 +01:00
|
|
|
search.initialize();
|
2012-11-23 23:53:38 +01:00
|
|
|
notifications.initialize();
|
2012-12-07 20:52:39 +01:00
|
|
|
hashchange.initialize();
|
2012-12-05 22:48:15 +01:00
|
|
|
invite.initialize();
|
2013-02-07 19:57:45 +01:00
|
|
|
activity.initialize();
|
2013-02-20 18:07:53 +01:00
|
|
|
subs.maybe_toggle_all_messages();
|
2013-02-13 22:15:13 +01:00
|
|
|
tutorial.initialize();
|
2012-10-18 20:23:57 +02:00
|
|
|
|
2012-11-14 23:33:13 +01:00
|
|
|
$("#main_div").on("click", ".messagebox", function (e) {
|
2013-03-11 18:21:39 +01:00
|
|
|
var target = $(e.target);
|
2013-04-19 03:45:59 +02:00
|
|
|
if (target.is("a") || target.is("img.message_inline_image") || target.is("img.twitter-avatar")) {
|
2012-11-14 23:33:13 +01:00
|
|
|
// If this click came from a hyperlink, don't trigger the
|
|
|
|
// reply action. The simple way of doing this is simply
|
|
|
|
// to call e.stopPropagation() from within the link's
|
|
|
|
// click handler.
|
|
|
|
//
|
|
|
|
// Unfortunately, on Firefox, this breaks Ctrl-click and
|
|
|
|
// Shift-click, because those are (apparently) implemented
|
|
|
|
// by adding an event listener on link clicks, and
|
|
|
|
// stopPropagation prevents them from being called.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!(clicking && mouse_moved)) {
|
|
|
|
// Was a click (not a click-and-drag).
|
|
|
|
var row = $(this).closest(".message_row");
|
2013-02-20 18:33:04 +01:00
|
|
|
current_msg_list.select_id(rows.id(row));
|
2012-11-14 23:33:13 +01:00
|
|
|
respond_to_message();
|
|
|
|
}
|
|
|
|
mouse_moved = false;
|
|
|
|
clicking = false;
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mousedown", ".messagebox", mousedown);
|
|
|
|
$("#main_div").on("mousemove", ".messagebox", mousemove);
|
|
|
|
$("#main_div").on("mouseover", ".messagebox", function (e) {
|
|
|
|
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
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseout", ".messagebox", function (e) {
|
2013-02-09 04:33:06 +01:00
|
|
|
message_unhover();
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
$("#main_div").on("mouseover", ".actions_hover", function (e) {
|
2012-11-14 23:33:13 +01:00
|
|
|
var row = $(this).closest(".message_row");
|
2013-02-09 04:33:06 +01:00
|
|
|
message_hover(row);
|
2013-02-09 04:43:59 +01:00
|
|
|
row.addClass("actions_hovered");
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
$("#main_div").on("mouseout", ".actions_hover", function (e) {
|
2012-11-14 23:33:13 +01:00
|
|
|
var row = $(this).closest(".message_row");
|
2013-02-09 04:33:06 +01:00
|
|
|
message_unhover();
|
2013-02-09 04:43:59 +01:00
|
|
|
row.removeClass("actions_hovered");
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2013-02-08 23:03:33 +01:00
|
|
|
$("#main_div").on("click", ".actions_hover", function (e) {
|
2012-11-14 23:33:13 +01:00
|
|
|
var row = $(this).closest(".message_row");
|
2012-11-16 02:30:44 +01:00
|
|
|
e.stopPropagation();
|
2013-02-09 20:20:35 +01:00
|
|
|
show_actions_popover(this, rows.id(row));
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2013-03-27 18:30:02 +01:00
|
|
|
$("#main_div").on("click", ".star", function (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
toggle_star(rows.id($(this).closest(".message_row")));
|
|
|
|
});
|
|
|
|
|
2012-11-21 20:22:20 +01:00
|
|
|
$("#home").on("click", ".narrows_by_recipient", function (e) {
|
2013-02-15 00:23:56 +01:00
|
|
|
var nearest = current_msg_list.get(rows.id($(this).closest(".recipient_row")));
|
|
|
|
var selected = current_msg_list.selected_message();
|
2013-02-26 23:09:15 +01:00
|
|
|
if (util.same_recipient(nearest, selected)) {
|
2013-02-15 00:23:56 +01:00
|
|
|
narrow.by_recipient(selected.id);
|
|
|
|
} else {
|
|
|
|
narrow.by_recipient(nearest.id);
|
|
|
|
}
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
|
|
|
|
2012-11-21 20:22:20 +01:00
|
|
|
$("#home").on("click", ".narrows_by_subject", function (e) {
|
2013-02-15 00:23:56 +01:00
|
|
|
var nearest = current_msg_list.get(rows.id($(this).closest(".recipient_row")));
|
|
|
|
var selected = current_msg_list.selected_message();
|
2013-03-05 21:10:28 +01:00
|
|
|
if (util.same_stream_and_subject(nearest, selected)) {
|
2013-02-15 00:23:56 +01:00
|
|
|
narrow.by_subject(selected.id);
|
|
|
|
} else {
|
|
|
|
narrow.by_subject(nearest.id);
|
|
|
|
}
|
2012-11-14 23:33:13 +01:00
|
|
|
});
|
2013-01-04 21:05:18 +01:00
|
|
|
|
|
|
|
$("#subscriptions_table").on("mouseover", ".subscription_header", function (e) {
|
|
|
|
$(this).addClass("active");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#subscriptions_table").on("mouseout", ".subscription_header", function (e) {
|
|
|
|
$(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 22:32:14 +01:00
|
|
|
$("li[data-name='home']").on('click', function () {
|
|
|
|
ui.change_tab_to('#home');
|
|
|
|
narrow.deactivate();
|
|
|
|
// We need to maybe scroll to the selected message
|
|
|
|
// once we have the proper viewport set up
|
|
|
|
setTimeout(maybe_scroll_to_selected, 0);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
2013-02-21 12:43:13 +01:00
|
|
|
$(".brand").on('click', function (e) {
|
2013-01-22 01:34:44 +01:00
|
|
|
if (exports.home_tab_obscured()) {
|
|
|
|
ui.change_tab_to('#home');
|
|
|
|
} else {
|
|
|
|
narrow.restore_home_state();
|
|
|
|
}
|
2013-02-12 22:32:14 +01:00
|
|
|
maybe_scroll_to_selected();
|
2013-01-22 01:34:44 +01:00
|
|
|
e.preventDefault();
|
|
|
|
});
|
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
|
|
|
|
|
|
|
$(document).on('message_selected.zephyr', function (event) {
|
|
|
|
if (current_msg_list !== event.msg_list) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var row = rows.get(event.id, event.msg_list.table_name);
|
|
|
|
$('.selected_message').removeClass('selected_message');
|
|
|
|
row.addClass('selected_message');
|
|
|
|
|
|
|
|
if (event.then_scroll) {
|
2013-04-03 19:33:10 +02:00
|
|
|
recenter_view(row, event.from_scroll);
|
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
|
|
|
|
|
|
|
$('#user_presences').on('click', 'a', function (e) {
|
|
|
|
var email = $(e.target).attr('data-email');
|
|
|
|
compose.start('private', {private_message_recipient: email});
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
2013-04-02 20:47:18 +02:00
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
$('#stream_filters li').on('click', 'a.subscription_name', function (e) {
|
2013-04-02 20:47:18 +02:00
|
|
|
var stream = $(e.target).parents('li').data('name');
|
|
|
|
narrow.by('stream', stream, {select_first_unread: true});
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
$('#stream_filters').on('click', '.expanded_subject a', function (e) {
|
|
|
|
var stream = $(e.target).parents('ul').data('stream');
|
|
|
|
var subject = $(e.target).parents('li').data('name');
|
|
|
|
|
|
|
|
narrow.activate([['stream', stream],
|
|
|
|
['subject', subject]],
|
|
|
|
{select_first_unread: true});
|
2013-04-02 20:47:18 +02:00
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
$('#stream_filters').on('click', '.streamlist_expand', function (e) {
|
|
|
|
var stream_li = $(e.target).parents('li');
|
|
|
|
var stream = stream_li.data('name');
|
|
|
|
|
|
|
|
$('ul.expanded_subjects', stream_li).toggleClass('hidden');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
2013-04-02 20:47:18 +02:00
|
|
|
$('.composebox-close').click(function (e) { compose.cancel(); });
|
|
|
|
$('.compose_stream_button').click(function (e) {
|
|
|
|
compose.set_mode('stream');
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
$('.compose_private_button').click(function (e) {
|
|
|
|
compose.set_mode('private');
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.empty_feed_compose_stream').click(function (e) {
|
|
|
|
compose.start('stream');
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
$('.empty_feed_compose_private').click(function (e) {
|
|
|
|
compose.start('private');
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
$('.empty_feed_join').click(function (e) {
|
|
|
|
subs.show_and_focus_on_narrow();
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.feedback_button').click(function (e) {
|
|
|
|
compose.start('private', { 'private_message_recipient': 'feedback@humbughq.com' });
|
|
|
|
});
|
|
|
|
$('.logout_button').click(function (e) {
|
|
|
|
$('#logout_form').submit();
|
|
|
|
});
|
|
|
|
$('.restart_get_updates_button').click(function (e) {
|
|
|
|
restart_get_updates({dont_block: true});
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#api_key_button').click(function (e) {
|
2013-04-08 20:06:43 +02:00
|
|
|
$("#get_api_key_box").show();
|
|
|
|
$("#api_key_button_box").hide();
|
2013-04-02 20:47:18 +02:00
|
|
|
});
|
|
|
|
$('.change_gravatar_button').click(function (e) {
|
|
|
|
ui.wait_for_gravatar();
|
|
|
|
});
|
|
|
|
$('.declare_bankruptcy_button').click(function (e) {
|
|
|
|
fast_forward_pointer(this);
|
|
|
|
});
|
|
|
|
|
|
|
|
$('body').on('click', '.respond_button', function (e) {
|
|
|
|
respond_to_message();
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
});
|
|
|
|
$('body').on('click', '.respond_personal_button', function (e) {
|
|
|
|
respond_to_message('personal');
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
});
|
|
|
|
$('body').on('click', '.popover_narrow_by_subject_button', function (e) {
|
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
narrow.by_subject(msgid);
|
|
|
|
});
|
|
|
|
$('body').on('click', '.popover_narrow_by_recipient_button', function (e) {
|
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
narrow.by_recipient(msgid);
|
|
|
|
});
|
|
|
|
$('body').on('click', '.popover_narrow_by_sender_button', function (e) {
|
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
|
|
|
var sender_email = $(e.currentTarget).data('sender_email');
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
narrow.by('sender', sender_email, {then_select_id: msgid});
|
|
|
|
});
|
|
|
|
$('body').on('click', '.popover_narrow_by_time_travel_button', function (e) {
|
|
|
|
var msgid = $(e.currentTarget).data('msgid');
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
narrow.by_time_travel(msgid);
|
|
|
|
});
|
|
|
|
|
|
|
|
$("body").on('click', function (e) {
|
|
|
|
// Dismiss the popover if the user has clicked outside it
|
|
|
|
if ($('.popover-inner').has(e.target).length === 0) {
|
|
|
|
ui.hide_actions_popover();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-10-03 21:44:07 +02:00
|
|
|
});
|
2012-11-05 23:51:27 +01:00
|
|
|
|
2013-02-06 19:48:26 +01:00
|
|
|
function sort_narrow_list() {
|
2013-04-10 22:24:15 +02:00
|
|
|
var items = $('#stream_filters > li').get();
|
2013-02-26 21:30:06 +01:00
|
|
|
var parent = $('#stream_filters');
|
2013-02-06 19:48:26 +01:00
|
|
|
items.sort(function(a,b){
|
2013-03-28 22:27:48 +01:00
|
|
|
return $(a).attr('data-name').localeCompare($(b).attr('data-name'));
|
2013-02-06 19:48:26 +01:00
|
|
|
});
|
|
|
|
|
2013-02-26 21:30:06 +01:00
|
|
|
parent.empty();
|
2013-02-06 19:48:26 +01:00
|
|
|
|
|
|
|
$.each(items, function(i, li){
|
2013-02-26 21:30:06 +01:00
|
|
|
parent.append(li);
|
2013-02-06 19:48:26 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
function iterate_to_find(selector, data_name, context) {
|
2013-03-09 01:36:52 +01:00
|
|
|
var retval = $();
|
2013-04-10 22:24:15 +02:00
|
|
|
$(selector, context).each(function (idx, elem) {
|
2013-03-09 01:36:52 +01:00
|
|
|
var jelem = $(elem);
|
2013-04-10 22:24:15 +02:00
|
|
|
if (jelem.attr('data-name') === data_name) {
|
2013-03-09 01:36:52 +01:00
|
|
|
retval = jelem;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return retval;
|
2013-04-10 22:24:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.get_filter_li = function(type, name) {
|
|
|
|
return iterate_to_find("#" + type + "_filters > li", name);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.get_subject_filter_li = function(stream, subject) {
|
|
|
|
var stream_li = exports.get_filter_li('stream', stream);
|
|
|
|
return iterate_to_find(".expanded_subjects li", subject, stream_li);
|
2013-02-12 20:41:01 +01:00
|
|
|
};
|
|
|
|
|
2013-02-06 19:48:26 +01:00
|
|
|
exports.add_narrow_filter = function(name, type, uri) {
|
|
|
|
var list_item;
|
|
|
|
|
2013-02-08 17:58:08 +01:00
|
|
|
/*
|
|
|
|
* We don't give MIT a stream list currently since that would likely be
|
|
|
|
* overwhelming for users given the vast number of streams MIT users are
|
|
|
|
* commonly subscribed to.
|
|
|
|
*
|
|
|
|
* This will not be as much of an issue once we do prioritization of streams
|
|
|
|
* in the list.
|
|
|
|
*/
|
2013-03-25 23:26:14 +01:00
|
|
|
if (page_params.domain === "mit.edu" && type === "stream") {
|
2013-02-08 17:58:08 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-12 20:41:01 +01:00
|
|
|
if (exports.get_filter_li(type, name).length) {
|
2013-02-06 19:48:26 +01:00
|
|
|
// already exists
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-02 03:10:32 +01:00
|
|
|
// For some reason, even though the span is inline-block, if it is empty it
|
|
|
|
// takes up no space and you don't see the background color. Thus, add a
|
|
|
|
// to get the inline-block behavior we want.
|
|
|
|
var swatch = $('<span/>').attr('id', "stream_sidebar_swatch_" + subs.stream_id(name))
|
|
|
|
.addClass('streamlist_swatch')
|
|
|
|
.css('background-color', subs.get_color(name)).html(" ");
|
2013-03-28 22:27:48 +01:00
|
|
|
list_item = $('<li>').attr('data-name', name).html(swatch);
|
2013-03-02 03:10:32 +01:00
|
|
|
list_item.append($('<a>').attr('href', uri)
|
|
|
|
.addClass('subscription_name')
|
|
|
|
.text(name)
|
|
|
|
.append('<span class="count">(<span class="value"></span>)</span>'));
|
2013-02-06 19:48:26 +01:00
|
|
|
if (type === "stream" && subs.have(name).invite_only) {
|
|
|
|
list_item.append("<i class='icon-lock'/>");
|
|
|
|
}
|
|
|
|
$("#" + type + "_filters").append(list_item);
|
|
|
|
sort_narrow_list();
|
|
|
|
};
|
|
|
|
|
2013-02-12 20:41:01 +01:00
|
|
|
exports.get_count = function (type, name) {
|
|
|
|
return exports.get_filter_li(type, name).find('.count .value').text();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.set_count = function (type, name, count) {
|
2013-03-28 22:27:48 +01:00
|
|
|
var count_span = exports.get_filter_li(type, name).find('.count');
|
2013-02-12 20:41:01 +01:00
|
|
|
var value_span = count_span.find('.value');
|
|
|
|
|
|
|
|
if (count === 0) {
|
2013-03-28 22:27:48 +01:00
|
|
|
return exports.clear_count(type, name);
|
2013-02-12 20:41:01 +01:00
|
|
|
}
|
|
|
|
count_span.show();
|
|
|
|
|
|
|
|
value_span.text(count);
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.clear_count = function (type, name) {
|
|
|
|
exports.get_filter_li(type, name).find('.count').hide()
|
|
|
|
.find('.value').text('');
|
|
|
|
};
|
|
|
|
|
2013-02-06 19:48:26 +01:00
|
|
|
exports.remove_narrow_filter = function (name, type) {
|
2013-02-12 20:41:01 +01:00
|
|
|
exports.get_filter_li(type, name).remove();
|
2013-02-06 19:48:26 +01:00
|
|
|
};
|
|
|
|
|
2013-04-04 22:30:28 +02:00
|
|
|
exports.remove_all_narrow_filters = function () {
|
|
|
|
$("#stream_filters").children().remove();
|
|
|
|
};
|
|
|
|
|
2013-02-17 19:22:26 +01:00
|
|
|
var presence_descriptions = {
|
|
|
|
active: ' is active',
|
|
|
|
away: ' was recently active',
|
|
|
|
idle: ' is not active'
|
|
|
|
};
|
|
|
|
|
2013-02-17 19:12:35 +01:00
|
|
|
exports.set_presence_list = function (users, presence_info) {
|
2013-02-07 19:57:45 +01:00
|
|
|
$('#user_presences').empty();
|
2013-02-14 16:19:32 +01:00
|
|
|
|
2013-02-17 19:12:35 +01:00
|
|
|
function add_entry(name, email, type) {
|
|
|
|
var entry = $('<li>')
|
2013-03-07 20:12:27 +01:00
|
|
|
.append($('<a>').attr({href: '#', 'data-email': email})
|
2013-02-17 19:12:35 +01:00
|
|
|
.text(name))
|
|
|
|
.addClass('user_' + type)
|
2013-03-07 20:12:27 +01:00
|
|
|
.attr('title', name + presence_descriptions[type]);
|
2013-03-07 17:48:20 +01:00
|
|
|
if (email === this.email) {
|
|
|
|
entry.addClass('my_fullname');
|
|
|
|
}
|
2013-02-17 19:12:35 +01:00
|
|
|
$('#user_presences').append(entry);
|
|
|
|
}
|
2013-02-14 16:19:32 +01:00
|
|
|
|
2013-03-25 23:26:14 +01:00
|
|
|
if (page_params.domain !== "mit.edu") {
|
|
|
|
add_entry(page_params.fullname, page_params.email, 'active');
|
2013-02-19 22:37:03 +01:00
|
|
|
}
|
2013-02-14 16:19:32 +01:00
|
|
|
|
2013-02-17 19:12:35 +01:00
|
|
|
$.each(users, function (idx, email) {
|
|
|
|
if (people_dict[email] !== undefined) {
|
|
|
|
add_entry(people_dict[email].full_name, email, presence_info[email]);
|
2013-02-15 21:01:49 +01:00
|
|
|
}
|
2013-02-07 19:57:45 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-02-13 22:10:11 +01:00
|
|
|
function add_font_size(offset, name) {
|
|
|
|
var style = {
|
|
|
|
'font-size': (14 + 3*offset) + 'px',
|
|
|
|
'line-height': (20 + 3*offset) + 'px'
|
|
|
|
};
|
|
|
|
|
|
|
|
var entry = $('<li>').append(
|
|
|
|
$('<a href="#">')
|
|
|
|
.text(name)
|
|
|
|
.css(style)
|
|
|
|
.click(function (e) {
|
|
|
|
$('body').css(style);
|
|
|
|
scroll_to_selected();
|
|
|
|
e.preventDefault();
|
|
|
|
}));
|
|
|
|
|
|
|
|
$('#font_size_menu').append(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
// Create font size menu entries
|
|
|
|
add_font_size(-1, 'Small' );
|
|
|
|
add_font_size( 0, 'Medium' );
|
|
|
|
add_font_size( 1, 'Large' );
|
|
|
|
add_font_size( 3, 'Larger' );
|
|
|
|
add_font_size( 5, 'Largest');
|
|
|
|
|
|
|
|
// The entry that produces the submenu has a href
|
|
|
|
// for style, but we don't want to navigate on click.
|
|
|
|
$('#font_size_menu_link').click(function (e) {
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
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 () {
|
|
|
|
$('#new_message_content').blur(function () {
|
|
|
|
saved_compose_cursor = $(this).caret().start;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
exports.restore_compose_cursor = function () {
|
|
|
|
// Restore as both the start and end point, i.e.
|
|
|
|
// nothing selected.
|
|
|
|
$('#new_message_content')
|
|
|
|
.focus()
|
|
|
|
.caret(saved_compose_cursor, saved_compose_cursor);
|
|
|
|
};
|
|
|
|
|
2013-04-10 22:24:15 +02:00
|
|
|
exports.update_recent_subjects = function () {
|
|
|
|
function same(arr1, arr2) {
|
|
|
|
var i = 0;
|
|
|
|
|
|
|
|
if (arr1.length !== arr2.length) return false;
|
|
|
|
for (i = 0; i < arr1.length; i++) {
|
|
|
|
if (arr2[i] !== arr1[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$("#stream_filters > li").each(function (idx, elem) {
|
|
|
|
var stream = $(elem).data('name');
|
|
|
|
var expander = $('.streamlist_expand', elem);
|
|
|
|
var subjects = recent_subjects[stream] || [];
|
|
|
|
var subject_names = $.map(subjects, function (elem, idx) {
|
|
|
|
return elem.subject;
|
|
|
|
});
|
|
|
|
|
|
|
|
expander.toggleClass('hidden', subjects.length === 0);
|
|
|
|
|
|
|
|
var currently_shown = $('ul.expanded_subjects li', elem).map(function(idx, elem) {
|
|
|
|
return $(elem).text().trim();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!same(currently_shown, subject_names)) {
|
|
|
|
var subject_list = $("ul.expanded_subjects", elem);
|
|
|
|
|
|
|
|
var was_hidden = subject_list.length === 0 || subject_list.hasClass('hidden');
|
|
|
|
// If this is the first subject in current narrow, show it regardless
|
|
|
|
var operators = narrow.operators();
|
|
|
|
if (subject_list.length === 0 && operators.length > 0 && operators[0][0] === 'stream') {
|
|
|
|
was_hidden = operators[0][1] !== stream;
|
|
|
|
}
|
|
|
|
var active_subject = $("ul.expanded_subjects li.active-subject-filter").text().trim();
|
|
|
|
|
|
|
|
subject_list.remove();
|
|
|
|
|
|
|
|
if (subjects.length > 0) {
|
|
|
|
$(elem).append(templates.render('sidebar_subject_list',
|
|
|
|
{subjects: subjects,
|
|
|
|
stream: stream,
|
|
|
|
hidden: was_hidden}));
|
|
|
|
if (active_subject !== '') {
|
|
|
|
exports.get_subject_filter_li(stream, active_subject).addClass('active-subject-filter');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2012-11-16 16:45:39 +01:00
|
|
|
return exports;
|
|
|
|
}());
|