2013-05-06 02:54:15 +02:00
|
|
|
var stream_list = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2013-11-22 21:04:49 +01:00
|
|
|
var zoomed_to_topics = false;
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
var zoomed_stream = '';
|
2015-11-25 18:41:32 +01:00
|
|
|
var private_messages_open = false;
|
2013-05-16 23:40:07 +02:00
|
|
|
var last_private_message_count = 0;
|
2013-05-31 19:07:59 +02:00
|
|
|
var last_mention_count = 0;
|
2013-05-07 19:07:05 +02:00
|
|
|
var previous_sort_order;
|
2016-07-01 07:26:09 +02:00
|
|
|
var previous_unpinned_order;
|
2013-05-16 23:40:07 +02:00
|
|
|
|
2013-11-22 21:04:49 +01:00
|
|
|
function active_stream_name() {
|
|
|
|
if (narrow.active()) {
|
|
|
|
var op_streams = narrow.filter().operands('stream');
|
|
|
|
if (op_streams) {
|
|
|
|
return op_streams[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
function filter_streams_by_search(streams) {
|
|
|
|
var search_box = $(".stream-list-filter");
|
|
|
|
|
|
|
|
var search_term = search_box.expectOne().val().trim();
|
|
|
|
|
|
|
|
if (search_term === '') {
|
|
|
|
return streams;
|
|
|
|
}
|
|
|
|
|
|
|
|
var search_terms = search_term.toLowerCase().split(",");
|
|
|
|
search_terms = _.map(search_terms, function (s) {
|
|
|
|
return s.trim();
|
|
|
|
});
|
|
|
|
|
|
|
|
var filtered_streams = _.filter(streams, function (stream) {
|
|
|
|
return _.any(search_terms, function (search_term) {
|
|
|
|
var lower_stream_name = stream.toLowerCase().split(" ");
|
|
|
|
return _.any(lower_stream_name, function (name) {
|
|
|
|
return name.indexOf(search_term) === 0;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return filtered_streams;
|
|
|
|
}
|
|
|
|
|
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
|
|
|
exports.create_initial_sidebar_rows = function () {
|
|
|
|
// This code is slightly opaque, but it ends up building
|
|
|
|
// up list items and attaching them to the "sub" data
|
|
|
|
// structures that are kept in stream_data.js.
|
|
|
|
var subs = stream_data.subscribed_subs();
|
|
|
|
|
|
|
|
_.each(subs, function (sub) {
|
|
|
|
exports.create_sidebar_row(sub);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list = function () {
|
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
|
|
|
// This function assumes we have already created the individual
|
|
|
|
// sidebar rows. Our job here is to build the bigger widget,
|
|
|
|
// which largely is a matter of arranging the individual rows in
|
|
|
|
// the right order.
|
2013-08-15 21:11:07 +02:00
|
|
|
var streams = stream_data.subscribed_streams();
|
2013-05-06 23:36:22 +02:00
|
|
|
if (streams.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
streams = filter_streams_by_search(streams);
|
|
|
|
|
2013-05-06 23:36:22 +02:00
|
|
|
var sort_recent = (streams.length > 40);
|
2016-07-01 07:26:09 +02:00
|
|
|
var pinned_streams = [];
|
|
|
|
var unpinned_streams = [];
|
|
|
|
var parent = $('#stream_filters');
|
|
|
|
var elems = [];
|
2013-05-06 23:36:22 +02:00
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
function add_sidebar_li(stream) {
|
|
|
|
var li = $(stream_data.get_sub(stream).sidebar_li);
|
|
|
|
if (sort_recent) {
|
2016-10-28 17:18:56 +02:00
|
|
|
if (stream_data.is_active(stream)) {
|
2016-07-01 07:26:09 +02:00
|
|
|
li.removeClass('inactive_stream');
|
2016-10-28 17:00:34 +02:00
|
|
|
} else {
|
|
|
|
li.addClass('inactive_stream');
|
2016-07-01 07:26:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
elems.push(li.get(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
_.each(streams, function (stream) {
|
|
|
|
var pinned = stream_data.get_sub(stream).pin_to_top;
|
|
|
|
if (pinned) {
|
|
|
|
pinned_streams.push(stream);
|
|
|
|
} else {
|
|
|
|
unpinned_streams.push(stream);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
unpinned_streams.sort(function (a, b) {
|
2013-05-06 02:54:15 +02:00
|
|
|
if (sort_recent) {
|
2016-10-28 17:18:56 +02:00
|
|
|
if (stream_data.is_active(b) && ! stream_data.is_active(a)) {
|
2013-05-06 02:54:15 +02:00
|
|
|
return 1;
|
2016-10-28 17:18:56 +02:00
|
|
|
} else if (! stream_data.is_active(b) && stream_data.is_active(a)) {
|
2013-05-06 02:54:15 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2013-05-06 23:36:22 +02:00
|
|
|
return util.strcmp(a, b);
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
streams = pinned_streams.concat(unpinned_streams);
|
|
|
|
|
|
|
|
if (previous_sort_order !== undefined && util.array_compare(previous_sort_order, streams)
|
|
|
|
&& util.array_compare(previous_unpinned_order, unpinned_streams)) {
|
2013-05-07 19:07:05 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
previous_sort_order = streams;
|
2016-07-01 07:26:09 +02:00
|
|
|
previous_unpinned_order = unpinned_streams;
|
2013-05-06 02:54:15 +02:00
|
|
|
parent.empty();
|
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
_.each(pinned_streams, function (stream) {
|
2013-08-15 21:11:07 +02:00
|
|
|
var li = $(stream_data.get_sub(stream).sidebar_li);
|
2013-05-06 02:54:15 +02:00
|
|
|
if (sort_recent) {
|
2016-10-28 17:18:56 +02:00
|
|
|
if (! stream_data.is_active(stream)) {
|
2013-05-06 23:36:22 +02:00
|
|
|
li.addClass('inactive_stream');
|
2013-05-06 02:54:15 +02:00
|
|
|
} else {
|
2013-05-06 23:36:22 +02:00
|
|
|
li.removeClass('inactive_stream');
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
}
|
2013-05-06 23:36:22 +02:00
|
|
|
elems.push(li.get(0));
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
2016-07-01 07:26:09 +02:00
|
|
|
|
|
|
|
if (pinned_streams.length > 0) {
|
|
|
|
_.each(pinned_streams, add_sidebar_li);
|
|
|
|
elems.push($('<hr class="pinned-stream-split">').get(0));
|
|
|
|
}
|
|
|
|
if (unpinned_streams.length > 0) {
|
|
|
|
_.each(unpinned_streams, add_sidebar_li);
|
|
|
|
}
|
|
|
|
|
2013-05-06 23:36:22 +02:00
|
|
|
$(elems).appendTo(parent);
|
2013-05-06 02:54:15 +02:00
|
|
|
};
|
|
|
|
|
2013-06-07 16:56:05 +02:00
|
|
|
function iterate_to_find(selector, name_to_find, context) {
|
2013-07-30 00:35:44 +02:00
|
|
|
var lowercase_name = name_to_find.toLowerCase();
|
|
|
|
var found = _.find($(selector, context), function (elem) {
|
2013-08-03 00:18:04 +02:00
|
|
|
return $(elem).attr('data-name').toLowerCase() === lowercase_name;
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
2013-07-30 00:35:44 +02:00
|
|
|
return found ? $(found) : $();
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
|
2013-06-05 20:29:31 +02:00
|
|
|
// TODO: Now that the unread count functions support the user sidebar
|
|
|
|
// as well, we probably should consider moving them to a different file.
|
2013-05-06 22:18:01 +02:00
|
|
|
function get_filter_li(type, name) {
|
2013-05-06 02:54:15 +02:00
|
|
|
if (type === 'stream') {
|
2014-02-07 16:55:54 +01:00
|
|
|
var sub = stream_data.get_sub(name);
|
|
|
|
return $("#stream_sidebar_" + sub.stream_id);
|
2013-06-05 20:29:31 +02:00
|
|
|
} else if (type === "private") {
|
2013-10-18 00:56:49 +02:00
|
|
|
if (name.indexOf(",") < 0) {
|
|
|
|
return $("li.user_sidebar_entry[data-email='" + name + "']");
|
|
|
|
} else {
|
|
|
|
return $("li.group-pms-sidebar-entry[data-emails='" + name + "']");
|
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
return iterate_to_find("#" + type + "_filters > li", name);
|
2013-05-06 22:18:01 +02:00
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
function zoom_in() {
|
|
|
|
popovers.hide_all();
|
|
|
|
zoomed_to_topics = true;
|
2015-12-11 08:24:00 +01:00
|
|
|
$("#streams_list").expectOne().removeClass("zoom-out").addClass("zoom-in");
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
zoomed_stream = active_stream_name();
|
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
// Hide stream list titles and pinned stream splitter
|
|
|
|
$(".stream-filters-label").each(function () {
|
|
|
|
$(this).hide();
|
|
|
|
});
|
|
|
|
$(".pinned-stream-split").each(function () {
|
|
|
|
$(this).hide();
|
|
|
|
});
|
|
|
|
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
$("#stream_filters li.narrow-filter").each(function () {
|
|
|
|
var elt = $(this);
|
|
|
|
|
|
|
|
if (elt.attr('data-name') === zoomed_stream) {
|
|
|
|
elt.show();
|
|
|
|
} else {
|
|
|
|
elt.hide();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function zoom_out() {
|
|
|
|
popovers.hide_all();
|
|
|
|
zoomed_to_topics = false;
|
2016-07-01 07:26:09 +02:00
|
|
|
// Show stream list titles and pinned stream splitter
|
|
|
|
$(".stream-filters-label").each(function () {
|
|
|
|
$(this).show();
|
|
|
|
});
|
|
|
|
$(".pinned-stream-split").each(function () {
|
|
|
|
$(this).show();
|
|
|
|
});
|
|
|
|
|
2015-12-11 08:24:00 +01:00
|
|
|
$("#streams_list").expectOne().removeClass("zoom-in").addClass("zoom-out");
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
$("#stream_filters li.narrow-filter").show();
|
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
function remove_expanded_private_messages() {
|
|
|
|
popovers.hide_topic_sidebar_popover();
|
|
|
|
$("ul.expanded_private_messages").remove();
|
|
|
|
resize.resize_stream_filters_container();
|
|
|
|
}
|
|
|
|
|
2015-12-11 08:35:36 +01:00
|
|
|
function reset_to_unnarrowed(narrowed_within_same_stream) {
|
|
|
|
if (zoomed_to_topics && narrowed_within_same_stream !== true) {
|
|
|
|
zoom_out();
|
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
private_messages_open = false;
|
2015-12-14 02:15:03 +01:00
|
|
|
$("ul.filters li").removeClass('active-filter active-sub-filter');
|
2016-10-27 01:53:47 +02:00
|
|
|
topic_list.remove_expanded_topics();
|
2015-11-25 18:41:32 +01:00
|
|
|
remove_expanded_private_messages();
|
2015-12-11 08:35:36 +01:00
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
function get_private_message_filter_li(conversation) {
|
|
|
|
var pm_li = get_filter_li('global', 'private');
|
|
|
|
return iterate_to_find(".expanded_private_messages li.expanded_private_message",
|
|
|
|
conversation, pm_li);
|
|
|
|
}
|
|
|
|
|
2013-06-12 00:07:35 +02:00
|
|
|
exports.set_in_home_view = function (stream, in_home) {
|
|
|
|
var li = get_filter_li('stream', stream);
|
|
|
|
if (in_home) {
|
|
|
|
li.removeClass("out_of_home_view");
|
|
|
|
} else {
|
|
|
|
li.addClass("out_of_home_view");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-01-16 23:14:55 +01:00
|
|
|
function build_stream_sidebar_row(name) {
|
2014-02-07 16:55:54 +01:00
|
|
|
var sub = stream_data.get_sub(name);
|
2013-05-30 20:50:32 +02:00
|
|
|
var args = {name: name,
|
2014-02-07 16:55:54 +01:00
|
|
|
id: sub.stream_id,
|
2013-05-30 20:50:32 +02:00
|
|
|
uri: narrow.by_stream_uri(name),
|
2013-08-15 21:11:07 +02:00
|
|
|
not_in_home_view: (stream_data.in_home_view(name) === false),
|
2014-02-07 16:55:54 +01:00
|
|
|
invite_only: sub.invite_only,
|
2016-07-01 07:26:09 +02:00
|
|
|
color: stream_data.get_color(name),
|
|
|
|
pin_to_top: sub.pin_to_top
|
2013-08-27 21:05:32 +02:00
|
|
|
};
|
|
|
|
args.dark_background = stream_color.get_color_class(args.color);
|
2013-06-12 18:03:16 +02:00
|
|
|
var list_item = $(templates.render('stream_sidebar_row', args));
|
2014-01-16 23:09:46 +01:00
|
|
|
$("#stream_filters").append(list_item);
|
2013-05-06 23:36:22 +02:00
|
|
|
return list_item;
|
2013-05-06 22:18:01 +02:00
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
|
2016-10-17 20:02:32 +02:00
|
|
|
exports.create_sidebar_row = function (sub) {
|
|
|
|
var stream_name = sub.name;
|
|
|
|
|
2014-01-16 22:59:30 +01:00
|
|
|
if (exports.get_stream_li(stream_name).length) {
|
2013-10-21 22:26:19 +02:00
|
|
|
// already exists
|
2016-10-17 20:02:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
var li = build_stream_sidebar_row(stream_name);
|
|
|
|
if (li) {
|
|
|
|
sub.sidebar_li = li;
|
2013-10-21 22:26:19 +02:00
|
|
|
}
|
2014-01-16 21:38:40 +01:00
|
|
|
};
|
|
|
|
|
2014-01-17 18:23:39 +01:00
|
|
|
exports.redraw_stream_privacy = function (stream_name) {
|
|
|
|
var li = exports.get_stream_li(stream_name);
|
|
|
|
var div = li.find('.stream-privacy');
|
2014-02-18 19:47:37 +01:00
|
|
|
var swatch = li.find('.streamlist_swatch');
|
2014-01-17 18:23:39 +01:00
|
|
|
var sub = stream_data.get_sub(stream_name);
|
|
|
|
var color = stream_data.get_color(stream_name);
|
|
|
|
var dark_background = stream_color.get_color_class(color);
|
|
|
|
|
|
|
|
var args = {
|
|
|
|
invite_only: sub.invite_only,
|
|
|
|
dark_background: dark_background
|
|
|
|
};
|
|
|
|
|
2014-02-18 19:47:37 +01:00
|
|
|
if (sub.invite_only) {
|
|
|
|
swatch.addClass("private-stream-swatch");
|
2016-06-09 23:02:49 +02:00
|
|
|
} else {
|
2014-02-18 19:47:37 +01:00
|
|
|
swatch.removeClass("private-stream-swatch");
|
|
|
|
}
|
|
|
|
|
2014-01-17 18:23:39 +01:00
|
|
|
var html = templates.render('stream_privacy', args);
|
|
|
|
div.html(html);
|
|
|
|
};
|
|
|
|
|
2014-01-16 21:38:40 +01:00
|
|
|
exports.get_stream_li = function (stream_name) {
|
|
|
|
return get_filter_li('stream', stream_name);
|
|
|
|
};
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
exports.get_count = function (type, name) {
|
2013-05-06 22:18:01 +02:00
|
|
|
return get_filter_li(type, name).find('.count .value').text();
|
2013-05-06 02:54:15 +02:00
|
|
|
};
|
|
|
|
|
2013-05-16 22:38:09 +02:00
|
|
|
function update_count_in_dom(count_span, value_span, count) {
|
2013-05-07 20:02:24 +02:00
|
|
|
if (count === 0) {
|
2013-05-16 22:38:09 +02:00
|
|
|
count_span.hide();
|
2013-09-06 07:39:40 +02:00
|
|
|
if (count_span.parent().hasClass("subscription_block")) {
|
|
|
|
count_span.parent(".subscription_block").removeClass("stream-with-count");
|
2016-06-09 23:05:34 +02:00
|
|
|
} else if (count_span.parent().hasClass("user_sidebar_entry")) {
|
2013-09-06 07:39:40 +02:00
|
|
|
count_span.parent(".user_sidebar_entry").removeClass("user-with-count");
|
2016-06-09 23:05:34 +02:00
|
|
|
} else if (count_span.parent().hasClass("group-pms-sidebar-entry")) {
|
2013-10-18 00:56:49 +02:00
|
|
|
count_span.parent(".group-pms-sidebar-entry").removeClass("group-with-count");
|
|
|
|
}
|
2013-05-16 22:38:09 +02:00
|
|
|
value_span.text('');
|
|
|
|
return;
|
2013-05-07 20:02:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
count_span.show();
|
2013-09-06 07:39:40 +02:00
|
|
|
|
|
|
|
if (count_span.parent().hasClass("subscription_block")) {
|
|
|
|
count_span.parent(".subscription_block").addClass("stream-with-count");
|
2016-06-09 23:05:34 +02:00
|
|
|
} else if (count_span.parent().hasClass("user_sidebar_entry")) {
|
2013-09-06 07:39:40 +02:00
|
|
|
count_span.parent(".user_sidebar_entry").addClass("user-with-count");
|
2016-06-09 23:05:34 +02:00
|
|
|
} else if (count_span.parent().hasClass("group-pms-sidebar-entry")) {
|
2013-10-18 00:56:49 +02:00
|
|
|
count_span.parent(".group-pms-sidebar-entry").addClass("group-with-count");
|
|
|
|
}
|
2013-05-07 20:02:24 +02:00
|
|
|
value_span.text(count);
|
|
|
|
}
|
|
|
|
|
2013-08-22 21:05:18 +02:00
|
|
|
function set_count(type, name, count) {
|
2013-05-06 22:18:01 +02:00
|
|
|
var count_span = get_filter_li(type, name).find('.count');
|
2013-05-06 02:54:15 +02:00
|
|
|
var value_span = count_span.find('.value');
|
2013-05-16 22:38:09 +02:00
|
|
|
update_count_in_dom(count_span, value_span, count);
|
2013-08-22 21:05:18 +02:00
|
|
|
}
|
2013-05-07 20:02:24 +02:00
|
|
|
|
2013-09-03 17:40:34 +02:00
|
|
|
function set_count_toggle_button(elem, count) {
|
|
|
|
if (count === 0) {
|
|
|
|
if (elem.is(':animated')) {
|
|
|
|
return elem.stop(true, true).hide();
|
|
|
|
}
|
|
|
|
return elem.hide(500);
|
|
|
|
} else if ((count > 0) && (count < 1000)) {
|
|
|
|
elem.show(500);
|
|
|
|
return elem.text(count);
|
|
|
|
} else {
|
|
|
|
elem.show(500);
|
|
|
|
return elem.text("1k+");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
exports.set_pm_conversation_count = function (conversation, count) {
|
|
|
|
var pm_li = get_private_message_filter_li(conversation);
|
|
|
|
var count_span = pm_li.find('.private_message_count');
|
|
|
|
var value_span = count_span.find('.value');
|
|
|
|
|
|
|
|
if (count_span.length === 0 || value_span.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
count_span.removeClass("zero_count");
|
|
|
|
update_count_in_dom(count_span, value_span, count);
|
|
|
|
};
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
exports.remove_narrow_filter = function (name, type) {
|
2013-05-06 22:18:01 +02:00
|
|
|
get_filter_li(type, name).remove();
|
2013-05-06 02:54:15 +02:00
|
|
|
};
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
exports._build_private_messages_list = function (active_conversation, max_private_messages) {
|
|
|
|
|
|
|
|
var private_messages = message_store.recent_private_messages || [];
|
|
|
|
var display_messages = [];
|
|
|
|
var hiding_messages = false;
|
|
|
|
|
|
|
|
_.each(private_messages, function (private_message_obj, idx) {
|
|
|
|
var recipients_string = private_message_obj.display_reply_to;
|
|
|
|
var replies_to = private_message_obj.reply_to;
|
|
|
|
var num_unread = unread.num_unread_for_person(private_message_obj.reply_to);
|
|
|
|
|
|
|
|
var always_visible = (idx < max_private_messages) || (num_unread > 0)
|
|
|
|
|| (replies_to === active_conversation);
|
|
|
|
|
|
|
|
if (!always_visible) {
|
|
|
|
hiding_messages = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
var display_message = {
|
|
|
|
recipients: recipients_string,
|
|
|
|
reply_to: replies_to,
|
|
|
|
unread: num_unread,
|
|
|
|
is_zero: num_unread === 0,
|
|
|
|
zoom_out_hide: !always_visible,
|
|
|
|
url: narrow.pm_with_uri(private_message_obj.reply_to)
|
|
|
|
};
|
|
|
|
display_messages.push(display_message);
|
|
|
|
});
|
|
|
|
|
|
|
|
var recipients_dom = templates.render('sidebar_private_message_list',
|
|
|
|
{messages: display_messages,
|
|
|
|
want_show_more_messages_links: hiding_messages});
|
|
|
|
return recipients_dom;
|
|
|
|
};
|
|
|
|
|
2016-08-27 04:08:43 +02:00
|
|
|
function rebuild_recent_topics(stream, active_topic) {
|
|
|
|
// TODO: Call rebuild_recent_topics less, not on every new
|
2013-11-22 18:50:10 +01:00
|
|
|
// message.
|
|
|
|
var stream_li = get_filter_li('stream', stream);
|
2016-10-27 02:33:24 +02:00
|
|
|
topic_list.rebuild(stream_li, stream, active_topic);
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
function rebuild_recent_private_messages(active_conversation) {
|
|
|
|
remove_expanded_private_messages();
|
|
|
|
if (private_messages_open)
|
|
|
|
{
|
|
|
|
var max_private_messages = 5;
|
|
|
|
var private_li = get_filter_li('global', 'private');
|
|
|
|
var private_messages_dom = exports._build_private_messages_list(active_conversation,
|
|
|
|
max_private_messages);
|
|
|
|
private_li.append(private_messages_dom);
|
|
|
|
}
|
|
|
|
if (active_conversation) {
|
|
|
|
get_private_message_filter_li(active_conversation).addClass('active-sub-filter');
|
|
|
|
}
|
|
|
|
|
|
|
|
resize.resize_stream_filters_container();
|
|
|
|
}
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
exports.update_streams_sidebar = function () {
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list();
|
2013-05-06 02:54:15 +02:00
|
|
|
|
|
|
|
if (! narrow.active()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var op_stream = narrow.filter().operands('stream');
|
2013-07-16 22:52:02 +02:00
|
|
|
var op_subject = narrow.filter().operands('topic');
|
2013-05-06 02:54:15 +02:00
|
|
|
var subject;
|
|
|
|
if (op_stream.length !== 0) {
|
|
|
|
if (op_subject.length !== 0) {
|
|
|
|
subject = op_subject[0];
|
|
|
|
}
|
2013-08-15 21:11:07 +02:00
|
|
|
if (stream_data.is_subscribed(op_stream[0])) {
|
2016-08-27 04:08:43 +02:00
|
|
|
rebuild_recent_topics(op_stream[0], subject);
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
exports.update_private_messages = function () {
|
|
|
|
exports._build_private_messages_list();
|
|
|
|
|
|
|
|
if (! narrow.active()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var is_pm_filter = _.contains(narrow.filter().operands('is'), "private");
|
|
|
|
var conversation = narrow.filter().operands('pm-with');
|
|
|
|
if (conversation.length === 1) {
|
|
|
|
rebuild_recent_private_messages(conversation[0]);
|
|
|
|
} else if (conversation.length !== 0) {
|
|
|
|
// TODO: This should be the reply-to of the thread.
|
|
|
|
rebuild_recent_private_messages("");
|
|
|
|
} else if (is_pm_filter) {
|
|
|
|
rebuild_recent_private_messages("");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-31 19:07:59 +02:00
|
|
|
function do_new_messages_animation(message_type) {
|
|
|
|
var li = get_filter_li("global", message_type);
|
|
|
|
li.addClass("new_messages");
|
2013-05-16 23:40:07 +02:00
|
|
|
function mid_animation() {
|
2013-05-31 19:07:59 +02:00
|
|
|
li.removeClass("new_messages");
|
|
|
|
li.addClass("new_messages_fadeout");
|
2013-05-16 23:40:07 +02:00
|
|
|
}
|
|
|
|
function end_animation() {
|
2013-05-31 19:07:59 +02:00
|
|
|
li.removeClass("new_messages_fadeout");
|
2013-05-16 23:40:07 +02:00
|
|
|
}
|
|
|
|
setTimeout(mid_animation, 3000);
|
|
|
|
setTimeout(end_animation, 6000);
|
|
|
|
}
|
|
|
|
|
|
|
|
function animate_private_message_changes(new_private_message_count) {
|
|
|
|
if (new_private_message_count > last_private_message_count) {
|
2013-07-10 03:22:34 +02:00
|
|
|
do_new_messages_animation('private');
|
2013-05-16 23:40:07 +02:00
|
|
|
}
|
|
|
|
last_private_message_count = new_private_message_count;
|
|
|
|
}
|
|
|
|
|
2013-05-31 19:07:59 +02:00
|
|
|
function animate_mention_changes(new_mention_count) {
|
|
|
|
if (new_mention_count > last_mention_count) {
|
2013-07-10 03:07:01 +02:00
|
|
|
do_new_messages_animation('mentioned');
|
2013-05-31 19:07:59 +02:00
|
|
|
}
|
|
|
|
last_mention_count = new_mention_count;
|
|
|
|
}
|
|
|
|
|
2013-08-22 19:06:04 +02:00
|
|
|
|
2013-08-22 21:28:34 +02:00
|
|
|
exports.set_presence_list_count = function (person, count) {
|
|
|
|
set_count("private", person, count);
|
2013-08-22 19:06:04 +02:00
|
|
|
};
|
|
|
|
|
2013-05-14 03:58:07 +02:00
|
|
|
exports.update_dom_with_unread_counts = function (counts) {
|
|
|
|
// counts is just a data object that gets calculated elsewhere
|
|
|
|
// Our job is to update some DOM elements.
|
|
|
|
|
|
|
|
// counts.stream_count maps streams to counts
|
2013-08-15 16:16:12 +02:00
|
|
|
counts.stream_count.each(function (count, stream) {
|
2013-08-22 21:05:18 +02:00
|
|
|
set_count("stream", stream, count);
|
2013-05-14 03:58:07 +02:00
|
|
|
});
|
|
|
|
|
2016-10-28 19:07:25 +02:00
|
|
|
// counts.subject_count maps streams to hashes of topics to counts
|
2013-08-15 16:16:12 +02:00
|
|
|
counts.subject_count.each(function (subject_hash, stream) {
|
2016-10-27 01:36:20 +02:00
|
|
|
var stream_li = get_filter_li('stream', stream);
|
2013-08-15 16:16:12 +02:00
|
|
|
subject_hash.each(function (count, subject) {
|
2016-10-27 01:36:20 +02:00
|
|
|
topic_list.set_count(stream_li, subject, count);
|
2013-05-14 03:58:07 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-08-22 21:28:34 +02:00
|
|
|
counts.pm_count.each(function (count, person) {
|
|
|
|
exports.set_presence_list_count(person, count);
|
2015-11-25 18:41:32 +01:00
|
|
|
exports.set_pm_conversation_count(person, count);
|
2013-08-22 21:28:34 +02:00
|
|
|
});
|
2013-06-05 20:29:31 +02:00
|
|
|
|
2013-05-14 03:58:07 +02:00
|
|
|
// integer counts
|
2013-08-22 21:05:18 +02:00
|
|
|
set_count("global", "private", counts.private_message_count);
|
|
|
|
set_count("global", "mentioned", counts.mentioned_message_count);
|
|
|
|
set_count("global", "home", counts.home_unread_messages);
|
2013-05-16 23:40:07 +02:00
|
|
|
|
2013-09-03 17:40:34 +02:00
|
|
|
set_count_toggle_button($("#streamlist-toggle-unreadcount"), counts.home_unread_messages);
|
|
|
|
set_count_toggle_button($("#userlist-toggle-unreadcount"), counts.private_message_count);
|
|
|
|
|
2013-05-16 23:40:07 +02:00
|
|
|
animate_private_message_changes(counts.private_message_count);
|
2013-05-31 19:07:59 +02:00
|
|
|
animate_mention_changes(counts.mentioned_message_count);
|
2013-05-14 03:58:07 +02:00
|
|
|
};
|
|
|
|
|
2013-10-21 22:26:19 +02:00
|
|
|
exports.rename_stream = function (sub) {
|
2014-01-16 23:14:55 +01:00
|
|
|
sub.sidebar_li = build_stream_sidebar_row(sub.name);
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list(); // big hammer
|
2013-10-21 22:26:19 +02:00
|
|
|
};
|
|
|
|
|
2016-10-26 06:56:10 +02:00
|
|
|
exports.refresh_pinned_or_unpinned_stream = function (sub) {
|
|
|
|
// Pinned/unpinned streams require re-ordering.
|
|
|
|
// We use kind of brute force now, which is probably fine.
|
2016-07-01 07:26:09 +02:00
|
|
|
sub.sidebar_li = build_stream_sidebar_row(sub.name);
|
|
|
|
exports.build_stream_list();
|
|
|
|
exports.update_streams_sidebar();
|
|
|
|
};
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
$(function () {
|
2016-10-27 03:47:05 +02:00
|
|
|
// TODO, Eventually topic_list won't be a big singleton,
|
|
|
|
// and we can create more component-based click handlers for
|
|
|
|
// each stream.
|
|
|
|
topic_list.set_click_handlers({
|
|
|
|
zoom_in: zoom_in,
|
|
|
|
zoom_out: zoom_out
|
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('narrow_activated.zulip', function (event) {
|
2015-12-11 08:35:36 +01:00
|
|
|
reset_to_unnarrowed(active_stream_name() === zoomed_stream);
|
2013-05-06 02:54:15 +02:00
|
|
|
|
|
|
|
// TODO: handle confused filters like "in:all stream:foo"
|
|
|
|
var op_in = event.filter.operands('in');
|
|
|
|
if (op_in.length !== 0) {
|
|
|
|
if (['all', 'home'].indexOf(op_in[0]) !== -1) {
|
|
|
|
$("#global_filters li[data-name='" + op_in[0] + "']").addClass('active-filter');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var op_is = event.filter.operands('is');
|
|
|
|
if (op_is.length !== 0) {
|
2015-11-25 18:41:32 +01:00
|
|
|
if (['starred', 'mentioned'].indexOf(op_is[0]) !== -1) {
|
2013-05-06 02:54:15 +02:00
|
|
|
$("#global_filters li[data-name='" + op_is[0] + "']").addClass('active-filter');
|
|
|
|
}
|
|
|
|
}
|
2015-11-25 18:41:32 +01:00
|
|
|
|
|
|
|
var op_pm = event.filter.operands('pm-with');
|
|
|
|
if ((op_is.length !== 0 && _.contains(op_is, "private")) || op_pm.length !== 0) {
|
|
|
|
private_messages_open = true;
|
|
|
|
if (op_pm.length === 1) {
|
|
|
|
$("#user_presences li[data-email='" + op_pm[0] + "']").addClass('active-filter');
|
|
|
|
rebuild_recent_private_messages(op_pm[0]);
|
|
|
|
} else if (op_pm.length !== 0) {
|
|
|
|
// TODO: Should pass the reply-to of the thread
|
|
|
|
rebuild_recent_private_messages("");
|
|
|
|
} else {
|
|
|
|
$("#global_filters li[data-name='private']").addClass('active-filter zoom-out');
|
|
|
|
rebuild_recent_private_messages("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
var op_stream = event.filter.operands('stream');
|
2013-08-15 21:11:07 +02:00
|
|
|
if (op_stream.length !== 0 && stream_data.is_subscribed(op_stream[0])) {
|
2013-05-06 22:18:01 +02:00
|
|
|
var stream_li = get_filter_li('stream', op_stream[0]);
|
2013-07-16 22:52:02 +02:00
|
|
|
var op_subject = event.filter.operands('topic');
|
2013-05-06 02:54:15 +02:00
|
|
|
var subject;
|
|
|
|
if (op_subject.length !== 0) {
|
|
|
|
subject = op_subject[0];
|
|
|
|
} else {
|
|
|
|
stream_li.addClass('active-filter');
|
|
|
|
}
|
2016-08-27 04:08:43 +02:00
|
|
|
rebuild_recent_topics(op_stream[0], subject);
|
2014-01-31 17:44:21 +01:00
|
|
|
unread.process_visible();
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('narrow_deactivated.zulip', function (event) {
|
2015-12-11 08:35:36 +01:00
|
|
|
reset_to_unnarrowed();
|
2013-05-06 02:54:15 +02:00
|
|
|
$("#global_filters li[data-name='home']").addClass('active-filter');
|
|
|
|
});
|
2013-05-06 02:54:15 +02:00
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('sub_obj_created.zulip', function (event) {
|
2013-05-06 02:54:15 +02:00
|
|
|
if (event.sub.subscribed) {
|
2016-10-17 20:02:32 +02:00
|
|
|
exports.create_sidebar_row(event.sub);
|
2013-05-06 02:54:15 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('subscription_add_done.zulip', function (event) {
|
2016-10-17 20:02:32 +02:00
|
|
|
exports.create_sidebar_row(event.sub);
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list();
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('subscription_remove_done.zulip', function (event) {
|
2013-05-06 02:54:15 +02:00
|
|
|
var stream_name = event.sub.name;
|
|
|
|
exports.remove_narrow_filter(stream_name, 'stream');
|
2013-05-13 17:23:07 +02:00
|
|
|
// We need to make sure we resort if the removed sub gets added again
|
|
|
|
previous_sort_order = undefined;
|
2016-07-01 07:26:09 +02:00
|
|
|
previous_unpinned_order = undefined;
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
2013-11-22 22:25:31 +01:00
|
|
|
|
2015-11-25 18:41:32 +01:00
|
|
|
$('#global_filters').on('click', '.show-more-private-messages', function (e) {
|
|
|
|
popovers.hide_all();
|
|
|
|
$(".expanded_private_messages").expectOne().removeClass("zoom-out").addClass("zoom-in");
|
|
|
|
$(".expanded_private_messages li.expanded_private_message").each(function () {
|
|
|
|
$(this).show();
|
|
|
|
});
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
2013-11-22 22:25:31 +01:00
|
|
|
$('#stream_filters').on('click', 'li .subscription_block', function (e) {
|
|
|
|
if (e.metaKey || e.ctrlKey) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ui.home_tab_obscured()) {
|
|
|
|
ui.change_tab_to('#home');
|
|
|
|
}
|
|
|
|
var stream = $(e.target).parents('li').attr('data-name');
|
2013-11-22 21:04:49 +01:00
|
|
|
|
2013-11-22 22:25:31 +01:00
|
|
|
narrow.by('stream', stream, {select_first_unread: true, trigger: 'sidebar'});
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
function actually_update_streams_for_search() {
|
|
|
|
exports.update_streams_sidebar();
|
|
|
|
resize.resize_page_components();
|
|
|
|
}
|
|
|
|
|
|
|
|
var update_streams_for_search = _.throttle(actually_update_streams_for_search, 50);
|
|
|
|
|
|
|
|
exports.searching = function () {
|
|
|
|
return $('.stream-list-filter').expectOne().is(':focus');
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.escape_search = function () {
|
|
|
|
var filter = $('.stream-list-filter').expectOne();
|
|
|
|
if (filter.val() === '') {
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filter.val('');
|
|
|
|
update_streams_for_search();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.initiate_search = function () {
|
|
|
|
var filter = $('.stream-list-filter').expectOne();
|
|
|
|
filter.removeClass('notdisplayed');
|
|
|
|
filter.focus();
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.clear_and_hide_search = function () {
|
|
|
|
var filter = $('.stream-list-filter');
|
|
|
|
if (filter.val() !== '') {
|
|
|
|
filter.val('');
|
|
|
|
update_streams_for_search();
|
|
|
|
}
|
|
|
|
filter.blur();
|
|
|
|
filter.addClass('notdisplayed');
|
|
|
|
};
|
|
|
|
|
|
|
|
function focus_stream_filter (e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
|
|
|
|
function maybe_select_stream (e) {
|
|
|
|
if (e.keyCode === 13) {
|
|
|
|
// Enter key was pressed
|
|
|
|
|
|
|
|
var topStream = $('#stream_filters li.narrow-filter').first().data('name');
|
|
|
|
if (topStream !== undefined) {
|
|
|
|
// undefined if there are no results
|
|
|
|
if (ui.home_tab_obscured()) {
|
|
|
|
ui.change_tab_to('#home');
|
|
|
|
}
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
narrow.by('stream', topStream, {select_first_unread: true, trigger: 'sidebar enter key'});
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggle_filter_displayed(e) {
|
|
|
|
if (e.target.id === 'streams_inline_cog') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (0 === $('.stream-list-filter.notdisplayed').length) {
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
} else {
|
|
|
|
exports.initiate_search();
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
$(".stream-list-filter").expectOne()
|
|
|
|
.on('click', focus_stream_filter)
|
|
|
|
.on('input', update_streams_for_search)
|
|
|
|
.on('keydown', maybe_select_stream);
|
|
|
|
});
|
|
|
|
|
|
|
|
$(function () {
|
|
|
|
$("#streams_header").expectOne()
|
|
|
|
.on('click', toggle_filter_displayed);
|
|
|
|
});
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
return exports;
|
|
|
|
}());
|
2013-11-26 16:39:58 +01:00
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = stream_list;
|
|
|
|
}
|