Files
zulip/zephyr/static/js/zephyr.js
Luke Faraone f6b481546f Offer to subscribe / create classes if user zwrites to a non-subbed class
Here we check if a class exists. If not, we prompt the user to create, sub,
and send his message to the class. If the class exists but we're not subbed
we prompt the user to sub.

This commit also added a decorator to views.py and refactored out some
redundant code.

(imported from commit 7234ef6c080f2a6de6ff0922635dddd90032f7fe)
2012-09-06 14:33:55 -04:00

492 lines
18 KiB
JavaScript

function resize_main_div() {
// Resize main_div to exactly take up remaining vertical space.
var div = $('#main_div');
div.height(Math.max(200, div.height() + $(window).height() - $('body').height()));
}
$(function () {
resize_main_div();
$(window).resize(resize_main_div);
$('#zephyr-type-tabs a').on('shown', function (e) { resize_main_div(); });
});
$(function() {
$('#zephyr-type-tabs a[href="#class-message"]').on('shown', function (e) {
$('#class-message input:not(:hidden):first').focus().select();
});
$('#zephyr-type-tabs a[href="#personal-message"]').on('shown', function (e) {
$('#personal-message input:not(:hidden):first').focus().select();
});
});
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
function sub(zephyr_class) {
// Supports multiple classes, separate by commas.
// TODO: check the return value and handle an error condition
$.post('/subscriptions/add/', {new_subscriptions: zephyr_class});
}
$(function() {
var status_classes = 'alert-error alert-success alert-info';
var send_status = $('#send-status');
var buttons = $('#class-message, #personal-message').find('input[type="submit"]');
var options = {
dataType: 'json', // This seems to be ignored. We still get back an xhr.
beforeSubmit: function (form, _options) {
send_status.removeClass(status_classes)
.addClass('alert-info')
.text('Sending')
.stop(true).fadeTo(0,1);
buttons.attr('disabled', 'disabled');
buttons.blur()
var okay = true;
$.ajaxSetup({async:false}); // so we get blocking gets
$.get("subscriptions/exists/" + $("#class").val(), function(data) {
if (data == "False") {
// The class doesn't exist
okay = false;
send_status.removeClass(status_classes)
send_status.toggle();
$('#class-dne-name').text($("#class").val());
$('#class-dne').show();
$('#create-it').focus()
.click(function() {
sub($("#class").val());
$("#class-message form").ajaxSubmit();
$('#class-dne').stop(true).fadeOut(500);
});
buttons.removeAttr('disabled');
}
});
$.ajaxSetup({async:true});
if (okay && class_list.indexOf($("#class").val()) == -1) {
// You're not subbed to the class
okay = false;
send_status.removeClass(status_classes);
send_status.toggle();
$('#class-nosub-name').text($("#class").val());
$('#class-nosub').show();
$('#sub-it').focus()
.click(function() {
sub($("#class").val());
$("#class-message form").ajaxSubmit();
$('#class-nosub').stop(true).fadeOut(500);
});
buttons.removeAttr('disabled');
}
return okay;
},
success: function (resp, statusText, xhr, form) {
form.find('textarea').val('');
send_status.removeClass(status_classes)
.addClass('alert-success')
.text('Sent message')
.stop(true).fadeTo(0,1).delay(1000).fadeOut(1000);
buttons.removeAttr('disabled');
},
error: function(xhr) {
var response = "Error sending message";
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;
}
send_status.removeClass(status_classes)
.addClass('alert-error')
.text(response)
.append($('<span />')
.addClass('send-status-close').html('&times;')
.click(function () { send_status.stop(true).fadeOut(500); }))
.stop(true).fadeTo(0,1);
buttons.removeAttr('disabled');
}
};
send_status.hide();
$("#class-message form").ajaxForm(options);
$("#personal-message form").ajaxForm(options);
});
selected_tag = '<p id="selected">&#x25b6;</p>'
var allow_hotkeys = true;
function select_zephyr(next_zephyr) {
if (next_zephyr.length == 0) {
// No match, e.g. bottom or top of page
return;
}
// Clear the previous arrow.
$("#selected").closest("td").empty();
next_zephyr.children("td:first").html(selected_tag);
$.post("update", {pointer: next_zephyr.attr("id")});
if (($(next_zephyr).offset().top < $("#main_div").offset().top) ||
($(next_zephyr).offset().top + $(next_zephyr).height() >
$("#main_div").offset().top + $("#main_div").height())) {
scroll_to_selected();
}
}
// NB: This just binds to current elements, and won't bind to elements
// created after ready() is called.
$(document).ready(function() {
$('input, textarea, button').focus(function() {
allow_hotkeys = false;
});
$('input, textarea, button').blur(function() {
allow_hotkeys = true;
});
$("body").delegate("p", "click", function(){
select_zephyr($(this).parent().parent());
});
});
var goto_pressed = false;
$(document).keydown(function(event) {
if (allow_hotkeys) {
if (event.keyCode == 38 || event.keyCode == 40) { // down or up arrow
var tr = $("#selected").closest("tr");
if (event.keyCode == 40) { // down arrow
// There are probably more verbose but more efficient ways to do this.
next_zephyr = tr.nextAll(":not(:hidden):first");
} else { // up arrow
next_zephyr = tr.prevAll(":not(:hidden):first");
}
select_zephyr(next_zephyr);
event.preventDefault();
} else if (event.keyCode == 82) { // 'r' keypress, for responding to a zephyr
var parent = $("#selected").parents("tr");
var zephyr_class = parent.find("span.zephyr_class").text();
var zephyr_huddle = parent.find("span.zephyr_huddle_recipient").text();
var zephyr_personal = parent.find("span.zephyr_personal_recipient").text();
var instance = parent.find("span.zephyr_instance").text();
if (zephyr_class != '') {
$('#zephyr-type-tabs a[href="#class-message"]').tab('show');
$("#class").val(zephyr_class);
$("#instance").val(instance);
$("#new_zephyr").focus();
$("#new_zephyr").select();
} else if (zephyr_huddle != '') {
var recipients = parent.find("span.zephyr_huddle_recipients_list").text();
$('#zephyr-type-tabs a[href="#personal-message"]').tab('show');
$("#recipient").val(recipients);
$("#new_personal_zephyr").focus();
$("#new_personal_zephyr").select();
} else if (zephyr_personal != '') {
var recipient = parent.find("span.zephyr_sender").text();
if (recipient == username) { // that is, we sent the original message
recipient = parent.find("span.zephyr_personal_recipient").text();
}
$('#zephyr-type-tabs a[href="#personal-message"]').tab('show');
$("#recipient").val(recipient);
$("#new_personal_zephyr").focus();
$("#new_personal_zephyr").select();
}
event.preventDefault();
} else if (event.keyCode == 71) { // 'g' keypress, set trigger for "go to"
goto_pressed = true;
event.preventDefault();
} else if (goto_pressed && event.keyCode == 67) { // 'c' keypress, for narrow-by-recipient
var parent = $("#selected").parents("tr");
var zephyr_class = parent.find("span.zephyr_class").text();
narrow_class(zephyr_class, parent.attr("id"));
event.preventDefault()
} else if (goto_pressed && event.keyCode == 73) { // 'i' keypress, for narrow-by-instance
var parent = $("#selected").parents("tr");
var zephyr_class = parent.find("span.zephyr_class").text();
var zephyr_instance = parent.find("span.zephyr_instance").text();
narrow_instance(zephyr_class, zephyr_instance, parent.attr("id"));
event.preventDefault()
} else if (goto_pressed && event.keyCode == 80) { // 'p' keypress, for narrow-to-personals
narrow_all_personals($("#selected").parents("tr").attr("id"));
event.preventDefault();
} else if (goto_pressed && event.keyCode == 65) { // 'a' keypress, for unnarrow
unhide();
event.preventDefault();
}
if (event.keyCode != 71) { // not 'g'
goto_pressed = false;
}
} else if (event.keyCode == 27) { // Esc pressed
$('input, textarea, button').blur();
event.preventDefault();
}
});
function scroll_to_selected() {
$('#main_div').scrollTop(0);
$('#main_div').scrollTop($("#selected").offset().top - $('#main_div').height()/1.5);
}
function home_view(element) {
return true;
}
var current_view_predicate = home_view;
function current_view(element) {
if (current_view_predicate(element)) {
element.show();
} else {
element.hide();
}
}
function do_narrow(target_zephyr, description, filter_function) {
// We want the zephyr on which the narrow happened to stay in the same place if possible.
var old_top = $("#main_div").offset().top - $("#" + target_zephyr).offset().top;
current_view_predicate = filter_function;
$("tr").each(function() {
current_view($(this))
});
$("#selected").closest("td").empty();
$("#" + target_zephyr).children("td:first").html(selected_tag);
$.post("update", {pointer: target_zephyr});
scroll_to_selected();
$("#unhide").removeAttr("disabled");
$("#narrow_indicator").html(description);
}
function narrow_huddle(target_zephyr) {
var parent = $("#" + target_zephyr);
var recipients = parent.find("span.zephyr_huddle_recipients_list").text();
var message = "Showing group chats with " + recipients;
do_narrow(target_zephyr, message,
function(element) {
return (element.find("span.zephyr_huddle_recipient").length > 0 &&
element.find("span.zephyr_huddle_recipients_list").text() == recipients);
}
);
}
function narrow_all_personals(target_zephyr) {
// Narrow to all personals
var message = "Showing all personals";
do_narrow(target_zephyr, message,
function(element) {
return (element.find("span.zephyr_personal_recipient").length > 0);
}
);
}
function narrow_personals(target_zephyr) {
// Narrow to personals with a specific user
var target_recipient = $("#" + target_zephyr).find("span.zephyr_personal_recipient").text();
var target_sender = $("#" + target_zephyr).find("span.zephyr_sender").text();
var other_party;
if (target_recipient == username) {
other_party = target_sender;
} else {
other_party = target_recipient;
}
var message = "Showing personals with " + other_party;
do_narrow(target_zephyr, message,
function(element) {
var recipient = element.find("span.zephyr_personal_recipient");
var sender = element.find("span.zephyr_sender");
return ((recipient.length > 0) &&
((recipient.text() == target_recipient) && (sender.text() == target_sender)) ||
((recipient.text() == target_sender) && (sender.text() == target_recipient)));
}
);
}
function narrow_class(class_name, target_zephyr) {
var message = "Showing <span class='label zephyr_class'>" + class_name + "</span>";
do_narrow(target_zephyr, message,
function(element) {
return (element.find("span.zephyr_class").length > 0 &&
element.find("span.zephyr_class").text() == class_name);
}
);
}
function narrow_instance(class_name, instance, target_zephyr) {
var message = "Showing <span class='label zephyr_class'>" + class_name
+ "</span> <span class='label zephyr_instance'>" + instance + "</span>";
do_narrow(target_zephyr, message,
function(element) {
return (element.find("span.zephyr_class").length > 0 &&
element.find("span.zephyr_class").text() == class_name &&
element.find("span.zephyr_instance").text() == instance);
}
);
}
function unhide() {
current_view_predicate = home_view;
$("tr").show();
scroll_to_selected();
$("#unhide").attr("disabled", "disabled");
$("#narrow_indicator").html("");
}
function newline2br(content) {
return content.replace(/\n/g, '<br />');
}
function update_autocomplete() {
class_list.sort();
instance_list.sort();
people_list.sort();
$( "#class" ).autocomplete({
source: class_list
});
$( "#instance" ).autocomplete({
source: instance_list
});
$( "#recipient" ).autocomplete({
source: people_list
});
}
function add_message(index, zephyr) {
if (zephyr.type == 'class') {
zephyr.is_class = true;
if ($.inArray(zephyr.display_recipient, class_list) == -1) {
class_list.push(zephyr.display_recipient);
update_autocomplete();
}
if ($.inArray(zephyr.instance, instance_list) == -1) {
instance_list.push(zephyr.instance);
update_autocomplete();
}
} else if (zephyr.type == "huddle") {
zephyr.is_huddle = true;
} else {
zephyr.is_personal = true;
if (zephyr.display_recipient != username &&
$.inArray(zephyr.display_recipient, people_list) == -1) {
people_list.push(zephyr.display_recipient);
update_autocomplete();
}
if (zephyr.sender != username &&
$.inArray(zephyr.sender, people_list) == -1) {
people_list.push(zephyr.sender);
update_autocomplete();
}
}
zephyr.html_content = newline2br(zephyr.content);
var new_tr = $('<tr />').attr('id', zephyr.id);
$('#table').append(new_tr);
new_tr.append(ich.zephyr(zephyr));
current_view(new_tr);
}
$(function () {
/* We can't easily embed this client-side template in index.html,
because its syntax conflicts with Django's. */
$.get('/static/templates/zephyr.html', function (template) {
ich.addTemplate('zephyr', template);
$(initial_zephyr_json).each(add_message);
var selected = $("#" + initial_pointer);
if (selected.length == 0) {
// initial_pointer names a zephyr we can't see
selected = $("tr:first");
}
select_zephyr(selected);
get_updates_longpoll();
});
});
var longpoll_failures = 0;
function get_updates_longpoll() {
var last_received = 0;
if ($("tr:last").attr("id")) {
last_received = $("tr:last").attr("id");
}
console.log(new Date() + ': longpoll started');
$.ajax({
type: 'POST',
url: 'get_updates_longpoll',
data: { last_received: last_received },
dataType: 'json',
timeout: 10*60*1000, // 10 minutes in ms
success: function (data) {
console.log(new Date() + ': longpoll success');
longpoll_failures = 0;
$('#connection-error').hide();
if (data && data.zephyrs) {
$.each(data.zephyrs, add_message);
}
setTimeout(get_updates_longpoll, 0);
},
error: function (xhr, error_type, exn) {
if (error_type == 'timeout') {
// Retry indefinitely on timeout.
console.log(new Date() + ': longpoll timed out');
longpoll_failures = 0;
$('#connection-error').hide();
} else {
console.log(new Date() + ': longpoll failed with ' + error_type +
' (' + longpoll_failures + ' failures)');
longpoll_failures += 1;
}
if (longpoll_failures >= 5) {
$('#connection-error').show();
} else {
$('#connection-error').hide();
}
resize_main_div();
var retry_sec = Math.min(90, Math.exp(longpoll_failures/2));
console.log(new Date() + ': longpoll retrying in ' + retry_sec + ' seconds');
setTimeout(get_updates_longpoll, retry_sec*1000);
}
});
}
$(function() {
update_autocomplete();
});