1031 lines
20 KiB
JavaScript
1031 lines
20 KiB
JavaScript
(function (window, $) {
|
|
|
|
'use strict';
|
|
|
|
if (!$) {
|
|
console.error('ScrollSig requiere jQuery.');
|
|
return;
|
|
}
|
|
|
|
var SCROLL_RENDERED = 'scrollsigrendered';
|
|
var SCROLL_CONTAINER = 'scrollsig-container';
|
|
var SCROLL_STICKY = 'scrollsig-sticky';
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* RENDERIZAR SCROLL SIG
|
|
* ============================================================
|
|
*
|
|
* Busca:
|
|
*
|
|
* table.scrollsig
|
|
*
|
|
* que todavía no tengan:
|
|
*
|
|
* scrollsigrendered
|
|
*/
|
|
window.RenderizarScrollSig = function () {
|
|
|
|
$('table.scrollsig')
|
|
.not('.' + SCROLL_RENDERED)
|
|
.each(function () {
|
|
|
|
var $grid = $(this);
|
|
|
|
renderizarGrid($grid);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* RENDERIZAR UN GRID
|
|
* ============================================================
|
|
*/
|
|
function renderizarGrid($grid) {
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* 1. EJECUTAR FUNCIÓN PREVIA
|
|
* --------------------------------------------------------
|
|
*
|
|
* Ejemplo:
|
|
*
|
|
* data-scrollfuncion="PrepararGrid"
|
|
*
|
|
* function PrepararGrid(grid) {
|
|
* }
|
|
*/
|
|
ejecutarFuncionPrevia($grid);
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* 2. CREAR CONTENEDOR
|
|
* --------------------------------------------------------
|
|
*/
|
|
crearContenedor($grid);
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* 3. CONFIGURAR TAMAÑO
|
|
* --------------------------------------------------------
|
|
*/
|
|
configurarTamano($grid);
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* 4. CONFIGURAR FILAS STICKY
|
|
* --------------------------------------------------------
|
|
*/
|
|
configurarFilasSticky($grid);
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* 5. MARCAR COMO RENDERIZADO
|
|
* --------------------------------------------------------
|
|
*/
|
|
$grid.addClass(
|
|
SCROLL_RENDERED
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* FUNCIÓN PREVIA
|
|
* ============================================================
|
|
*
|
|
* data-scrollfuncion="MiFuncion"
|
|
*
|
|
* Se busca:
|
|
*
|
|
* window.MiFuncion
|
|
*
|
|
* y se ejecuta:
|
|
*
|
|
* MiFuncion(grid);
|
|
*/
|
|
function ejecutarFuncionPrevia($grid) {
|
|
|
|
var nombreFuncion =
|
|
$.trim(
|
|
$grid.attr('data-scrollfuncion') || ''
|
|
);
|
|
|
|
|
|
if (!nombreFuncion) {
|
|
return;
|
|
}
|
|
|
|
|
|
var funcion =
|
|
obtenerFuncionGlobal(nombreFuncion);
|
|
|
|
|
|
if (typeof funcion !== 'function') {
|
|
|
|
console.warn(
|
|
'ScrollSig: no se encontró la función "' +
|
|
nombreFuncion +
|
|
'".'
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Mandamos el elemento TABLE real.
|
|
*/
|
|
funcion(
|
|
$grid[0]
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Permite incluso nombres como:
|
|
*
|
|
* MiFuncion
|
|
*
|
|
* MiModulo.PrepararGrid
|
|
*/
|
|
function obtenerFuncionGlobal(nombre) {
|
|
|
|
var partes =
|
|
nombre.split('.');
|
|
|
|
var contexto =
|
|
window;
|
|
|
|
|
|
for (
|
|
var i = 0;
|
|
i < partes.length;
|
|
i++
|
|
) {
|
|
|
|
if (
|
|
contexto === null ||
|
|
contexto === undefined
|
|
) {
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
contexto =
|
|
contexto[partes[i]];
|
|
}
|
|
|
|
|
|
return contexto;
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* CREAR CONTENEDOR
|
|
* ============================================================
|
|
*
|
|
* Antes:
|
|
*
|
|
* <table class="scrollsig">
|
|
*
|
|
*
|
|
* Después:
|
|
*
|
|
* <div class="scrollsig-container">
|
|
*
|
|
* <table class="scrollsig scrollsigrendered">
|
|
*
|
|
* </div>
|
|
*/
|
|
function crearContenedor($grid) {
|
|
|
|
/*
|
|
* Protección adicional.
|
|
*
|
|
* Si por alguna razón ya está dentro
|
|
* del contenedor no volvemos a envolverlo.
|
|
*/
|
|
if (
|
|
$grid.parent().hasClass(
|
|
SCROLL_CONTAINER
|
|
)
|
|
) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
$grid.wrap(
|
|
$('<div>', {
|
|
'class': SCROLL_CONTAINER
|
|
})
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* CONFIGURAR TAMAÑO
|
|
* ============================================================
|
|
*
|
|
* PRIORIDAD:
|
|
*
|
|
* 1. data-scrollmargintop
|
|
*
|
|
* 2. data-scrollsize
|
|
*
|
|
* 3. 300px
|
|
*/
|
|
function configurarTamano($grid) {
|
|
|
|
var $container =
|
|
obtenerContenedor($grid);
|
|
|
|
|
|
if (!$container.length) {
|
|
return;
|
|
}
|
|
|
|
|
|
var marginTop =
|
|
$.trim(
|
|
$grid.attr(
|
|
'data-scrollmargintop'
|
|
) || ''
|
|
);
|
|
|
|
|
|
var scrollSize =
|
|
$.trim(
|
|
$grid.attr(
|
|
'data-scrollsize'
|
|
) || ''
|
|
);
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* MARGIN TOP
|
|
* --------------------------------------------------------
|
|
*
|
|
* Ejemplo:
|
|
*
|
|
* data-scrollmargintop="250"
|
|
*
|
|
* viewport:
|
|
*
|
|
* 900px
|
|
*
|
|
* resultado:
|
|
*
|
|
* 650px
|
|
*/
|
|
if (marginTop !== '') {
|
|
|
|
var margen =
|
|
obtenerNumero(
|
|
marginTop
|
|
);
|
|
|
|
|
|
if (margen !== null) {
|
|
|
|
$container.attr(
|
|
'data-scrollsig-mode',
|
|
'margin'
|
|
);
|
|
|
|
$container.attr(
|
|
'data-scrollsig-margin',
|
|
margen
|
|
);
|
|
|
|
|
|
aplicarTamanoPorMargen(
|
|
$container,
|
|
margen
|
|
);
|
|
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* SIZE
|
|
* --------------------------------------------------------
|
|
*
|
|
* Ejemplos:
|
|
*
|
|
* data-scrollsize="500px"
|
|
*
|
|
* data-scrollsize="50vh"
|
|
*
|
|
* data-scrollsize="30rem"
|
|
*
|
|
* data-scrollsize="400"
|
|
*/
|
|
if (scrollSize !== '') {
|
|
|
|
scrollSize =
|
|
normalizarTamano(
|
|
scrollSize
|
|
);
|
|
|
|
|
|
$container.attr(
|
|
'data-scrollsig-mode',
|
|
'size'
|
|
);
|
|
|
|
|
|
$container.css(
|
|
'max-height',
|
|
scrollSize
|
|
);
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* --------------------------------------------------------
|
|
* DEFAULT
|
|
* --------------------------------------------------------
|
|
*/
|
|
$container.attr(
|
|
'data-scrollsig-mode',
|
|
'default'
|
|
);
|
|
|
|
|
|
$container.css(
|
|
'max-height',
|
|
'300px'
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* TAMAÑO POR MARGEN
|
|
* ============================================================
|
|
*/
|
|
function aplicarTamanoPorMargen(
|
|
$container,
|
|
margen
|
|
) {
|
|
|
|
var altura =
|
|
window.innerHeight -
|
|
margen;
|
|
|
|
|
|
/*
|
|
* Protección para evitar tamaños negativos
|
|
* o absurdamente pequeños.
|
|
*/
|
|
if (altura < 50) {
|
|
|
|
altura = 50;
|
|
}
|
|
|
|
|
|
$container.css(
|
|
'max-height',
|
|
altura + 'px'
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* NORMALIZAR TAMAÑO
|
|
* ============================================================
|
|
*
|
|
* 400
|
|
*
|
|
* se convierte en:
|
|
*
|
|
* 400px
|
|
*
|
|
*
|
|
* 400px
|
|
* 50vh
|
|
* 20rem
|
|
*
|
|
* se conservan.
|
|
*/
|
|
function normalizarTamano(valor) {
|
|
|
|
valor =
|
|
$.trim(
|
|
String(valor)
|
|
);
|
|
|
|
|
|
if (
|
|
/^\d+(\.\d+)?$/.test(
|
|
valor
|
|
)
|
|
) {
|
|
|
|
return valor + 'px';
|
|
}
|
|
|
|
|
|
return valor;
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* OBTENER NÚMERO
|
|
* ============================================================
|
|
*
|
|
* Acepta:
|
|
*
|
|
* 250
|
|
* 250px
|
|
*/
|
|
function obtenerNumero(valor) {
|
|
|
|
if (
|
|
valor === null ||
|
|
valor === undefined
|
|
) {
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
valor =
|
|
String(valor)
|
|
.replace('px', '')
|
|
.trim();
|
|
|
|
|
|
var numero =
|
|
parseFloat(
|
|
valor
|
|
);
|
|
|
|
|
|
if (isNaN(numero)) {
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
return numero;
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* FILAS STICKY
|
|
* ============================================================
|
|
*
|
|
* data-scrollrowsheader="1"
|
|
*
|
|
* Por defecto:
|
|
*
|
|
* 1
|
|
*
|
|
*
|
|
* Si:
|
|
*
|
|
* data-scrollrowsheader="3"
|
|
*
|
|
* las primeras 3 filas serán sticky.
|
|
*
|
|
*
|
|
* IMPORTANTE:
|
|
*
|
|
* El sticky se aplica EXCLUSIVAMENTE al TR.
|
|
*
|
|
* NO se aplica:
|
|
*
|
|
* position: sticky
|
|
* top
|
|
* z-index
|
|
*
|
|
* a TH ni TD.
|
|
*/
|
|
function configurarFilasSticky($grid) {
|
|
|
|
/*
|
|
* Primero limpiamos cualquier configuración
|
|
* anterior por seguridad.
|
|
*/
|
|
limpiarSticky($grid);
|
|
|
|
|
|
var cantidad =
|
|
parseInt(
|
|
$grid.attr(
|
|
'data-scrollrowsheader'
|
|
),
|
|
10
|
|
);
|
|
|
|
|
|
/*
|
|
* Por defecto la primera fila.
|
|
*/
|
|
if (
|
|
isNaN(cantidad)
|
|
) {
|
|
|
|
cantidad = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Permitir explícitamente:
|
|
*
|
|
* data-scrollrowsheader="0"
|
|
*
|
|
* para no tener sticky.
|
|
*/
|
|
if (cantidad <= 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Tomamos las primeras N filas
|
|
* reales de la tabla.
|
|
*/
|
|
var $filas =
|
|
obtenerFilasSuperiores(
|
|
$grid,
|
|
cantidad
|
|
);
|
|
|
|
|
|
if (!$filas.length) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Empezamos en -1px.
|
|
*
|
|
* Esto desplaza todo el bloque sticky
|
|
* 1px hacia arriba.
|
|
*
|
|
* Evita la pequeña línea transparente
|
|
* que puede aparecer encima del header.
|
|
*
|
|
* Si tenemos varias filas sticky:
|
|
*
|
|
* fila 1:
|
|
*
|
|
* top = -1
|
|
*
|
|
* fila 2:
|
|
*
|
|
* top = altura fila 1 - 1
|
|
*
|
|
* fila 3:
|
|
*
|
|
* top = altura fila 1
|
|
* + altura fila 2
|
|
* - 1
|
|
*/
|
|
var topAcumulado = -1;
|
|
|
|
|
|
$filas.each(function (indice) {
|
|
|
|
var $fila =
|
|
$(this);
|
|
|
|
|
|
/*
|
|
* IMPORTANTE:
|
|
*
|
|
* Toda la configuración sticky
|
|
* se aplica directamente al TR.
|
|
*/
|
|
$fila
|
|
.addClass(
|
|
SCROLL_STICKY
|
|
)
|
|
.css({
|
|
'top': topAcumulado + 'px',
|
|
'z-index': 3 - indice
|
|
});
|
|
|
|
|
|
/*
|
|
* Obtener altura REAL de la fila.
|
|
*
|
|
* Se utiliza para posicionar la
|
|
* siguiente fila sticky.
|
|
*/
|
|
var altura =
|
|
$fila.outerHeight();
|
|
|
|
|
|
if (
|
|
!altura ||
|
|
altura < 1
|
|
) {
|
|
|
|
altura = 0;
|
|
}
|
|
|
|
|
|
topAcumulado +=
|
|
altura;
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* OBTENER PRIMERAS N FILAS
|
|
* ============================================================
|
|
*
|
|
* Preferimos THEAD.
|
|
*
|
|
* Si el GridView no genera THEAD,
|
|
* usamos las primeras filas de la tabla.
|
|
*/
|
|
function obtenerFilasSuperiores(
|
|
$grid,
|
|
cantidad
|
|
) {
|
|
|
|
var $filasThead =
|
|
$grid.find(
|
|
'> thead > tr'
|
|
);
|
|
|
|
|
|
/*
|
|
* Si THEAD tiene suficientes filas,
|
|
* utilizamos esas.
|
|
*/
|
|
if ($filasThead.length) {
|
|
|
|
var $resultado =
|
|
$filasThead.slice(
|
|
0,
|
|
cantidad
|
|
);
|
|
|
|
|
|
/*
|
|
* Si necesitamos más filas que las
|
|
* disponibles en THEAD, completamos
|
|
* con TBODY.
|
|
*/
|
|
if (
|
|
$resultado.length <
|
|
cantidad
|
|
) {
|
|
|
|
var faltantes =
|
|
cantidad -
|
|
$resultado.length;
|
|
|
|
|
|
var $tbody =
|
|
$grid
|
|
.find(
|
|
'> tbody > tr'
|
|
)
|
|
.slice(
|
|
0,
|
|
faltantes
|
|
);
|
|
|
|
|
|
$resultado =
|
|
$resultado.add(
|
|
$tbody
|
|
);
|
|
}
|
|
|
|
|
|
return $resultado;
|
|
}
|
|
|
|
|
|
/*
|
|
* GridView sin THEAD.
|
|
*/
|
|
return $grid
|
|
.find('> tbody > tr')
|
|
.slice(
|
|
0,
|
|
cantidad
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* LIMPIAR STICKY
|
|
* ============================================================
|
|
*
|
|
* Ahora solamente limpiamos el TR.
|
|
*
|
|
* Ya NO existe:
|
|
*
|
|
* scrollsig-sticky-cell
|
|
*/
|
|
function limpiarSticky($grid) {
|
|
|
|
$grid
|
|
.find('.' + SCROLL_STICKY)
|
|
.removeClass(
|
|
SCROLL_STICKY
|
|
)
|
|
.css({
|
|
'top': '',
|
|
'z-index': ''
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* OBTENER CONTENEDOR
|
|
* ============================================================
|
|
*/
|
|
function obtenerContenedor($grid) {
|
|
|
|
return $grid.closest(
|
|
'.' + SCROLL_CONTAINER
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* RECALCULAR SCROLLSIG
|
|
* ============================================================
|
|
*
|
|
* Método público por si en algún momento
|
|
* necesitas recalcular manualmente:
|
|
*
|
|
* RecalcularScrollSig();
|
|
*
|
|
* o:
|
|
*
|
|
* RecalcularScrollSig('GvTramites');
|
|
*/
|
|
window.RecalcularScrollSig = function (
|
|
grid
|
|
) {
|
|
|
|
/*
|
|
* Un Grid específico.
|
|
*/
|
|
if (grid) {
|
|
|
|
var $grid =
|
|
obtenerGrid(
|
|
grid
|
|
);
|
|
|
|
|
|
if (!$grid.length) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
recalcularGrid(
|
|
$grid
|
|
);
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Todos los ScrollSig.
|
|
*/
|
|
$('table.scrollsig.' + SCROLL_RENDERED)
|
|
.each(function () {
|
|
|
|
recalcularGrid(
|
|
$(this)
|
|
);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* RECALCULAR GRID
|
|
* ============================================================
|
|
*/
|
|
function recalcularGrid($grid) {
|
|
|
|
var $container =
|
|
obtenerContenedor(
|
|
$grid
|
|
);
|
|
|
|
|
|
if (!$container.length) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Si usa margin top recalculamos
|
|
* contra window.innerHeight.
|
|
*/
|
|
if (
|
|
$container.attr(
|
|
'data-scrollsig-mode'
|
|
) === 'margin'
|
|
) {
|
|
|
|
var margen =
|
|
parseFloat(
|
|
$container.attr(
|
|
'data-scrollsig-margin'
|
|
)
|
|
);
|
|
|
|
|
|
if (!isNaN(margen)) {
|
|
|
|
aplicarTamanoPorMargen(
|
|
$container,
|
|
margen
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Recalcular TOP de sticky rows.
|
|
*
|
|
* Esto es importante si alguna función
|
|
* cambió la altura de los headers.
|
|
*/
|
|
configurarFilasSticky(
|
|
$grid
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* OBTENER GRID
|
|
* ============================================================
|
|
*/
|
|
function obtenerGrid(grid) {
|
|
|
|
if (grid instanceof jQuery) {
|
|
|
|
return grid;
|
|
}
|
|
|
|
|
|
if (
|
|
typeof HTMLElement !== 'undefined' &&
|
|
grid instanceof HTMLElement
|
|
) {
|
|
|
|
return $(grid);
|
|
}
|
|
|
|
|
|
if (
|
|
typeof grid === 'string'
|
|
) {
|
|
|
|
if (
|
|
grid.charAt(0) === '#'
|
|
) {
|
|
|
|
return $(grid);
|
|
}
|
|
|
|
|
|
return $(
|
|
'#' + grid
|
|
);
|
|
}
|
|
|
|
|
|
return $();
|
|
}
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* RESIZE DE VENTANA
|
|
* ============================================================
|
|
*
|
|
* No ejecutamos el cálculo inmediatamente en cada pixel
|
|
* que cambia el navegador.
|
|
*
|
|
* Esperamos 100 ms para evitar trabajo excesivo.
|
|
*/
|
|
var timeoutResize = null;
|
|
|
|
|
|
$(window).on(
|
|
'resize.scrollsig',
|
|
function () {
|
|
|
|
if (
|
|
timeoutResize !== null
|
|
) {
|
|
|
|
clearTimeout(
|
|
timeoutResize
|
|
);
|
|
}
|
|
|
|
|
|
timeoutResize =
|
|
setTimeout(
|
|
function () {
|
|
|
|
RecalcularScrollSig();
|
|
|
|
timeoutResize = null;
|
|
|
|
},
|
|
100
|
|
);
|
|
}
|
|
);
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* PRIMERA CARGA
|
|
* ============================================================
|
|
*/
|
|
$(function () {
|
|
|
|
RenderizarScrollSig();
|
|
});
|
|
|
|
|
|
/**
|
|
* ============================================================
|
|
* ASP.NET WEBFORMS / UPDATEPANEL
|
|
* ============================================================
|
|
*
|
|
* Si un UpdatePanel reemplaza el GridView:
|
|
*
|
|
* el nuevo TABLE ya no tendrá:
|
|
*
|
|
* scrollsigrendered
|
|
*
|
|
* por lo tanto se procesará nuevamente.
|
|
*/
|
|
if (
|
|
typeof Sys !== 'undefined' &&
|
|
Sys.WebForms &&
|
|
Sys.WebForms.PageRequestManager
|
|
) {
|
|
|
|
Sys.WebForms.PageRequestManager
|
|
.getInstance()
|
|
.add_endRequest(
|
|
function () {
|
|
|
|
RenderizarScrollSig();
|
|
|
|
/*
|
|
* Recalculamos también los grids que
|
|
* sobrevivieron al postback parcial.
|
|
*/
|
|
RecalcularScrollSig();
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
})(window, window.jQuery); |