(function (ventana, documento, $) { "use strict"; if (ventana.SIGPanelLateral) { return; } // Uso: data-sig-panel-abrir="#MiPanel" o $("#MiPanel").panelLateral("abrir"). // Eventos por panel: abrir.sig.panel, abierto.sig.panel, cerrar.sig.panel, cerrado.sig.panel. // Los eventos abrir y cerrar admiten preventDefault(). No se cierran otros paneles. var abiertos = []; function resolver(destino) { if (typeof destino === "string") { destino = documento.getElementById(destino.replace(/^#/, "")); } if (destino && destino.jquery) { destino = destino[0]; } return destino && $(destino).hasClass("sig_panel_lateral") ? destino : null; } function instancia(panel) { var estado = $.data(panel, "sig.panel"); if (!estado) { estado = { panel: panel, abierto: false, ocupado: false, origenFoco: null, ubicacion: null, fondo: null }; $.data(panel, "sig.panel", estado); $(panel).attr("data-sig-panel-initialized", "1"); if (!panel.hasAttribute("tabindex")) { panel.setAttribute("tabindex", "-1"); } } return estado; } function ultimoAbierto() { return abiertos.length ? abiertos[abiertos.length - 1] : null; } function actualizarPagina() { $(documento.body).toggleClass("sig_panel_activo", abiertos.length > 0); } function abrir(destino, origen) { var panel = resolver(destino); if (!panel) { return; } var estado = instancia(panel); if (estado.ocupado) { return; } if (estado.abierto) { // Elevar en el orden del DOM conserva el mismo z-index para todos. abiertos = abiertos.filter(function (actual) { return actual !== estado; }); abiertos.push(estado); panel.parentNode.appendChild(panel); panel.focus(); return; } estado.ocupado = true; var evento = $.Event("abrir.sig.panel", { relatedTarget: origen || null }); $(panel).trigger(evento); if (evento.isDefaultPrevented()) { estado.ocupado = false; return; } estado.origenFoco = origen || documento.activeElement; // Conserva el formulario WebForms y evita recortes o contextos de apilado de sus ancestros. // Al cerrar se devuelve exactamente al lugar donde fue declarado. estado.ubicacion = documento.createComment("Ubicación del panel lateral"); panel.parentNode.insertBefore(estado.ubicacion, panel); var contenedor = $(panel).closest("form")[0] || documento.body; if ($(panel).attr("data-sig-panel-fondo") !== "false") { estado.fondo = $("
", { "class": "sig_panel_fondo", "aria-hidden": "true" })[0]; $.data(estado.fondo, "sig.panel.propietario", panel); contenedor.appendChild(estado.fondo); } contenedor.appendChild(panel); estado.abierto = true; abiertos.push(estado); $(panel).addClass("sig_panel_open").attr("aria-hidden", "false"); actualizarPagina(); panel.focus(); estado.ocupado = false; $(panel).trigger($.Event("abierto.sig.panel", { relatedTarget: origen || null })); } function retirar(estado) { abiertos = abiertos.filter(function (actual) { return actual !== estado; }); if (estado.fondo) { $(estado.fondo).remove(); estado.fondo = null; } estado.abierto = false; actualizarPagina(); } function cerrar(destino) { var panel = resolver(destino); var estado = panel ? $.data(panel, "sig.panel") : null; if (!estado || !estado.abierto || estado.ocupado) { return; } estado.ocupado = true; var evento = $.Event("cerrar.sig.panel"); $(panel).trigger(evento); if (evento.isDefaultPrevented()) { estado.ocupado = false; return; } var teniaFoco = ultimoAbierto() === estado; $(panel).removeClass("sig_panel_open").attr("aria-hidden", "true"); if (estado.ubicacion && estado.ubicacion.parentNode) { estado.ubicacion.parentNode.replaceChild(panel, estado.ubicacion); } estado.ubicacion = null; retirar(estado); if (teniaFoco) { var siguiente = ultimoAbierto(); if (siguiente) { siguiente.panel.focus(); } else if (estado.origenFoco && documento.documentElement.contains(estado.origenFoco)) { estado.origenFoco.focus(); } } estado.ocupado = false; $(panel).trigger("cerrado.sig.panel"); } ventana.SIGPanelLateral = { abrir: abrir, cerrar: cerrar }; $.fn.panelLateral = function (accion) { return this.each(function () { if (accion === "cerrar") { cerrar(this); } else if (accion === "abrir" || !accion) { abrir(this); } }); }; // Delegación única: los botones nuevos de un UpdatePanel funcionan sin reinicialización. $(documento).on("click.sigPanelLateral", "[data-sig-panel-abrir]", function (evento) { var destino = resolver($(this).attr("data-sig-panel-abrir")); if (destino) { evento.preventDefault(); abrir(destino, this); } }); $(documento).on("click.sigPanelLateral", "[data-sig-panel-cerrar]", function (evento) { var destino = $(this).attr("data-sig-panel-cerrar") || $(this).closest(".sig_panel_lateral"); if (resolver(destino)) { evento.preventDefault(); cerrar(destino); } }); $(documento).on("click.sigPanelLateral", ".sig_panel_fondo", function () { cerrar($.data(this, "sig.panel.propietario")); }); $(documento).on("keydown.sigPanelLateral", function (evento) { var estado = ultimoAbierto(); if (!estado || $(".modal.in:visible, .sweet-alert.visible, .swal2-container:visible, .select2-container--open, .select2-drop-active:visible").length) { return; } if (evento.key === "Escape") { evento.preventDefault(); cerrar(estado.panel); return; } if (evento.key !== "Tab") { return; } var controles = $(estado.panel).find("a[href], button, input, select, textarea, [tabindex]") .filter(":visible").filter(function () { return !this.disabled && $(this).attr("tabindex") !== "-1"; }); var primero = controles[0]; var ultimo = controles[controles.length - 1]; var dentro = estado.panel.contains(documento.activeElement); if (!primero) { evento.preventDefault(); estado.panel.focus(); } else if (evento.shiftKey && (documento.activeElement === primero || documento.activeElement === estado.panel || !dentro)) { evento.preventDefault(); ultimo.focus(); } else if (!evento.shiftKey && (documento.activeElement === ultimo || !dentro)) { evento.preventDefault(); primero.focus(); } }); // Si un UpdatePanel sustituye la ubicación original, libera la instancia antigua. // No agrega otro puente a PageRequestManager ni conserva paneles huérfanos en el formulario. var observador = new MutationObserver(function () { abiertos.slice().forEach(function (estado) { if (documento.documentElement.contains(estado.ubicacion) && documento.documentElement.contains(estado.panel)) { return; } var identificador = estado.panel.id; retirar(estado); $(estado.panel).remove(); if (estado.ubicacion && estado.ubicacion.parentNode) { estado.ubicacion.parentNode.removeChild(estado.ubicacion); } var reemplazo = identificador ? documento.getElementById(identificador) : null; if (reemplazo) { abrir(reemplazo); } }); }); observador.observe(documento.documentElement, { childList: true, subtree: true }); }(window, document, jQuery));