"use strict"; let active_overlay; let close_handler; let open_overlay_name; function reset_state() { active_overlay = undefined; close_handler = undefined; open_overlay_name = undefined; } exports.is_active = function () { return !!open_overlay_name; }; exports.is_modal_open = function () { return $(".modal").hasClass("in"); }; exports.info_overlay_open = function () { return open_overlay_name === "informationalOverlays"; }; exports.settings_open = function () { return open_overlay_name === "settings"; }; exports.streams_open = function () { return open_overlay_name === "subscriptions"; }; exports.lightbox_open = function () { return open_overlay_name === "lightbox"; }; exports.drafts_open = function () { return open_overlay_name === "drafts"; }; exports.recent_topics_open = function () { return open_overlay_name === "recent_topics"; }; // To address bugs where mouse might apply to the streams/settings // overlays underneath an open modal within those settings UI, we add // this inline style to '.overlay.show', overriding the // "pointer-events: all" style in app_components.scss. // // This is kinda hacky; it only works for modals within overlays, and // we need to make sure it gets re-enabled when the modal closes. exports.disable_background_mouse_events = function () { $(".overlay.show").attr("style", "pointer-events: none"); }; // This removes only the inline-style of the element that // was added in disable_background_mouse_events and // enables the background mouse events. exports.enable_background_mouse_events = function () { $(".overlay.show").attr("style", null); }; exports.active_modal = function () { if (!exports.is_modal_open()) { blueslip.error("Programming error — Called active_modal when there is no modal open"); return; } return "#" + $(".modal.in").attr("id"); }; exports.open_overlay = function (opts) { popovers.hide_all(); if (!opts.name || !opts.overlay || !opts.on_close) { blueslip.error("Programming error in open_overlay"); return; } if (active_overlay || open_overlay_name || close_handler) { blueslip.error( "Programming error — trying to open " + opts.name + " before closing " + open_overlay_name, ); return; } blueslip.debug("open overlay: " + opts.name); // Our overlays are kind of crufty...we have an HTML id // attribute for them and then a data-overlay attribute for // them. Make sure they match. if (opts.overlay.attr("data-overlay") !== opts.name) { blueslip.error("Bad overlay setup for " + opts.name); return; } open_overlay_name = opts.name; active_overlay = opts.overlay; opts.overlay.addClass("show"); opts.overlay.attr("aria-hidden", "false"); $(".app").attr("aria-hidden", "true"); $(".fixed-app").attr("aria-hidden", "true"); $(".header").attr("aria-hidden", "true"); close_handler = function () { opts.on_close(); reset_state(); }; }; exports.open_modal = function (selector) { if (selector === undefined) { blueslip.error("Undefined selector was passed into open_modal"); return; } if (selector[0] !== "#") { blueslip.error("Non-id-based selector passed in to open_modal: " + selector); return; } if (exports.is_modal_open()) { blueslip.error( "open_modal() was called while " + exports.active_modal() + " modal was open.", ); return; } blueslip.debug("open modal: " + selector); const elem = $(selector).expectOne(); elem.modal("show").attr("aria-hidden", false); // Disable background mouse events when modal is active exports.disable_background_mouse_events(); // Remove previous alert messages from modal, if exists. elem.find(".alert").hide(); elem.find(".alert-notification").html(""); }; exports.close_overlay = function (name) { if (name !== open_overlay_name) { blueslip.error("Trying to close " + name + " when " + open_overlay_name + " is open."); return; } if (name === undefined) { blueslip.error("Undefined name was passed into close_overlay"); return; } blueslip.debug("close overlay: " + name); active_overlay.removeClass("show"); active_overlay.attr("aria-hidden", "true"); $(".app").attr("aria-hidden", "false"); $(".fixed-app").attr("aria-hidden", "false"); $(".header").attr("aria-hidden", "false"); if (!close_handler) { blueslip.error("Overlay close handler for " + name + " not properly setup."); return; } close_handler(); }; exports.close_active = function () { if (!open_overlay_name) { blueslip.warn("close_active() called without checking is_active()"); return; } exports.close_overlay(open_overlay_name); }; exports.close_modal = function (selector) { if (selector === undefined) { blueslip.error("Undefined selector was passed into close_modal"); return; } if (!exports.is_modal_open()) { blueslip.warn("close_active_modal() called without checking is_modal_open()"); return; } if (exports.active_modal() !== selector) { blueslip.error( "Trying to close " + selector + " modal when " + exports.active_modal() + " is open.", ); return; } blueslip.debug("close modal: " + selector); const elem = $(selector).expectOne(); elem.modal("hide").attr("aria-hidden", true); // Enable mouse events for the background as the modal closes. exports.enable_background_mouse_events(); }; exports.close_active_modal = function () { if (!exports.is_modal_open()) { blueslip.warn("close_active_modal() called without checking is_modal_open()"); return; } $(".modal.in").modal("hide").attr("aria-hidden", true); }; exports.close_for_hash_change = function () { $(".overlay.show").removeClass("show"); reset_state(); }; exports.open_settings = function () { exports.open_overlay({ name: "settings", overlay: $("#settings_overlay_container"), on_close() { hashchange.exit_overlay(); }, }); }; exports.initialize = function () { $("body").on("click", ".overlay, .overlay .exit", (e) => { let $target = $(e.target); // if the target is not the .overlay element, search up the node tree // until it is found. if ($target.is(".exit, .exit-sign, .overlay-content, .exit span")) { $target = $target.closest("[data-overlay]"); } else if (!$target.is(".overlay")) { // not a valid click target then. return; } const target_name = $target.attr("data-overlay"); exports.close_overlay(target_name); e.preventDefault(); e.stopPropagation(); }); }; window.overlays = exports;