text: import $ from "jquery";
  import _ from "lodash";
  import tippy from "tippy.js";
  import WinChan from "winchan";
  
  // You won't find every click handler here, but it's a good place to start!
  
  import render_buddy_list_tooltip_content from "../templates/buddy_list_tooltip_content.hbs";
  
  import * as activity from "./activity";
  import * as blueslip from "./blueslip";
  import * as browser_history from "./browser_history";
  import * as buddy_data from "./buddy_data";
  import * as channel from "./channel";
  import * as compose from "./compose";
  import * as compose_actions from "./compose_actions";
  import * as compose_state from "./compose_state";
  import {media_breakpoints_num} from "./css_variables";
  import * as emoji_picker from "./emoji_picker";
  import * as hash_util from "./hash_util";
  import * as hotspots from "./hotspots";
  import {$t} from "./i18n";
  import * as message_edit from "./message_edit";
  import * as message_edit_history from "./message_edit_history";
  import * as message_flags from "./message_flags";
  import * as message_lists from "./message_lists";
  import * as message_store from "./message_store";
  import * as muting_ui from "./muting_ui";
  import * as narrow from "./narrow";
  import * as notifications from "./notifications";
  import * as overlays from "./overlays";
  import {page_params} from "./page_params";
  import * as popovers from "./popovers";
  import * as reactions from "./reactions";
  import * as recent_topics from "./recent_topics";
  import * as rows from "./rows";
  import * as server_events from "./server_events";
  import * as settings_panel_menu from "./settings_panel_menu";
  import * as settings_toggle from "./settings_toggle";
  import * as stream_edit from "./stream_edit";
  import * as stream_list from "./stream_list";
  import * as stream_popover from "./stream_popover";
  import * as ui_util from "./ui_util";
  import * as unread_ops from "./unread_ops";
  import * as user_status_ui from "./user_status_ui";
  import * as util from "./util";
  
  export function initialize() {
      // MESSAGE CLICKING
  
      function initialize_long_tap() {
          const MS_DELAY = 750;
          const meta = {
              touchdown: false,
              current_target: undefined,
          };
  
          $("#main_div").on("touchstart", ".messagebox", function () {
              meta.touchdown = true;
              meta.invalid = false;
              const id = rows.id($(this).closest(".message_row"));
              meta.current_target = id;
              if (!id) {
                  return;
              }
              message_lists.current.select_id(id);
              setTimeout(() => {
                  // The algorithm to trigger long tap is that first, we check
                  // whether the message is still touched after MS_DELAY ms and
                  // the user isn't scrolling the messages(see other touch event
                  // handlers to see how these meta variables are handled).
                  // Later we check whether after MS_DELAY the user is still
                  // long touching the same message as it can be possible that
                  // user touched another message within MS_DELAY period.
                  if (meta.touchdown === true && !meta.invalid && id === meta.current_target) {
                      $(this).trigger("longtap");
                  }
              }, MS_DELAY);
          });
  
          $("#main_div").on("touchend", ".messagebox", () => {
              meta.touchdown = false;
          });
  
          $("#main_div").on("touchmove", ".messagebox", () => {
              meta.invalid = true;
          });
  
          $("#main_div").on("contextmenu", ".messagebox", (e) => {
              e.preventDefault();
          });
      }
  
      // this initializes the trigger that will give off the longtap event, which
      // there is no point in running if we are on desktop since this isn't a
      // standard event that we would want to support.
      if (util.is_mobile()) {
          initialize_long_tap();
      }
  
      function is_clickable_message_element(target) {
          // This function defines all the elements within a message
          // body that have UI behavior other than starting a reply.
  
          // Links should be handled by the browser.
          if (target.closest("a").length > 0) {
              return true;
          }
  
          // Forms for message editing contain input elements
          if (target.is("textarea") || target.is("input")) {
              return true;
          }
  
          // Widget for adjusting the height of a message.
          if (target.is("div.message_length_controller")) {
              return true;
          }
  
          // Inline image and twitter previews.
          if (target.is("img.message_inline_image") || target.is("img.twitter-avatar")) {
              return true;
          }
  
          // UI elements for triggering message editing or viewing edit history.
          if (target.is("i.edit_content_button") || target.is(".message_edit_notice")) {
              return true;
          }
  
          // For spoilers, allow clicking either the header or elements within it
          if (target.is(".spoiler-header") || target.parents(".spoiler-header").length > 0) {
              return true;
          }
  
          // Ideally, this should be done via ClipboardJS, but it doesn't support
          // feature of stopPropagation once clicked.
          // See https://github.com/zenorocha/clipboard.js/pull/475
          if (target.is(".copy_codeblock") || target.parents(".copy_codeblock").length > 0) {
              return true;
          }
  
          return false;
      }
  
      const select_message_function = function (e) {
          if (is_clickable_message_element($(e.target))) {
              // 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 (document.getSelection().type === "Range") {
              // Drags on the message (to copy message text) shouldn't trigger a reply.
              return;
          }
  
          const row = $(this).closest(".message_row");
          const id = rows.id(row);
  
          if (message_edit.is_editing(id)) {
              // Clicks on a message being edited shouldn't trigger a reply.
              return;
          }
  
          message_lists.current.select_id(id);
          compose_actions.respond_to_message({trigger: "message click"});
          e.stopPropagation();
          popovers.hide_all();
      };
  
      // if on normal non-mobile experience, a `click` event should run the message
      // selection function which will open the compose box  and select the message.
      if (!util.is_mobile()) {
          $("#main_div").on("click", ".messagebox", select_message_function);
          // on the other hand, on mobile it should be done with a long tap.
      } else {
          $("#main_div").on("longtap", ".messagebox", function (e) {
              // find the correct selection API for the browser.
              const sel = window.getSelection ? window.getSelection() : document.selection;
              // if one matches, remove the current selections.
              // after a longtap that is valid, there should be no text selected.
              if (sel) {
                  if (sel.removeAllRanges) {
                      sel.removeAllRanges();
                  } else if (sel.empty) {
                      sel.empty();
                  }
              }
  
              select_message_function.call(this, e);
          });
      }
  
      $("#main_div").on("click", ".star_container", function (e) {
          e.stopPropagation();
          popovers.hide_all();
  
          const message_id = rows.id($(this).closest(".message_row"));
          const message = message_store.get(message_id);
          message_flags.toggle_starred_and_update_server(message);
      });
  
      $("#main_div").on("click", ".message_reaction", function (e) {
          e.stopPropagation();
          emoji_picker.hide_emoji_popover();
          const local_id = $(this).attr("data-reaction-id");
          const message_id = rows.get_message_id(this);
          reactions.process_reaction_click(message_id, local_id);
          $(".tooltip").remove();
      });
  
      $("body").on("mouseenter", ".message_edit_notice", (e) => {
          if (page_params.realm_allow_edit_history) {
              $(e.currentTarget).addClass("message_edit_notice_hover");
          }
      });
  
      $("body").on("mouseleave", ".message_edit_notice", (e) => {
          if (page_params.realm_allow_edit_history) {
              $(e.currentTarget).removeClass("message_edit_notice_hover");
          }
      });
  
      $("body").on("click", ".message_edit_notice", (e) => {
          popovers.hide_all();
          const message_id = rows.id($(e.currentTarget).closest(".message_row"));
          const row = message_lists.current.get_row(message_id);
          const message = message_lists.current.get(rows.id(row));
          const message_history_cancel_btn = $("#message-history-cancel");
  
          if (page_params.realm_allow_edit_history) {
              message_edit_history.show_history(message);
              message_history_cancel_btn.trigger("focus");
          }
          e.stopPropagation();
          e.preventDefault();
      });
  
      $("#main_div").on("click", "a.stream", function (e) {
          e.preventDefault();
          // Note that we may have an href here, but we trust the stream id more,
          // so we re-encode the hash.
          const stream_id = Number.parseInt($(this).attr("data-stream-id"), 10);
          if (stream_id) {
              browser_history.go_to_location(hash_util.by_stream_uri(stream_id));
              return;
          }
          window.location.href = $(this).attr("href");
      });
  
      // USER STATUS MODAL
  
      $(".user-status-value").on("click", (e) => {
          e.stopPropagation();
          const user_status_value = $(e.currentTarget).attr("data-user-status-value");
          $("input.user_status").val(user_status_value);
          user_status_ui.toggle_clear_message_button();
          user_status_ui.update_button();
      });
  
      // MESSAGE EDITING
  
      $("body").on("click", ".edit_content_button", function (e) {
          const row = message_lists.current.get_row(rows.id($(this).closest(".message_row")));
          message_lists.current.select_id(rows.id(row));
          message_edit.start(row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".always_visible_topic_edit,.on_hover_topic_edit", function (e) {
          const recipient_row = $(this).closest(".recipient_row");
          message_edit.start_inline_topic_edit(recipient_row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".topic_edit_save", function (e) {
          const recipient_row = $(this).closest(".recipient_row");
          message_edit.save_inline_topic_edit(recipient_row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".topic_edit_cancel", function (e) {
          const recipient_row = $(this).closest(".recipient_row");
          message_edit.end_inline_topic_edit(recipient_row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".message_edit_save", function (e) {
          const row = $(this).closest(".message_row");
          message_edit.save_message_row_edit(row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".message_edit_cancel", function (e) {
          const row = $(this).closest(".message_row");
          message_edit.end_message_row_edit(row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".message_edit_close", function (e) {
          const row = $(this).closest(".message_row");
          message_edit.end_message_row_edit(row);
          e.stopPropagation();
          popovers.hide_all();
      });
      $("body").on("click", ".copy_message", function (e) {
          const row = $(this).closest(".message_row");
          message_edit.end_message_row_edit(row);
          row.find(".alert-msg").text($t({defaultMessage: "Copied!"}));
          row.find(".alert-msg").css("display", "block");
          row.find(".alert-msg").delay(1000).fadeOut(300);
          if ($(".tooltip").is(":visible")) {
              $(".tooltip").hide();
          }
          e.preventDefault();
          e.stopPropagation();
      });
      $("body").on("click", "a", function () {
          if (document.activeElement === this) {
              ui_util.blur_active_element();
          }
      });
      $(".message_edit_form .send-status-close").on("click", function () {
          const row_id = rows.id($(this).closest(".message_row"));
          const send_status = $(`#message-edit-send-status-${CSS.escape(row_id)}`);
          $(send_status).stop(true).fadeOut(200);
      });
      $("body").on("click", ".message_edit_form .compose_upload_file", function (e) {
          e.preventDefault();
  
          const row_id = rows.id($(this).closest(".message_row"));
          $(`#edit_form_${CSS.escape(row_id)} .file_input`).trigger("click");
      });
  
      $("body").on("click", ".message_edit_form .markdown_preview", (e) => {
          e.preventDefault();
          const row = rows.get_closest_row(e.target);
          const $msg_edit_content = row.find(".message_edit_content");
          const content = $msg_edit_content.val();
          $msg_edit_content.hide();
          row.find(".markdown_preview").hide();
          row.find(".undo_markdown_preview").show();
          row.find(".preview_message_area").show();
  
          compose.render_and_show_preview(
              row.find(".markdown_preview_spinner"),
              row.find(".preview_content"),
              content,
          );
      });
  
      $("body").on("click", ".message_edit_form .undo_markdown_preview", (e) => {
          e.preventDefault();
          const row = rows.get_closest_row(e.target);
          row.find(".message_edit_content").show();
          row.find(".undo_markdown_preview").hide();
          row.find(".preview_message_area").hide();
          row.find(".preview_content").empty();
          row.find(".markdown_preview").show();
      });
  
      // TOPIC MUTING
      function mute_or_unmute_topic($elt, mute_topic) {
          const stream_id = Number.parseInt($elt.attr("data-stream-id"), 10);
          const topic = $elt.attr("data-topic-name");
          if (mute_topic) {
              muting_ui.mute_topic(stream_id, topic);
          } else {
              muting_ui.unmute_topic(stream_id, topic);
          }
      }
  
      $("body").on("click", ".message_header .on_hover_topic_mute", (e) => {
          e.stopPropagation();
          mute_or_unmute_topic($(e.target), true);
      });
  
      $("body").on("click", ".message_header .on_hover_topic_unmute", (e) => {
          e.stopPropagation();
          mute_or_unmute_topic($(e.target), false);
      });
  
      // RECENT TOPICS
  
      $("body").on("keydown", ".on_hover_topic_mute", ui_util.convert_enter_to_click);
  
      $("body").on("click", "#recent_topics_table .on_hover_topic_unmute", (e) => {
          e.stopPropagation();
          const $elt = $(e.target);
          recent_topics.focus_clicked_element($elt, recent_topics.COLUMNS.mute);
          mute_or_unmute_topic($elt, false);
      });
  
      $("body").on("keydown", ".on_hover_topic_unmute", ui_util.convert_enter_to_click);
  
      $("body").on("click", "#recent_topics_table .on_hover_topic_mute", (e) => {
          e.stopPropagation();
          const $elt = $(e.target);
          recent_topics.focus_clicked_element($elt, recent_topics.COLUMNS.mute);
          mute_or_unmute_topic($elt, true);
      });
  
      $("body").on("click", "#recent_topics_search", (e) => {
          e.stopPropagation();
          recent_topics.change_focused_element($(e.target), "click");
      });
  
      $("body").on("click", "#recent_topics_table .on_hover_topic_read", (e) => {
          e.stopPropagation();
          recent_topics.focus_clicked_element($(e.target), recent_topics.COLUMNS.read);
          const stream_id = Number.parseInt($(e.currentTarget).attr("data-stream-id"), 10);
          const topic = $(e.currentTarget).attr("data-topic-name");
          unread_ops.mark_topic_as_read(stream_id, topic);
      });
  
      $("body").on("keydown", ".on_hover_topic_read", ui_util.convert_enter_to_click);
  
      $("body").on("click", ".btn-recent-filters", (e) => {
          e.stopPropagation();
          recent_topics.change_focused_element($(e.target), "click");
          recent_topics.set_filter(e.currentTarget.dataset.filter);
          recent_topics.update_filters_view();
      });
  
      $("body").on("click", "td.recent_topic_stream", (e) => {
          e.stopPropagation();
          recent_topics.focus_clicked_element($(e.target), recent_topics.COLUMNS.stream);
          window.location.href = $(e.currentTarget).find("a").attr("href");
      });
  
      $("body").on("click", "td.recent_topic_name", (e) => {
          e.stopPropagation();
          recent_topics.focus_clicked_element($(e.target), recent_topics.COLUMNS.topic);
          window.location.href = $(e.currentTarget).find("a").attr("href");
      });
  
      // Search for all table rows (this combines stream & topic names)
      $("body").on(
          "keyup",
          "#recent_topics_search",
          _.debounce(() => {
              recent_topics.update_filters_view();
              // Wait for user to go idle before initiating search.
          }, 300),
      );
  
      $("body").on("click", "#recent_topics_search_clear", (e) => {
          e.stopPropagation();
          $("#recent_topics_search").val("");
          recent_topics.update_filters_view();
      });
  
      // RECIPIENT BARS
  
      function get_row_id_for_narrowing(narrow_link_elem) {
          const group = rows.get_closest_group(narrow_link_elem);
          const msg_id = rows.id_for_recipient_row(group);
  
          const nearest = message_lists.current.get(msg_id);
          const selected = message_lists.current.selected_message();
          if (util.same_recipient(nearest, selected)) {
              return selected.id;
          }
          return nearest.id;
      }
  
      $("#message_feed_container").on("click", ".narrows_by_recipient", function (e) {
          if (e.metaKey || e.ctrlKey) {
              return;
          }
          e.preventDefault();
          const row_id = get_row_id_for_narrowing(this);
          narrow.by_recipient(row_id, {trigger: "message header"});
      });
  
      $("#message_feed_container").on("click", ".narrows_by_topic", function (e) {
          if (e.metaKey || e.ctrlKey) {
              return;
          }
          e.preventDefault();
          const row_id = get_row_id_for_narrowing(this);
          narrow.by_topic(row_id, {trigger: "message header"});
      });
  
      // SIDEBARS
  
      $("#userlist-toggle-button").on("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
  
          const sidebarHidden = !$(".app-main .column-right").hasClass("expanded");
          popovers.hide_all();
          if (sidebarHidden) {
              popovers.show_userlist_sidebar();
          }
      });
  
      $("#streamlist-toggle-button").on("click", (e) => {
          e.preventDefault();
          e.stopPropagation();
  
          const sidebarHidden = !$(".app-main .column-left").hasClass("expanded");
          popovers.hide_all();
          if (sidebarHidden) {
              stream_popover.show_streamlist_sidebar();
          }
      });
  
      $("#user_presences")
          .expectOne()
          .on("click", ".selectable_sidebar_block", (e) => {
              const li = $(e.target).parents("li");
  
              activity.narrow_for_user({li});
  
              e.preventDefault();
              e.stopPropagation();
              popovers.hide_all();
              $(".tooltip").remove();
          });
  
      function do_render_buddy_list_tooltip(elem, title_data) {
          let placement = "left";
          let observer;
          if (window.innerWidth < media_breakpoints_num.md) {
              // On small devices display tooltips based on available space.
              // This will default to "bottom" placement for this tooltip.
              placement = "auto";
          }
          tippy(elem[0], {
              // Quickly display and hide right sidebar tooltips
              // so that they don't stick and overlap with
              // each other.
              delay: 0,
              content: render_buddy_list_tooltip_content(title_data),
              arrow: true,
              placement,
              allowHTML: true,
              showOnCreate: true,
              onHidden: (instance) => {
                  instance.destroy();
                  observer.disconnect();
              },
              onShow: (instance) => {
                  // For both buddy list and top left corner pm list, `target_node`
                  // is their parent `ul` element. We cannot use MutationObserver
                  // directly on the reference element because it will be removed
                  // and we need to attach it on an element which will remain in the
                  // DOM which is their parent `ul`.
                  const target_node = $(instance.reference).parents("ul").get(0);
                  // We only need to know if any of the `li` elements were removed.
                  const config = {attributes: false, childList: true, subtree: false};
                  const callback = function (mutationsList) {
                      for (const mutation of mutationsList) {
                          // Hide instance if reference is in the removed node list.
                          if (
                              Array.prototype.includes.call(
                                  mutation.removedNodes,
                                  instance.reference.parentElement,
                              )
                          ) {
                              instance.hide();
                          }
                      }
                  };
                  observer = new MutationObserver(callback);
                  observer.observe(target_node, config);
              },
              appendTo: () => document.body,
          });
      }
  
      // BUDDY LIST TOOLTIPS
      $("#user_presences").on("mouseenter", ".selectable_sidebar_block", (e) => {
          e.stopPropagation();
          const elem = $(e.currentTarget).closest(".user_sidebar_entry").find(".user-presence-link");
          const user_id_string = elem.attr("data-user-id");
          const title_data = buddy_data.get_title_data(user_id_string, false);
          do_render_buddy_list_tooltip(elem.parent(), title_data);
      });
  
      // PM LIST TOOLTIPS
      $("body").on("mouseenter", "#pm_user_status", (e) => {
          e.stopPropagation();
          const elem = $(e.currentTarget);
          const user_ids_string = elem.attr("data-user-ids-string");
          // This converts from 'true' in the DOM to true.
          const is_group = JSON.parse(elem.attr("data-is-group"));
  
          const title_data = buddy_data.get_title_data(user_ids_string, is_group);
          do_render_buddy_list_tooltip(elem, title_data);
      });
  
      // MISC
  
      (function () {
          const sel = ["#stream_filters", "#global_filters", "#user_presences"].join(", ");
  
          $(sel).on("click", "a", function () {
              this.blur();
          });
      })();
  
      popovers.register_click_handlers();
      emoji_picker.register_click_handlers();
      stream_popover.register_click_handlers();
      notifications.register_click_handlers();
  
      $("body").on("click", ".logout_button", () => {
          $("#logout_form").trigger("submit");
      });
  
      $(".restart_get_events_button").on("click", () => {
          server_events.restart_get_events({dont_block: true});
      });
  
      $("#settings_page").on("click", ".collapse-settings-btn", () => {
          settings_toggle.toggle_org_setting_collapse();
      });
  
      $(".organization-box").on("show.bs.modal", () => {
          popovers.hide_all();
      });
  
      // COMPOSE
  
      $("body").on("click", "#compose-send-status .compose-send-status-close", () => {
          $("#compose-send-status").stop(true).fadeOut(500);
      });
      $("body").on("click", "#nonexistent_stream_reply_error .compose-send-status-close", () => {
          $("#nonexistent_stream_reply_error").stop(true).fadeOut(500);
      });
  
      $("body").on("click", ".empty_feed_compose_stream", (e) => {
          compose_actions.start("stream", {trigger: "empty feed message"});
          e.preventDefault();
      });
      $("body").on("click", ".empty_feed_compose_private", (e) => {
          compose_actions.start("private", {trigger: "empty feed message"});
          e.preventDefault();
      });
  
      $("body").on("click", "[data-overlay-trigger]", function () {
          const target = $(this).attr("data-overlay-trigger");
          browser_history.go_to_location(target);
      });
  
      function handle_compose_click(e) {
          // Emoji clicks should be handled by their own click handler in emoji_picker.js
          if ($(e.target).is(".emoji_map, img.emoji, .drag, .compose_gif_icon")) {
              return;
          }
  
          // The mobile compose button has its own popover when clicked, so it already.
          // hides other popovers.
          if ($(e.target).is(".compose_mobile_button, .compose_mobile_button *")) {
              return;
          }
  
          // Don't let clicks in the compose area count as
          // "unfocusing" our compose -- in other words, e.g.
          // clicking "Press Enter to send" should not
          // trigger the composebox-closing code above.
          // But do allow our formatting link.
          if (!$(e.target).is("a")) {
              e.stopPropagation();
          }
          // Still hide the popovers, however
          popovers.hide_all();
      }
  
      $("body").on("click", "#compose_buttons", handle_compose_click);
      $("body").on("click", ".compose-content", handle_compose_click);
  
      $("body").on("click", "#compose_close", () => {
          compose_actions.cancel();
      });
  
      // LEFT SIDEBAR
  
      $("#streams_inline_cog").on("click", (e) => {
          e.stopPropagation();
          browser_history.go_to_location("streams/subscribed");
      });
  
      $("#streams_filter_icon").on("click", (e) => {
          e.stopPropagation();
          stream_list.toggle_filter_displayed(e);
      });
  
      // WEBATHENA
  
      $("body").on("click", ".webathena_login", (e) => {
          $("#zephyr-mirror-error").removeClass("show");
          const principal = ["zephyr", "zephyr"];
          WinChan.open(
              {
                  url: "https://webathena.mit.edu/#!request_ticket_v1",
                  relay_url: "https://webathena.mit.edu/relay.html",
                  params: {
                      realm: "ATHENA.MIT.EDU",
                      principal,
                  },
              },
              (err, r) => {
                  if (err) {
                      blueslip.warn(err);
                      return;
                  }
                  if (r.status !== "OK") {
                      blueslip.warn(r);
                      return;
                  }
  
                  channel.post({
                      url: "/accounts/webathena_kerberos_login/",
                      data: {cred: JSON.stringify(r.session)},
                      success() {
                          $("#zephyr-mirror-error").removeClass("show");
                      },
                      error() {
                          $("#zephyr-mirror-error").addClass("show");
                      },
                  });
              },
          );
          $("#settings-dropdown").dropdown("toggle");
          e.preventDefault();
          e.stopPropagation();
      });
      // End Webathena code
  
      // disable the draggability for left-sidebar components
      $("#stream_filters, #global_filters").on("dragstart", (e) => {
          e.target.blur();
          return false;
      });
  
      // Chrome focuses an element when dragging it which can be confusing when
      // users involuntarily drag something and we show them the focus outline.
      $("body").on("dragstart", "a", (e) => e.target.blur());
  
      // Don't focus links on middle click.
      $("body").on("mouseup", "a", (e) => {
          if (e.which === 2) {
              // middle click
              e.target.blur();
          }
      });
  
      // Don't focus links on context menu.
      $("body").on("contextmenu", "a", (e) => e.target.blur());
  
      (function () {
          const map = {
              ".stream-description-editable": {
                  on_start: stream_edit.set_raw_description,
                  on_save: stream_edit.change_stream_description,
              },
              ".stream-name-editable": {
                  on_start: null,
                  on_save: stream_edit.change_stream_name,
              },
          };
  
          $(document).on("keydown", ".editable-section", function (e) {
              e.stopPropagation();
              // Cancel editing description if Escape key is pressed.
              if (e.which === 27) {
                  $("[data-finish-editing='.stream-description-editable']").hide();
                  $(this).attr("contenteditable", false);
                  $(this).text($(this).attr("data-prev-text"));
                  $("[data-make-editable]").html("");
              } else if (e.which === 13) {
                  $(this).siblings(".checkmark").trigger("click");
              }
          });
  
          $(document).on("drop", ".editable-section", () => false);
  
          $(document).on("input", ".editable-section", function () {
              // if there are any child nodes, inclusive of <br> which means you
              // have lines in your description or title, you're doing something
              // wrong.
              for (let x = 0; x < this.childNodes.length; x += 1) {
                  if (this.childNodes[x].nodeType !== 3) {
                      this.textContent = this.textContent.replace(/\n/, "");
                      break;
                  }
              }
          });
  
          $("body").on("click", "[data-make-editable]", function () {
              const selector = $(this).attr("data-make-editable");
              const edit_area = $(this).parent().find(`${selector}`);
              $(selector).removeClass("stream-name-edit-box");
              if (edit_area.attr("contenteditable") === "true") {
                  $(`[data-finish-editing='${CSS.escape(selector)}']`).hide();
                  edit_area.attr("contenteditable", false);
                  edit_area.text(edit_area.attr("data-prev-text"));
                  $(this).html("");
              } else {
                  $(`[data-finish-editing='${CSS.escape(selector)}']`).show();
  
                  $(selector).addClass("stream-name-edit-box");
                  edit_area
                      .attr("data-prev-text", edit_area.text().trim())
                      .attr("contenteditable", true);
  
                  if (map[selector].on_start) {
                      map[selector].on_start(this, edit_area);
                  }
  
                  ui_util.place_caret_at_end(edit_area[0]);
  
                  $(this).html("&times;");
              }
          });
  
          $("body").on("click", "[data-finish-editing]", function (e) {
              const selector = $(this).attr("data-finish-editing");
              $(selector).removeClass("stream-name-edit-box");
              if (map[selector].on_save) {
                  map[selector].on_save(e);
                  $(this).hide();
                  $(this).parent().find(`${selector}`).attr("contenteditable", false);
                  $(`[data-make-editable='${CSS.escape(selector)}']`).html("");
              }
          });
      })();
  
      // HOTSPOTS
  
      // open
      $("body").on("click", ".hotspot-icon", function (e) {
          // hide icon
          hotspots.close_hotspot_icon(this);
  
          // show popover
          const hotspot_name = $(e.target)
              .closest(".hotspot-icon")
              .attr("id")
              .replace("hotspot_", "")
              .replace("_icon", "");
          const overlay_name = "hotspot_" + hotspot_name + "_overlay";
  
          overlays.open_overlay({
              name: overlay_name,
              overlay: $(`#${CSS.escape(overlay_name)}`),
              on_close: function () {
                  // close popover
                  $(this).css({display: "block"});
                  $(this).animate(
                      {opacity: 1},
                      {
                          duration: 300,
                      },
                  );
              }.bind(this),
          });
  
          e.preventDefault();
          e.stopPropagation();
      });
  
      // confirm
      $("body").on("click", ".hotspot.overlay .hotspot-confirm", function (e) {
          e.preventDefault();
          e.stopPropagation();
  
          const overlay_name = $(this).closest(".hotspot.overlay").attr("id");
  
          const hotspot_name = overlay_name.replace("hotspot_", "").replace("_overlay", "");
  
          // Comment below to disable marking hotspots as read in production
          hotspots.post_hotspot_as_read(hotspot_name);
  
          overlays.close_overlay(overlay_name);
          $(`#hotspot_${CSS.escape(hotspot_name)}_icon`).remove();
      });
  
      $("body").on("click", ".hotspot-button", (e) => {
          e.preventDefault();
          e.stopPropagation();
  
          hotspots.post_hotspot_as_read("intro_reply");
          hotspots.close_hotspot_icon($("#hotspot_intro_reply_icon"));
          $("#hotspot_intro_reply_icon").remove();
      });
  
      // stop propagation
      $("body").on("click", ".hotspot.overlay .hotspot-popover", (e) => {
          e.stopPropagation();
      });
  
      $("body").on("hidden.bs.modal", () => {
          // Enable mouse events for the background as the modal closes.
          overlays.enable_background_mouse_events();
      });
  
      // MAIN CLICK HANDLER
  
      $(document).on("click", (e) => {
          if (e.button !== 0 || $(e.target).is(".drag")) {
              // Firefox emits right click events on the document, but not on
              // the child nodes, so the #compose stopPropagation doesn't get a
              // chance to capture right clicks.
              return;
          }
  
          // Dismiss popovers if the user has clicked outside them
          if (
              $(
                  '.popover-inner, #user-profile-modal, .emoji-info-popover, .app-main [class^="column-"].expanded',
              ).has(e.target).length === 0
          ) {
              popovers.hide_all();
          }
  
          if (compose_state.composing()) {
              if ($(e.target).closest("a").length > 0) {
                  // Refocus compose message text box if one clicks an external
                  // link/url to view something else while composing a message
                  // See issue #4331 for more details
                  $("#compose-textarea").trigger("focus");
                  return;
              } else if (
                  !window.getSelection().toString() &&
                  // Clicks inside an overlay, popover, custom
                  // modal, or backdrop of one of the above
                  // should not have any effect on the compose
                  // state.
                  !$(e.target).closest(".overlay").length &&
                  !$(e.target).closest(".popover").length &&
                  !$(e.target).closest(".modal").length &&
                  !$(e.target).closest(".modal-backdrop").length &&
                  $(e.target).closest("body").length
              ) {
                  // Unfocus our compose area if we click out of it. Don't let exits out
                  // of overlays or selecting text (for copy+paste) trigger cancelling.
                  // Check if the click is within the body to prevent extensions from
                  // interfering with the compose box.
                  compose_actions.cancel();
              }
          }
      });
  
      // Workaround for Bootstrap issue #5900, which basically makes dropdowns
      // unclickable on mobile devices.
      // https://github.com/twitter/bootstrap/issues/5900
      $("a.dropdown-toggle, .dropdown-menu a").on("touchstart", (e) => {
          e.stopPropagation();
      });
  
      $(".settings-header.mobile .fa-chevron-left").on("click", () => {
          settings_panel_menu.mobile_deactivate_section();
      });
  }
  