(function (window, $) { 'use strict'; if (!$) { console.error('FiltroSig requiere jQuery.'); return; } var menuFlotante = null; function cerrarMenuFlotante() { if (!menuFlotante) { return; } var actual = menuFlotante; menuFlotante = null; actual.control.removeClass('menu-open'); actual.flecha.attr('aria-expanded', 'false'); actual.menu.removeClass('filtrosig-menu-flotante').removeAttr('style'); if ($.contains(document, actual.control[0])) { actual.menu.detach().appendTo(actual.control); } else { actual.menu.remove(); } } function posicionarMenuFlotante() { if (!menuFlotante) { return; } var actual = menuFlotante; if (!$.contains(document, actual.control[0])) { cerrarMenuFlotante(); return; } var borde = actual.flecha[0].getBoundingClientRect(); var ancho = window.innerWidth; var alto = window.innerHeight; actual.menu.css({ width: Math.min(240, ancho - 12), minWidth: Math.min(210, ancho - 12), maxHeight: Math.max(50, alto - 12), left: 0, top: 0, right: 'auto', bottom: 'auto' }); var altura = actual.menu.outerHeight(); var arriba = borde.bottom + 4; if (arriba + altura > alto - 6) { arriba = Math.max(6, borde.top - altura - 4); } actual.menu.css({ left: Math.max(6, Math.min(borde.right - actual.menu.outerWidth(), ancho - actual.menu.outerWidth() - 6)), top: Math.min(arriba, Math.max(6, alto - altura - 6)) }); } function alternarMenuFlotante(control, menu, flecha) { var cerrar = menuFlotante && menuFlotante.control[0] === control[0]; cerrarMenuFlotante(); if (cerrar) { return; } menuFlotante = { control: control, menu: menu, flecha: flecha }; control.addClass('menu-open'); flecha.attr('aria-expanded', 'true'); menu.detach().appendTo(control.closest('form')).addClass('filtrosig-menu-flotante'); posicionarMenuFlotante(); } window.addEventListener('resize', posicionarMenuFlotante); document.addEventListener('scroll', function (evento) { if (menuFlotante && !menuFlotante.menu[0].contains(evento.target)) { posicionarMenuFlotante(); } }, true); $(document).on('keydown.sigFiltroMenu', function (evento) { if (evento.key === 'Escape') { cerrarMenuFlotante(); } }); var FILTRO_RENDERED = 'filtrosigrendered'; var FILTRO_HIDDEN = 'filtrosig-hidden'; var FILTRO_IGNORE = 'filtrosig-ignore'; /** * Inicializa todos los GridView marcados con: * * class="filtrosig" * * que todavía no hayan sido renderizados. */ window.RenderizarFiltrosSig = function () { $('table.filtrosig') .not('.' + FILTRO_RENDERED) .each(function () { var $grid = $(this); prepararFilasEspeciales($grid); var campos = obtenerCampos($grid); crearControl($grid, campos); $grid.addClass(FILTRO_RENDERED).attr('data-sig-filtro-inicializado', '1'); }); }; /** * Método público fijo para filtrar un GridView. * * grid: * DOM table, jQuery table o id. * * texto: * Texto a buscar. * * tipo: * all * column * data * * campo: * índice de columna o nombre del data. */ window.FiltrarGrid = function (grid, texto, tipo, campo) { var $grid = obtenerGrid(grid); if (!$grid.length) { return; } texto = normalizarTexto(texto); var $filas = obtenerFilasFiltrables($grid); $filas.each(function () { var $fila = $(this); var coincide = false; if (texto === '') { coincide = true; } else if (tipo === 'column') { coincide = filtrarPorColumna( $fila, campo, texto ); } else if (tipo === 'data') { coincide = filtrarPorData( $fila, campo, texto ); } else { coincide = filtrarTodo( $fila, texto ); } if (coincide) { /* * IMPORTANTE: * * Solo quitamos nuestra propia clase. * * NO modificamos: * * style="display:none" * clases externas * ocultamientos realizados por otras funciones. */ $fila.removeClass(FILTRO_HIDDEN); } else { /* * Esta es nuestra única marca para ocultar. */ $fila.addClass(FILTRO_HIDDEN); } }); actualizarContador($grid); }; /** * Obtiene el GridView. */ function obtenerGrid(grid) { if (grid instanceof jQuery) { return grid; } if (grid instanceof HTMLElement) { return $(grid); } if (typeof grid === 'string') { if (grid.charAt(0) === '#') { return $(grid); } return $('#' + grid); } return $(); } /** * Marca las filas que FiltroSig NO debe ocultar. */ function prepararFilasEspeciales($grid) { $grid.find('tr').each(function () { var $tr = $(this); /* * Si contiene TH es header. */ if ($tr.children('th').length > 0) { $tr.addClass(FILTRO_IGNORE); } /* * Compatibilidad con las filas ADtr * existentes en SIG. */ if ($tr.hasClass('ADtr')) { $tr.addClass(FILTRO_IGNORE); } }); /* * THEAD y TFOOT siempre se ignoran. */ $grid .find('thead tr, tfoot tr') .addClass(FILTRO_IGNORE); } /** * Obtiene: * * - Todos * - columnas visibles del GridView * - campos data adicionales */ function obtenerCampos($grid) { var campos = []; /* * TODOS */ campos.push({ tipo: 'all', campo: '', titulo: 'Todos' }); /* * COLUMNAS DEL GRIDVIEW */ var $headers = obtenerHeaders($grid); $headers.each(function (index) { var $th = $(this); /* * Permite excluir una columna: * * data-filtrosig-ignore="true" */ if ( String( $th.attr('data-filtrosig-ignore') ).toLowerCase() === 'true' ) { return; } var titulo = $.trim( $th.text() ); /* * Si no tiene título normalmente es * una columna de botones. */ if (!titulo) { return; } campos.push({ tipo: 'column', campo: index, titulo: titulo }); }); /* * DATAS ADICIONALES * * Ejemplo: * * data-filtrosig-datas= * "cliente:Cliente,etapa:Etapa,cvecliente:Clave cliente" */ var configuracionDatas = $grid.attr('data-filtrosig-datas'); if (configuracionDatas) { configuracionDatas .split(',') .forEach(function (item) { item = $.trim(item); if (!item) { return; } var partes = item.split(':'); var campo = $.trim(partes[0]); var titulo = partes.length > 1 ? $.trim( partes.slice(1).join(':') ) : campo; campos.push({ tipo: 'data', campo: campo, titulo: titulo }); }); } return campos; } /** * Obtiene los headers. */ function obtenerHeaders($grid) { var $headers = $grid.find('thead tr:first th'); if ($headers.length) { return $headers; } /* * Fallback para GridView sin * UseAccessibleHeader. */ return $grid.find('tr:first th'); } /** * Genera el control visual de búsqueda. */ function crearControl($grid, campos) { var gridId = $grid.attr('id'); /* * Si por alguna razón el GridView * no tiene ID, generamos uno. */ if (!gridId) { gridId = 'filtrosig_grid_' + Math.random() .toString(36) .substring(2, 10); $grid.attr( 'id', gridId ); } /* * CONTENEDOR */ var $contenedor = $('
', { 'class': 'filtrosig-container', 'data-filtrosig-grid': gridId }); /* * CONTROL */ var $control = $('
', { 'class': 'filtrosig-control' }); /* * INPUT * * autocomplete se coloca mediante attr() * para evitar conflicto con jQuery UI autocomplete. */ var $input = $('', { type: 'text', 'class': 'filtrosig-input', placeholder: 'Buscar...' }); $input.attr( 'autocomplete', 'off' ); /* * CONTENEDOR AZUL */ var $acciones = $('
', { 'class': 'filtrosig-actions' }); /* * BOTÓN BUSCAR * * Usa FontAwesome. */ var $buscar = $('