2020-08-01 03:43:15 +02:00
|
|
|
"use strict";
|
|
|
|
|
2020-07-25 02:02:35 +02:00
|
|
|
const _ = require("lodash");
|
|
|
|
|
2020-07-25 23:59:49 +02:00
|
|
|
const generated_emoji_codes = require("../generated/emoji/emoji_codes.json");
|
2020-09-26 06:02:29 +02:00
|
|
|
const generated_pygments_data = require("../generated/pygments_data.json");
|
2020-07-25 00:12:17 +02:00
|
|
|
const emoji = require("../shared/js/emoji");
|
2020-09-26 06:02:29 +02:00
|
|
|
const fenced_code = require("../shared/js/fenced_code");
|
2020-07-25 14:35:56 +02:00
|
|
|
const render_edit_content_button = require("../templates/edit_content_button.hbs");
|
2020-07-25 23:59:49 +02:00
|
|
|
|
2021-02-10 17:01:31 +01:00
|
|
|
const alert_words = require("./alert_words");
|
2021-02-28 01:00:56 +01:00
|
|
|
const click_handlers = require("./click_handlers");
|
2021-02-28 00:51:57 +01:00
|
|
|
const compose = require("./compose");
|
2021-02-28 00:38:58 +01:00
|
|
|
const compose_pm_pill = require("./compose_pm_pill");
|
2021-02-28 00:56:48 +01:00
|
|
|
const condense = require("./condense");
|
2021-02-10 16:58:56 +01:00
|
|
|
const copy_and_paste = require("./copy_and_paste");
|
2021-02-28 00:37:57 +01:00
|
|
|
const drafts = require("./drafts");
|
2021-02-10 16:53:37 +01:00
|
|
|
const echo = require("./echo");
|
2020-02-21 00:20:39 +01:00
|
|
|
const emojisets = require("./emojisets");
|
2021-02-28 01:02:12 +01:00
|
|
|
const gear_menu = require("./gear_menu");
|
2021-02-28 01:07:47 +01:00
|
|
|
const hashchange = require("./hashchange");
|
2021-02-10 17:00:58 +01:00
|
|
|
const invite = require("./invite");
|
2021-02-28 00:58:23 +01:00
|
|
|
const lightbox = require("./lightbox");
|
2021-02-28 00:49:36 +01:00
|
|
|
const markdown = require("./markdown");
|
2020-07-15 01:29:15 +02:00
|
|
|
const markdown_config = require("./markdown_config");
|
2021-02-28 00:56:21 +01:00
|
|
|
const message_edit = require("./message_edit");
|
2021-02-28 00:59:17 +01:00
|
|
|
const message_scroll = require("./message_scroll");
|
2021-02-28 00:42:00 +01:00
|
|
|
const message_viewport = require("./message_viewport");
|
2021-02-28 00:41:04 +01:00
|
|
|
const muting = require("./muting");
|
2021-02-28 01:05:26 +01:00
|
|
|
const navigate = require("./navigate");
|
2021-02-28 01:06:34 +01:00
|
|
|
const notifications = require("./notifications");
|
2021-02-28 01:03:46 +01:00
|
|
|
const overlays = require("./overlays");
|
2020-08-20 21:24:06 +02:00
|
|
|
const people = require("./people");
|
2020-08-18 19:53:22 +02:00
|
|
|
const pm_conversations = require("./pm_conversations");
|
2021-02-28 00:44:12 +01:00
|
|
|
const pm_list = require("./pm_list");
|
2021-02-28 00:48:40 +01:00
|
|
|
const reload = require("./reload");
|
2021-02-28 00:57:20 +01:00
|
|
|
const resize = require("./resize");
|
2021-02-28 00:42:30 +01:00
|
|
|
const rows = require("./rows");
|
2021-02-28 01:01:53 +01:00
|
|
|
const scroll_bar = require("./scroll_bar");
|
2021-02-28 01:04:58 +01:00
|
|
|
const search = require("./search");
|
2021-02-28 00:50:19 +01:00
|
|
|
const sent_messages = require("./sent_messages");
|
2021-02-28 01:01:14 +01:00
|
|
|
const settings_panel_menu = require("./settings_panel_menu");
|
2021-02-28 01:01:33 +01:00
|
|
|
const settings_toggle = require("./settings_toggle");
|
2021-02-10 17:10:39 +01:00
|
|
|
const spoilers = require("./spoilers");
|
2021-02-28 00:53:05 +01:00
|
|
|
const stream_color = require("./stream_color");
|
2021-02-28 00:53:59 +01:00
|
|
|
const stream_data = require("./stream_data");
|
2021-02-28 00:55:38 +01:00
|
|
|
const stream_edit = require("./stream_edit");
|
2021-02-28 00:43:33 +01:00
|
|
|
const topic_list = require("./topic_list");
|
2021-02-10 16:51:01 +01:00
|
|
|
const topic_zoom = require("./topic_zoom");
|
2021-02-10 17:04:41 +01:00
|
|
|
const tutorial = require("./tutorial");
|
2021-02-10 17:08:06 +01:00
|
|
|
const typing = require("./typing");
|
2021-02-28 01:00:36 +01:00
|
|
|
const ui_util = require("./ui_util");
|
2021-02-28 00:40:18 +01:00
|
|
|
const unread_ui = require("./unread_ui");
|
2021-02-28 00:42:57 +01:00
|
|
|
const user_groups = require("./user_groups");
|
2021-02-10 17:03:21 +01:00
|
|
|
const user_status = require("./user_status");
|
2021-02-10 17:03:40 +01:00
|
|
|
const user_status_ui = require("./user_status_ui");
|
markdown: Add helper configuration for mobile.
This refactoring is the first step toward sharing
our markdown code with mobile. This focuses on
the Zulip layer, not the underlying third party `marked`
library.
In this commit we do a one-time initialization to
wire up the markdown functions, but after further
discussions with Greg, it might make more sense
to just pass in helpers on every use of markdown
(which is generally only once per sent message).
I'll address that in follow-up commits.
Even though it looks like a pretty invasive change,
you will note that we barely needed to modify the
node tests to make this pass. And we have pretty
decent test coverage here.
All of the places where we used to depend on
other Zulip modules now use helper functions that
any client (e.g. mobile) can configure themselves.
Or course, in the webapp, we configure these from
modules like people/stream_data/hash_util/etc.
Even in places where markdown used to deal directly with
data structures from other modules, we now use functions.
We may revisit this in a future commit, and we might
just pass data directly for certain things.
I decided to keep the helpers data structure completely flat,
so we don't have ugly nested names like
`helpers.emoji.get_emoji_codepoint`. Because of this,
some of the names aren't 1:1, which I think is fine.
For example, we map `user_groups.is_member_of` to
`is_member_of_user_group`.
It's likely that mobile already has different names
for their versions of these functions, so trying for
fake consistency would only help the webapp. In some
cases, I think the webapp functions have names that
could be improved, but we can clean that up in future
commits, and since the names aren't coupled to markdown
itself (i.e. only the config), we will be less
constrained.
It's worth noting that `marked` has an `options`
data structure that it uses for configuration, but
I didn't piggyback onto it, since the `marked`
options are more at the lexing/parsing layer vs.
the app-data layer stuff that our helpers mostly
help with.
Hopefully it's obvious why I just put helpers in
the top-level namespace for the module rather than
passing it around through multiple layers of the
parser.
There were a couple places in markdown where we
were doing awkward `hasOwnProperty` checks for
emoji-related stuff. Now we use the Python
principle of ask-forgiveness-not-permission and
just handle the getters returning falsy data. (It
should be `undefined`, but any falsy value is
unworkable in the places I changed, so I use
the simpler, less brittle form.)
We also break our direct dependency on
`emoji_codes.json` (with some help from the
prior commit).
In one place I rename streamName to stream_name,
fixing up an ancient naming violation that goes
way back to before this code was even extracted
away from echo.js. I didn't bother to split this
out into a separate commit, since 2 of the 4
lines would be immediately re-modified in the
subsequent commit.
Note that we still depend on `fenced_code`
via the global namespace, instead of simply
requiring it directly or injecting it. The
reason I'm postponing any action there is that
we'll have to change things once we move
markdown into a shared library. (The most
likely outcome is that we'll rename/move both files
at the same time and fix the namespace/require
details as part of that commit.)
Also the markdown code still relies on `_` being
available in the global namespace. We aren't
quite ready to share code with mobile yet, but the
underscore dependency should not be problematic,
since mobile already uses underscore to use the
webapp's shared typing_status module.
2020-02-13 13:54:11 +01:00
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
// This is where most of our initialization takes place.
|
|
|
|
// TODO: Organize it a lot better. In particular, move bigger
|
|
|
|
// functions to other modules.
|
|
|
|
|
|
|
|
/* We use 'visibility' rather than 'display' and jQuery's show() / hide(),
|
|
|
|
because we want to reserve space for the email address. This avoids
|
|
|
|
things jumping around slightly when the email address is shown. */
|
|
|
|
|
2019-11-02 00:06:25 +01:00
|
|
|
let current_message_hover;
|
2017-03-18 20:29:55 +01:00
|
|
|
function message_unhover() {
|
|
|
|
if (current_message_hover === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
2020-07-15 01:29:15 +02:00
|
|
|
current_message_hover.find("span.edit_content").html("");
|
2017-03-18 20:29:55 +01:00
|
|
|
current_message_hover = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
function message_hover(message_row) {
|
2020-02-12 07:31:25 +01:00
|
|
|
const id = rows.id(message_row);
|
|
|
|
if (current_message_hover && rows.id(current_message_hover) === id) {
|
2017-03-18 20:29:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Don't allow on-hover editing for local-only messages
|
2020-07-15 01:29:15 +02:00
|
|
|
if (message_row.hasClass("local")) {
|
2017-03-18 20:29:55 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-10-26 00:11:05 +02:00
|
|
|
const message = current_msg_list.get(rows.id(message_row));
|
2017-03-18 20:29:55 +01:00
|
|
|
message_unhover();
|
|
|
|
current_message_hover = message_row;
|
|
|
|
|
2018-04-25 23:15:57 +02:00
|
|
|
if (!message_edit.is_topic_editable(message)) {
|
2017-03-18 20:29:55 +01:00
|
|
|
// The actions and reactions icon hover logic is handled entirely by CSS
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-26 20:55:18 +02:00
|
|
|
// But the message edit hover icon is determined by whether the message is still editable
|
2020-07-25 14:35:56 +02:00
|
|
|
const is_message_editable =
|
|
|
|
message_edit.get_editability(message) === message_edit.editability_types.FULL;
|
|
|
|
const args = {
|
|
|
|
is_editable: is_message_editable && !message.status_message,
|
|
|
|
msg_id: id,
|
|
|
|
};
|
|
|
|
message_row.find(".edit_content").html(render_edit_content_button(args));
|
2017-03-18 20:29:55 +01:00
|
|
|
}
|
|
|
|
|
2018-12-26 19:56:19 +01:00
|
|
|
exports.initialize_kitchen_sink_stuff = function () {
|
2018-05-15 22:03:14 +02:00
|
|
|
// TODO:
|
|
|
|
// This function is a historical dumping ground
|
|
|
|
// for lots of miscellaneous setup. Almost all of
|
|
|
|
// the code here can probably be moved to more
|
|
|
|
// specific-purpose modules like message_viewport.js.
|
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
const throttled_mousewheelhandler = _.throttle((e, delta) => {
|
2017-03-18 20:29:55 +01:00
|
|
|
// Most of the mouse wheel's work will be handled by the
|
|
|
|
// scroll handler, but when we're at the top or bottom of the
|
|
|
|
// page, the pointer may still need to move.
|
|
|
|
|
2020-12-23 01:18:26 +01:00
|
|
|
if (delta < 0 && message_viewport.at_top()) {
|
|
|
|
navigate.up();
|
2020-12-22 11:26:39 +01:00
|
|
|
} else if (delta > 0 && message_viewport.at_bottom()) {
|
|
|
|
navigate.down();
|
2017-03-18 20:29:55 +01:00
|
|
|
}
|
|
|
|
|
2018-08-04 08:33:00 +02:00
|
|
|
message_viewport.set_last_movement_direction(delta);
|
2018-05-16 20:42:42 +02:00
|
|
|
}, 50);
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
message_viewport.message_pane.on("wheel", (e) => {
|
2019-11-02 00:06:25 +01:00
|
|
|
const delta = e.originalEvent.deltaY;
|
2020-09-15 07:45:17 +02:00
|
|
|
if (!overlays.is_active() && !recent_topics.is_visible()) {
|
2017-03-18 20:29:55 +01:00
|
|
|
// In the message view, we use a throttled mousewheel handler.
|
|
|
|
throttled_mousewheelhandler(e, delta);
|
|
|
|
}
|
|
|
|
// If in a modal, we neither handle the event nor
|
|
|
|
// preventDefault, allowing the modal to scroll normally.
|
|
|
|
});
|
|
|
|
|
2020-07-20 21:26:58 +02:00
|
|
|
$(window).on("resize", _.throttle(resize.handler, 50));
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2017-05-27 15:40:54 +02:00
|
|
|
// Scrolling in overlays. input boxes, and other elements that
|
2017-03-18 20:29:55 +01:00
|
|
|
// explicitly scroll should not scroll the main view. Stop
|
|
|
|
// propagation in all cases. Also, ignore the event if the
|
|
|
|
// element is already at the top or bottom. Otherwise we get a
|
|
|
|
// new scroll event on the parent (?).
|
2020-07-15 01:29:15 +02:00
|
|
|
$(".modal-body, .scrolling_list, input, textarea").on("wheel", function (e) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const self = ui.get_scroll_element($(this));
|
|
|
|
const scroll = self.scrollTop();
|
|
|
|
const delta = e.originalEvent.deltaY;
|
2017-03-18 20:29:55 +01:00
|
|
|
|
|
|
|
// The -1 fudge factor is important here due to rounding errors. Better
|
|
|
|
// to err on the side of not scrolling.
|
2019-11-02 00:06:25 +01:00
|
|
|
const max_scroll = self.prop("scrollHeight") - self.innerHeight() - 1;
|
2017-03-18 20:29:55 +01:00
|
|
|
|
|
|
|
e.stopPropagation();
|
2020-07-15 00:34:28 +02:00
|
|
|
if ((delta < 0 && scroll <= 0) || (delta > 0 && scroll >= max_scroll)) {
|
2017-03-18 20:29:55 +01:00
|
|
|
e.preventDefault();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Ignore wheel events in the compose area which weren't already handled above.
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#compose").on("wheel", (e) => {
|
2017-03-18 20:29:55 +01:00
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
});
|
|
|
|
|
|
|
|
// A little hackish, because it doesn't seem to totally get us the
|
|
|
|
// exact right width for the floating_recipient_bar and compose
|
|
|
|
// box, but, close enough for now.
|
|
|
|
resize.handler();
|
|
|
|
|
|
|
|
if (!page_params.left_side_userlist) {
|
|
|
|
$("#navbar-buttons").addClass("right-userlist");
|
|
|
|
}
|
|
|
|
|
2017-07-07 18:15:10 +02:00
|
|
|
if (page_params.high_contrast_mode) {
|
|
|
|
$("body").addClass("high-contrast");
|
|
|
|
}
|
|
|
|
|
2018-05-24 20:53:26 +02:00
|
|
|
if (!page_params.dense_mode) {
|
|
|
|
$("body").addClass("less_dense_mode");
|
|
|
|
} else {
|
|
|
|
$("body").addClass("more_dense_mode");
|
|
|
|
}
|
|
|
|
|
2020-04-02 21:55:54 +02:00
|
|
|
$("#main_div").on("mouseover", ".message_table .message_row", function () {
|
2019-11-02 00:06:25 +01:00
|
|
|
const row = $(this).closest(".message_row");
|
2017-03-18 20:29:55 +01:00
|
|
|
message_hover(row);
|
|
|
|
});
|
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#main_div").on("mouseleave", ".message_table .message_row", () => {
|
2017-03-18 20:29:55 +01:00
|
|
|
message_unhover();
|
|
|
|
});
|
|
|
|
|
2019-03-08 19:14:15 +01:00
|
|
|
$("#main_div").on("mouseover", ".sender_info_hover", function () {
|
2019-11-02 00:06:25 +01:00
|
|
|
const row = $(this).closest(".message_row");
|
2017-03-18 20:29:55 +01:00
|
|
|
row.addClass("sender_name_hovered");
|
|
|
|
});
|
|
|
|
|
2019-03-08 19:14:15 +01:00
|
|
|
$("#main_div").on("mouseout", ".sender_info_hover", function () {
|
2019-11-02 00:06:25 +01:00
|
|
|
const row = $(this).closest(".message_row");
|
2017-03-18 20:29:55 +01:00
|
|
|
row.removeClass("sender_name_hovered");
|
|
|
|
});
|
|
|
|
|
2018-09-30 13:52:38 +02:00
|
|
|
$("#main_div").on("mouseenter", ".youtube-video a", function () {
|
|
|
|
$(this).addClass("fa fa-play");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseleave", ".youtube-video a", function () {
|
|
|
|
$(this).removeClass("fa fa-play");
|
|
|
|
});
|
|
|
|
|
2019-05-02 18:58:39 +02:00
|
|
|
$("#main_div").on("mouseenter", ".embed-video a", function () {
|
2019-11-02 00:06:25 +01:00
|
|
|
const elem = $(this);
|
2019-05-02 18:58:39 +02:00
|
|
|
// Set image height and css vars for play button position, if not done already
|
2019-11-02 00:06:25 +01:00
|
|
|
const setPosition = !elem.data("entered-before");
|
2019-05-02 18:58:39 +02:00
|
|
|
if (setPosition) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const imgW = elem.find("img")[0].width;
|
|
|
|
const imgH = elem.find("img")[0].height;
|
2019-05-02 18:58:39 +02:00
|
|
|
// Ensure height doesn't change on mouse enter
|
2020-04-26 17:19:26 +02:00
|
|
|
elem.css("height", `${imgH}px`);
|
2019-05-02 18:58:39 +02:00
|
|
|
// variables to set play button position
|
2020-04-26 17:19:26 +02:00
|
|
|
const marginLeft = (imgW - 30) / 2;
|
|
|
|
const marginTop = (imgH - 26) / 2;
|
2020-07-15 00:34:28 +02:00
|
|
|
elem.css("--margin-left", `${marginLeft}px`).css("--margin-top", `${marginTop}px`);
|
2019-05-02 18:58:39 +02:00
|
|
|
elem.data("entered-before", true);
|
|
|
|
}
|
|
|
|
elem.addClass("fa fa-play");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#main_div").on("mouseleave", ".embed-video a", function () {
|
|
|
|
$(this).removeClass("fa fa-play");
|
|
|
|
});
|
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
$("#subscriptions_table").on("mouseover", ".subscription_header", function () {
|
|
|
|
$(this).addClass("active");
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#subscriptions_table").on("mouseout", ".subscription_header", function () {
|
|
|
|
$(this).removeClass("active");
|
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
$("#stream_message_recipient_stream").on("blur", function () {
|
2020-05-12 11:02:13 +02:00
|
|
|
ui_util.decorate_stream_bar(this.value, $("#stream-message .message_header_stream"), true);
|
2018-11-13 17:16:02 +01:00
|
|
|
});
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
$(window).on("blur", () => {
|
|
|
|
$(document.body).addClass("window_blurred");
|
2017-03-18 20:29:55 +01:00
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
$(window).on("focus", () => {
|
|
|
|
$(document.body).removeClass("window_blurred");
|
2017-03-18 20:29:55 +01:00
|
|
|
});
|
|
|
|
|
2020-07-15 01:29:15 +02:00
|
|
|
$(document).on("message_selected.zulip", (event) => {
|
2017-03-18 20:29:55 +01:00
|
|
|
if (current_msg_list !== event.msg_list) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event.id === -1) {
|
|
|
|
// If the message list is empty, don't do anything
|
|
|
|
return;
|
|
|
|
}
|
2019-11-02 00:06:25 +01:00
|
|
|
const row = event.msg_list.get_row(event.id);
|
2020-07-15 01:29:15 +02:00
|
|
|
$(".selected_message").removeClass("selected_message");
|
|
|
|
row.addClass("selected_message");
|
2017-03-18 20:29:55 +01:00
|
|
|
|
|
|
|
if (event.then_scroll) {
|
|
|
|
if (row.length === 0) {
|
2019-11-02 00:06:25 +01:00
|
|
|
const row_from_dom = current_msg_list.get_row(event.id);
|
|
|
|
const messages = event.msg_list.all_messages();
|
2017-03-18 20:29:55 +01:00
|
|
|
blueslip.debug("message_selected missing selected row", {
|
2020-09-13 18:29:24 +02:00
|
|
|
previously_selected_id: event.previously_selected_id,
|
2017-03-18 20:29:55 +01:00
|
|
|
selected_id: event.id,
|
|
|
|
selected_idx: event.msg_list.selected_idx(),
|
2018-05-25 14:37:06 +02:00
|
|
|
selected_idx_exact: messages.indexOf(event.msg_list.get(event.id)),
|
2017-03-18 20:29:55 +01:00
|
|
|
render_start: event.msg_list.view._render_win_start,
|
|
|
|
render_end: event.msg_list.view._render_win_end,
|
2018-05-25 14:37:06 +02:00
|
|
|
selected_id_from_idx: messages[event.msg_list.selected_idx()].id,
|
2017-03-18 20:29:55 +01:00
|
|
|
msg_list_sorted: _.isEqual(
|
2020-07-02 01:39:34 +02:00
|
|
|
messages.map((message) => message.id),
|
2020-02-26 09:27:17 +01:00
|
|
|
current_msg_list
|
|
|
|
.all_messages()
|
|
|
|
.map((message) => message.id)
|
|
|
|
.sort(),
|
2017-03-18 20:29:55 +01:00
|
|
|
),
|
|
|
|
found_in_dom: row_from_dom.length,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (event.target_scroll_offset !== undefined) {
|
2018-04-14 01:10:22 +02:00
|
|
|
current_msg_list.view.set_message_offset(event.target_scroll_offset);
|
2017-03-18 20:29:55 +01:00
|
|
|
} else {
|
|
|
|
// Scroll to place the message within the current view;
|
|
|
|
// but if this is the initial placement of the pointer,
|
|
|
|
// just place it in the very center
|
2020-07-15 00:34:28 +02:00
|
|
|
message_viewport.recenter_view(row, {
|
|
|
|
from_scroll: event.from_scroll,
|
2020-09-13 18:29:24 +02:00
|
|
|
force_center: event.previously_selected_id === -1,
|
2020-07-15 00:34:28 +02:00
|
|
|
});
|
2017-03-18 20:29:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$("#main_div").on("mouseenter", ".message_time", (e) => {
|
2019-11-02 00:06:25 +01:00
|
|
|
const time_elem = $(e.target);
|
|
|
|
const row = time_elem.closest(".message_row");
|
|
|
|
const message = current_msg_list.get(rows.id(row));
|
2017-03-18 20:29:55 +01:00
|
|
|
timerender.set_full_datetime(message, time_elem);
|
|
|
|
});
|
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
$("#streams_header h4").tooltip({placement: "right", animation: false});
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
$('#streams_header i[data-toggle="tooltip"]').tooltip({placement: "left", animation: false});
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
$("#userlist-header #userlist-title").tooltip({placement: "right", animation: false});
|
2018-01-04 18:15:42 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
$("#userlist-header #user_filter_icon").tooltip({placement: "left", animation: false});
|
2018-01-04 18:15:42 +01:00
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
$('.message_failed i[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
2017-01-17 20:51:30 +01:00
|
|
|
$('.copy_message[data-toggle="tooltip"]').tooltip();
|
|
|
|
|
2019-06-08 12:15:33 +02:00
|
|
|
// We disable animations here because they can cause the tooltip
|
|
|
|
// to change shape while fading away in weird way.
|
2020-10-19 19:23:03 +02:00
|
|
|
$("#keyboard-icon").tooltip({placement: "left", animation: false});
|
2018-05-08 00:04:58 +02:00
|
|
|
|
2017-04-26 05:14:33 +02:00
|
|
|
$("body").on("mouseover", ".message_edit_content", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).closest(".message_row").find(".copy_message").show();
|
|
|
|
});
|
|
|
|
|
2017-04-26 05:14:33 +02:00
|
|
|
$("body").on("mouseout", ".message_edit_content", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).closest(".message_row").find(".copy_message").hide();
|
|
|
|
});
|
|
|
|
|
2017-03-26 12:15:50 +02:00
|
|
|
$("body").on("mouseenter", ".copy_message", function () {
|
2017-01-17 20:51:30 +01:00
|
|
|
$(this).show();
|
2020-07-15 01:29:15 +02:00
|
|
|
$(this).tooltip("show");
|
2017-01-17 20:51:30 +01:00
|
|
|
});
|
|
|
|
|
2017-03-26 12:15:50 +02:00
|
|
|
$("body").on("mouseleave", ".copy_message", function () {
|
2020-07-15 01:29:15 +02:00
|
|
|
$(this).tooltip("hide");
|
2017-01-17 20:51:30 +01:00
|
|
|
});
|
|
|
|
|
2017-03-18 20:29:55 +01:00
|
|
|
if (!page_params.realm_allow_message_editing) {
|
|
|
|
$("#edit-message-hotkey-help").hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_params.realm_presence_disabled) {
|
|
|
|
$("#user-list").hide();
|
|
|
|
}
|
2018-12-26 19:56:19 +01:00
|
|
|
};
|
2017-03-18 20:29:55 +01:00
|
|
|
|
2018-12-26 19:56:19 +01:00
|
|
|
exports.initialize_everything = function () {
|
2020-02-25 12:16:26 +01:00
|
|
|
/*
|
|
|
|
When we initialize our various modules, a lot
|
|
|
|
of them will consume data from the server
|
|
|
|
in the form of `page_params`.
|
|
|
|
|
|
|
|
The global `page_params` var is basically
|
|
|
|
a massive dictionary with all the information
|
|
|
|
that the client needs to run the app. Here
|
|
|
|
are some examples of what it includes:
|
|
|
|
|
|
|
|
- all of the user's user-specific settings
|
|
|
|
- all realm-specific settings that are
|
|
|
|
pertinent to the user
|
|
|
|
- info about streams/subscribers on the realm
|
|
|
|
- realm settings
|
|
|
|
- info about all the other users
|
|
|
|
- some fairly dynamic data, like which of
|
|
|
|
the other users are "present"
|
|
|
|
|
|
|
|
Except for the actual Zulip messages, basically
|
|
|
|
any data that you see in the app soon after page
|
|
|
|
load comes from `page_params`.
|
|
|
|
|
|
|
|
## Mostly static data
|
|
|
|
|
|
|
|
Now, we mostly leave `page_params` intact through
|
|
|
|
the duration of the app. Most of the data in
|
|
|
|
`page_params` is fairly static in nature, and we
|
|
|
|
will simply update it for basic changes like
|
|
|
|
the following (meant as examples, not gospel):
|
|
|
|
|
|
|
|
- I changed my 24-hour time preference.
|
|
|
|
- The realm admin changed who can edit topics.
|
|
|
|
- The team's realm icon has changed.
|
|
|
|
- I switched from day mode to night mode.
|
|
|
|
|
|
|
|
Especially for things that are settings-related,
|
|
|
|
we rarely abstract away the data from `page_params`.
|
|
|
|
As of this writing, over 90 modules refer directly
|
|
|
|
to `page_params` for some reason or another.
|
|
|
|
|
|
|
|
## Dynamic data
|
|
|
|
|
|
|
|
Some of the data in `page_params` is either
|
|
|
|
more highly dynamic than settings data, or
|
|
|
|
has more performance requirements than
|
|
|
|
simple settings data, or both. Examples
|
|
|
|
include:
|
|
|
|
|
|
|
|
- tracking all users (we want to have
|
|
|
|
multiple Maps to find users, for example)
|
|
|
|
- tracking all streams
|
|
|
|
- tracking presence data
|
|
|
|
- tracking user groups and bots
|
|
|
|
- tracking recent PMs
|
|
|
|
|
|
|
|
Using stream data as an example, we use a
|
|
|
|
module called `stream_data` to actually track
|
|
|
|
all the info about the streams that a user
|
|
|
|
can know about. We populate this module
|
|
|
|
with data from `page_params`, but thereafter
|
|
|
|
`stream_data.js` "owns" the stream data:
|
|
|
|
|
|
|
|
- other modules should ask `stream_data`
|
|
|
|
for stuff (and not go to `page_params`)
|
|
|
|
- when server events come in, they should
|
|
|
|
be processed by stream_data to update
|
|
|
|
its own data structures
|
|
|
|
|
|
|
|
To help enforce this paradigm, we do the
|
|
|
|
following:
|
|
|
|
|
|
|
|
- only pass `stream_data` what it needs
|
|
|
|
from `page_params`
|
|
|
|
- delete the reference to data owned by
|
|
|
|
`stream_data` in `page_params` itself
|
|
|
|
*/
|
|
|
|
|
|
|
|
function pop_fields(...fields) {
|
|
|
|
const result = {};
|
|
|
|
|
|
|
|
for (const field of fields) {
|
|
|
|
result[field] = page_params[field];
|
|
|
|
delete page_params[field];
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const alert_words_params = pop_fields("alert_words");
|
2020-02-26 23:53:46 +01:00
|
|
|
|
2020-07-24 01:21:48 +02:00
|
|
|
const emoji_params = pop_fields("realm_emoji");
|
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const bot_params = pop_fields("realm_bots");
|
2020-02-25 12:16:26 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const people_params = pop_fields("realm_users", "realm_non_active_users", "cross_realm_bots");
|
2020-02-25 12:16:26 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const pm_conversations_params = pop_fields("recent_private_conversations");
|
2020-02-26 23:36:38 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const presence_params = pop_fields("presences", "initial_servertime");
|
2020-02-25 12:16:26 +01:00
|
|
|
|
|
|
|
const stream_data_params = pop_fields(
|
2020-07-15 01:29:15 +02:00
|
|
|
"subscriptions",
|
|
|
|
"unsubscribed",
|
|
|
|
"never_subscribed",
|
|
|
|
"realm_default_streams",
|
2020-02-25 12:16:26 +01:00
|
|
|
);
|
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const user_groups_params = pop_fields("realm_user_groups");
|
2020-02-25 12:16:26 +01:00
|
|
|
|
2020-07-15 00:34:28 +02:00
|
|
|
const user_status_params = pop_fields("user_status");
|
2020-02-25 12:16:26 +01:00
|
|
|
|
2020-02-26 23:53:46 +01:00
|
|
|
alert_words.initialize(alert_words_params);
|
2020-02-21 00:20:39 +01:00
|
|
|
emojisets.initialize();
|
2020-02-25 12:16:26 +01:00
|
|
|
people.initialize(page_params.user_id, people_params);
|
2018-06-19 20:16:00 +02:00
|
|
|
scroll_bar.initialize();
|
2018-05-15 22:03:14 +02:00
|
|
|
message_viewport.initialize();
|
2018-12-26 19:56:19 +01:00
|
|
|
exports.initialize_kitchen_sink_stuff();
|
2018-05-15 22:03:14 +02:00
|
|
|
echo.initialize();
|
|
|
|
stream_color.initialize();
|
|
|
|
stream_edit.initialize();
|
2020-02-25 12:16:26 +01:00
|
|
|
stream_data.initialize(stream_data_params);
|
2020-02-26 23:36:38 +01:00
|
|
|
pm_conversations.recent.initialize(pm_conversations_params);
|
2018-12-15 19:53:20 +01:00
|
|
|
muting.initialize();
|
2018-05-15 22:03:14 +02:00
|
|
|
subs.initialize();
|
2019-03-22 17:26:51 +01:00
|
|
|
stream_list.initialize();
|
2018-05-15 22:03:14 +02:00
|
|
|
condense.initialize();
|
2020-04-04 22:14:34 +02:00
|
|
|
spoilers.initialize();
|
2018-05-15 22:03:14 +02:00
|
|
|
lightbox.initialize();
|
|
|
|
click_handlers.initialize();
|
|
|
|
copy_and_paste.initialize();
|
|
|
|
overlays.initialize();
|
|
|
|
invite.initialize();
|
|
|
|
timerender.initialize();
|
2020-07-08 23:44:01 +02:00
|
|
|
message_view_header.initialize();
|
2017-07-04 03:14:24 +02:00
|
|
|
server_events.initialize();
|
2020-02-25 12:16:26 +01:00
|
|
|
user_status.initialize(user_status_params);
|
2018-03-06 15:07:55 +01:00
|
|
|
compose_pm_pill.initialize();
|
2018-06-08 14:24:17 +02:00
|
|
|
search_pill_widget.initialize();
|
2018-03-06 15:07:55 +01:00
|
|
|
reload.initialize();
|
2020-02-25 12:16:26 +01:00
|
|
|
user_groups.initialize(user_groups_params);
|
2017-08-01 14:50:40 +02:00
|
|
|
unread.initialize();
|
2020-02-25 12:16:26 +01:00
|
|
|
bot_data.initialize(bot_params); // Must happen after people.initialize()
|
2017-07-04 01:30:47 +02:00
|
|
|
message_fetch.initialize();
|
2018-03-08 20:18:36 +01:00
|
|
|
message_scroll.initialize();
|
2020-07-25 23:59:49 +02:00
|
|
|
emoji.initialize({
|
|
|
|
realm_emoji: emoji_params.realm_emoji,
|
|
|
|
emoji_codes: generated_emoji_codes,
|
|
|
|
});
|
2020-07-15 00:34:28 +02:00
|
|
|
markdown.initialize(page_params.realm_filters, markdown_config.get_helpers());
|
2018-03-06 15:07:55 +01:00
|
|
|
compose.initialize();
|
|
|
|
composebox_typeahead.initialize(); // Must happen after compose.initialize()
|
2017-03-18 20:29:55 +01:00
|
|
|
search.initialize();
|
|
|
|
tutorial.initialize();
|
|
|
|
notifications.initialize();
|
|
|
|
gear_menu.initialize();
|
2020-06-19 21:47:39 +02:00
|
|
|
presence.initialize(presence_params);
|
2018-06-03 17:29:11 +02:00
|
|
|
settings_panel_menu.initialize();
|
2017-05-09 06:58:49 +02:00
|
|
|
settings_sections.initialize();
|
2018-04-14 16:06:51 +02:00
|
|
|
settings_toggle.initialize();
|
2017-03-18 20:29:55 +01:00
|
|
|
hashchange.initialize();
|
|
|
|
unread_ui.initialize();
|
|
|
|
activity.initialize();
|
2017-08-16 23:54:26 +02:00
|
|
|
emoji_picker.initialize();
|
2017-06-14 19:58:51 +02:00
|
|
|
pm_list.initialize();
|
2018-09-10 14:52:58 +02:00
|
|
|
topic_list.initialize();
|
|
|
|
topic_zoom.initialize();
|
2017-06-02 23:12:15 +02:00
|
|
|
drafts.initialize();
|
sending messages: Extract sent_messages.js.
This commit extract send_messages.js to clean up code related
to the following things:
* sending data to /json/report_send_time
* restarting the event loop if events don't arrive on time
The code related to /json/report changes the following ways:
* We track the state almost completely in the new
send_messages.js module, with other modules just
making one-line calls.
* We no longer send "displayed" times to the servers, since
we were kind of lying about them anyway.
* We now explicitly track the state of each single sent
message in its own object.
* We now look up data related to the messages by local_id,
instead of message_id. The problem with message_id was
that is was mutable. Now we use local_id, and we extend
the local_id concept to messages that don't get rendered
client side. We no longer need to react to the
'message_id_changed' event to change our hash key.
* The code used to live in many places:
* various big chunks were scattered among compose.js,
and those were all moved or reduced to one-line
calls into the new module
* echo.js continues to make basically one-line calls,
but it no longer calls compose.report_as_received(),
nor does it set the "start" time.
* message_util.js used to report received events, but
only when they finally got drawn in the home view;
this code is gone now
The code related to restarting the event loop if events don't arrive
changes as follows:
* The timer now gets set up from within
send_messages.message_state.report_server_ack,
where we can easily inspect the current state of the
possibly-still-in-flight message.
* The code to confirm that an event was received happens now
in server_events.js, rather than later, so that we don't
falsely blame the event loop for a downstream bug. (Plus
it's easier to just do it one place.)
This change removes a fair amount of code from our node tests. Some
of the removal is good stuff related to us completing killing off
unnecessary code. Other removals are more expediency-driven, and
we should make another sweep at ramping up our coverage on compose.js,
with possibly a little more mocking of the new `send_messages` code
layer, since it's now abstracted better.
There is also some minor cleanup to echo.resend_message() in this
commit.
See #5968 for a detailed breakdown of the changes.
2017-07-30 12:56:46 +02:00
|
|
|
sent_messages.initialize();
|
2017-09-26 00:03:27 +02:00
|
|
|
hotspots.initialize();
|
2017-08-08 02:16:35 +02:00
|
|
|
ui.initialize();
|
2018-01-31 01:11:21 +01:00
|
|
|
panels.initialize();
|
2018-03-16 12:39:46 +01:00
|
|
|
typing.initialize();
|
2018-08-17 03:46:32 +02:00
|
|
|
starred_messages.initialize();
|
2019-01-26 21:14:33 +01:00
|
|
|
user_status_ui.initialize();
|
2020-09-26 06:02:29 +02:00
|
|
|
fenced_code.initialize(generated_pygments_data);
|
2018-12-26 19:56:19 +01:00
|
|
|
};
|
|
|
|
|
2020-07-02 01:45:54 +02:00
|
|
|
$(() => {
|
2021-01-31 13:57:52 +01:00
|
|
|
blueslip.measure_time("initialize_everything", () => {
|
|
|
|
exports.initialize_everything();
|
|
|
|
});
|
2017-03-18 20:29:55 +01:00
|
|
|
});
|