(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));