MediaWiki:Common.js/Clases/TableManager.js
De WikiDex
Ir a la navegaciónIr a la búsqueda
Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.
- Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
- Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
- Internet Explorer/Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
- Opera: Presiona Ctrl+F5.
//<pre>
/************************************/
/* TableManager: Permite mostrar/ocultar y mover las columnas de una tabla a voluntad, y ordenar las filas por una o varias columnas
* Copyright (C) 2008 Jesús Martínez Novo ([[User:Ciencia Al Poder]])
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* @param {HTMLTable} table: Tabla sobre la que actuar
* @param {object} cfg: (opcional) Configuración adicional:
* unsortable:{bool} Indica si debe deshabilitar la ordenación. En ese caso no aparecerán controles de ordenación. El mismo efecto se consigue con class="unsortable" en la tabla
* emptysort:{number} Indica la posición de las celdas vacías: 1: siempre arriba (independientemente de la ordenación), -1:siempre abajo (ídem), 0:lo determinará la función de ordenación
* sortCfg:{obj} Funciones de ordenación. El nombre de cada objeto se usará como CSSClass en los botones de ordenación (tm-sort-{nombre} (y sufijo -asc o -dsc según estado actual)). También sirve para determinar desde el inicio la función de ordenación a comprobar, oniendo como CSSClass en la columna sort{nombre}
* {
* testFn: {function} función que determina si debe ser ésta la ordenación. Parámetros: 1:celda 2:texto de la celda. Si es null se asume que debe usarse éste parámetro de ordenación y no comprueba el resto de opciones. Útil para la última función.
* sortFn: {function} función de ordenación (compatible con funciones de SortableTables de MediaWiki). Parámetros: a,b = Array[1:fila, 2:texto de la celda, 3:índice actual de fila, en negativo si se ordena al revés, 4:celda].
* priority: {number} prioridad para determinar qué reglas se comprobarán antes
* title: {string} Título del tipo de datos de ordenación, para el texto de los botones.
* }
*/
function TableManager(table,cfg){
cfg = cfg||{};
// {obj} Configuraciones
this.cfg = { unsortable:false, emptysort:0 };
YAHOO.lang.augmentObject(this.cfg,cfg,true);
// {HTMLTable} Tabla asociada
this.table = table;
// {bool} Si se ha cargado la configuración de la tabla
this.tableInit = false;
// {bool} Recargar datos de diálogo
this.reloadDlg = true;
// {YUIDialog} Diálogo
this.dlg = null;
this.sortCfg = {
number: {testFn:function(c,t){
return (t=='') || (t=='-') || /^([-]\s*)?(\d+|\d{1,3}([.]\d{3})+)(,\d+)?(\s*[%\u00a3$\u20ac])?$/.test(t);
}, sortFn:function(a,b){
var A = a[1], B = b[1];
if (A=='') return B=='' ? 0 : -1;
else if (B == '') return 1;
if (A=='-') A = '0';
if (B=='-') B = '0';
var aa = parseFloat(A.replace(/[.\s%\u00a3$\u20ac]/g,'').replace(',','.')), bb = parseFloat(B.replace(/[.\s%\u00a3$\u20ac]/g,'').replace(',','.'));
return (isNaN(aa) ? 0 : aa) - (isNaN(bb) ? 0 : bb);
}, priority:1, title:'numérico'},
text: {testFn:null, sortFn:function(a,b) {
var A = a[1].toLowerCase(), B = b[1].toLowerCase();
return (A<B ? -1 : A>B ? 1 : 0);
}, priority:99, title:'alfabético'}
};
if (cfg.sortCfg) this.sortCfg = YAHOO.lang.augmentObject(this.sortCfg,cfg.sortCfg,true);
this.init();
}
TableManager.prototype = {
version:'1.1',
init: function(args){
var mb = $T.create('span', {'class':'menubtn'}, 'opciones de tabla');
var panel = $T.create('div', {'class':'tm-ctrl noprint'}, [mb]);
this.table.parentNode.insertBefore(panel,this.table);
$E.on(mb,'click',this.menuClick,null,this);
},
menuClick:function(e){
if (!this.tableInit) this.initTable();
this.showDialog(e);
},
// Inicialización y lectura de las propiedades de la tabla
initTable:function(){
for (var i=0, cs = this.table.rows[0].cells; i < cs.length; i++){
cs[i].tmColDfn = {
// {number} índice original
originalIndex:cs[i].cellIndex,
// {number} nuevo índice después de mover
newIndex:cs[i].cellIndex,
// {bool} indica si está visible
visible:true,
// {object}
// state: {number} 0: sin ordenar, 1: ascendente, -1:descendente
// oldState: {number} 0: sin ordenar, 1: ascendente, -1:descendente
// sortFn: {function} Función de ordenación.
// title: {string} título
// name: {string} nombre para className
// priority: {number} Prioridad en ordenación multi-columna
// oldPriority: {number} Prioridad anterior
sort:null
};
}
// Si se define que la tabla no debe permitir ordenación, no hace falta continuar
if ((' '+this.table.className+' ').indexOf(' unsortable ') != -1) this.cfg.unsortable = true;
if (this.cfg.unsortable || !this.sortCfg){
this.tableInit = true;
return;
}
// Determinamos la ordenación
// Generamos opciones de ordenación por prioridad
var cSort=[];
for (var item in this.sortCfg){
this.sortCfg[item].name = item;// Lo necesitamos para obtener el nombre después
cSort.push(this.sortCfg[item]);
}
if (!cSort.length){
this.tableInit = true;
return;
}
cSort.sort(function(a,b){ return (a.priority||0)-(b.priority||0); });
var kSort=[]; // Índice de función de ordenación
// Primero si lo han definido en la propia columna
for (var i=0, cs = this.table.rows[0].cells; i < cs.length; i++){
// Si la columna tiene class unsortable, le deshabilitamos orden
if ((' '+cs[i].className+' ').indexOf(' unsortable ') != -1){
kSort[i] = -1;
continue;
}
for (var j=0; j < cSort.length; j++){
if ((' '+cs[i].className+' ').indexOf(' sort'+cSort[j].name+' ') != -1){
kSort[i] = j;
break;
}
}
}
// Determinamos la ordenación a nivel de columna
// Recorremos las filas para determinar el orden
for (var i=1, rs = this.table.rows; i < rs.length; i++){
var cSkip = 0;// Celdas ya completas
for (var j=0, cs = rs[i].cells; j < cs.length; j++){
kSort[j] = (kSort[j]||0);
// Condición de salida rápida: Si no es ordenable, o no tiene testFn
if (kSort[j] == -1 || !cSort[kSort[j]].testFn ){
cSkip++;
continue;
}
var t = this.getTextContent(cs[j]);
// Miramos si se saltan las celdas vacías
if (this.cfg.emptysort != 0 && t=='') continue;
// Recorremos las funciones de ordenación para encontrar la que funcione
for (var k = kSort[j]; k < cSort.length; k++){
if (!cSort[k].testFn || cSort[k].testFn(cs[j],t)){
kSort[j] = k;
break;
}
// Si no ha pasado la última función, no es ordenable
if (k == cSort.length - 1) kSort[j] = -1;
}
}
// Salida rápida
if (cSkip == cs.length) break;
}
// Indicamos la ordenación final
for (var i=0, cs = this.table.rows[0].cells; i < cs.length; i++){
if (typeof kSort[i] != 'undefined' && kSort[i] != -1){
var s = cSort[kSort[i]];
cs[i].tmColDfn.sort = {state:0, oldState:0, sortFn:s.sortFn, title:s.title, name:s.name, priority:0, oldPriority:0};
}
}
this.tableInit = true;
},
// Devuelve el valor en texto de un elemento HTML
getTextContent:function(el){
if ((el.nodeType == 1 && el.getElementsByTagName('img').lenght == 0) || el.nodeType != 1) return $T.trim(el.textContent || el.innerText || '');
var str = '', cs = el.childNodes;
for (var i = 0; i < cs.length; i++) {
if (cs[i].nodeType == 1){ //ELEMENT
if (cs[i].tagName.toLowerCase() == 'img') str += cs[i].alt;
else str += this.getTextContent(cs[i]);
} else if (cs[i].nodeType == 3){ //TEXT
str += cs[i].nodeValue;
}
}
return $T.trim(str);
},
showDialog:function(e){
$E.stopEvent(e);
if (this.reloadDlg) this.genDialog();
this.dlg.show();
this.dlg.center();
// Se hace aquí porque se desactivará al ocultar
this.dlg.cfg.setProperty('constraintoviewport',true);
},
// Crea el diálogo
genDialog:function(){
// Diálogo base
if (!this.dlg){
var el = $T.create('div');
$D.generateId(el,'tm-dlg');
document.body.appendChild(el);
this.dlg = new YAHOO.widget.Dialog(el, {
visible: false,
modal: true,
dragOnly: true,
buttons: [
{ text: 'Aplicar cambios', handler: { fn: this.applyChanges, scope: this } },
{ text: 'Cancelar', handler: function(){ this.cancel();} }
]
});
this.dlg.setHeader('Controles de tabla');
this.dlg.subscribe('beforeHide', function(){
// Desactivamos propiedad porque sino no dejará hacer la siguiente instrucción
this.cfg.setProperty('constraintoviewport',false);
// Lo posicionamos fuera de los márgenes porque en FF2(X11) aun oculto se ve la silueta semitransparente del diálogo
this.moveTo(-10000,-10000);
});
// Si ha cancelado, dejamos opciones como estaban
this.dlg.subscribe('cancel', function(){ this.discardChanges(); }, null, this);
// Hasta que no se renderiza no hay body. Si aun no existe, encolamos el evento. Una vez se haya hecho no se debería volver a agregar porque el elemento no se destruye.
if (!this.dlg.body){
this.dlg.subscribe('render', function(){
$E.addListener(this.dlg.body,'mousedown',this.beginMove,null,this);
$E.addListener(this.dlg.body,'click',this.dlgClick,null,this);
}, null, this);
}
// Si se sobrescribe el contenido, borramos referencias
this.dlg.subscribe('changeBody', function(){ this.moveArgs.locator = null; }, null, this);
$D.addClass(this.dlg.element,'tm-dialog');
// Datos para mover columnas:
// target: [HTMLTableCell] celda que inició el movimiento
// proxy: [HTMLDiv] Div del contorno de lo que se mueve
// locator: [HTMLDiv] Div indicando la posición destino donde se mueve
// visible: [bool] Indica si el contorno se muestra
// rowRegionCache: [Array([Array([YUIRegion],[HTMLTableRow])])] Array de colección de region y TableRow para obtener la posición
this.moveArgs = {target:null, proxy:null, locator:null, visible:false, rowRegionCache:null};
}
// Tabla con el contenido
var t = $T.create('table');
// Informamos el contenido
this.dlg.setBody(t);
for (var i=0, cs = this.table.rows[0].cells; i < cs.length; i++){
var d = cs[i].tmColDfn;
if (!d) continue;
var r = t.insertRow(t.rows.length);
// Asignamos una referencia a la celda
r.tmCell = cs[i];
this.dlg.cplTBody = t.tBodies[0];// Atajo
// Check de "Visible"
var c1 = r.insertCell(0);
c1.className = 'tm-tc-vis';
var chk = $T.create('input', { type:'checkbox' });
if (cs[i].tmColDfn.visible) chk.checked = true;
c1.appendChild(chk);
// Título de la columna
var c2 = r.insertCell(1);
c2.className = 'tm-tc-col';
c2.appendChild(document.createTextNode( cs[i].innerText ? cs[i].innerText : cs[i].textContent ));
if (!this.cfg.unsortable){
// Botón ordenación
var c2 = r.insertCell(2);
c2.className = 'tm-tc-sort';
if (!d.sort) c2.appendChild($T.create('div', { 'class':'tm-unsortable', title:'Sin orden' }));
else {
c2.appendChild($T.create('div', [$T.create('span',{'class':'tm-sortpri'},'')]));
this.changeSortState(i,0);
}
}
}
// Header
var thr = t.createTHead().insertRow(0);
thr.insertCell(0).appendChild($T.create('span', {title:'Sólo se mostrarán las columnas que estén marcadas'}, 'Mostrar'));
thr.insertCell(1).appendChild($T.create('span', {title:'Puedes mover las columnas de posición con el mouse, haciendo clic y moviéndolo sin soltar el botón'}, 'Columna'));
if (!this.cfg.unsortable){
thr.insertCell(2).appendChild($T.create('span', {title:'Selecciona la columna por la que se ordenarán las filas'}, 'Orden'));
var c = $T.create('input', {type:'checkbox'});
this.dlg.body.appendChild($T.create('div', {'class':'tm-options'}, [c,$T.create('label',{'for':$D.generateId(c,'tm-chk')},'Permitir ordenar múltiples columnas')]));
this.dlg.multiSort = c;// Atajo
}
// Renderizamos
this.reloadDlg = false;
this.dlg.render();
},
// Evento en MouseDown
beginMove:function(e){
if (e.button!=0 || e.ctrlKey || e.shiftKey || e.altKey) return;
$E.stopEvent(e);
var target = $E.getTarget(e);
// Sólo si estamos sobre el nombre de columna
if (target.tagName.toLowerCase() != 'td' || target.className != 'tm-tc-col') return;
// Queremos trabajar sobre la row
target = target.parentNode;
this.moveArgs.target = target;
// Agregamos eventos
$E.addListener(document.body,'mousemove',this.colMove,null,this);
$E.addListener(document.body,'mouseup',this.endMove,null,this);
// Creamos indicadores
if (!this.moveArgs.proxy){
this.moveArgs.proxy = $T.create('div', {'class':'tm-dlg-proxy', style:'display:none;'});
// Lo situamos fuera el body para que no se elimine si cambia éste
this.dlg.element.appendChild(this.moveArgs.proxy);
}
if (!this.moveArgs.locator){
this.moveArgs.locator = $T.create('div', {'class':'tm-dlg-locator', style:'display:none;'});
this.dlg.body.appendChild(this.moveArgs.locator);
}
// Region de TR
var reg = $D.getRegion(target);
$D.setStyle([this.moveArgs.proxy,this.moveArgs.locator], 'width', (reg.right - reg.left).toString()+'px');
$D.setStyle(this.moveArgs.proxy, 'height', (reg.bottom - reg.top).toString()+'px');
// Posicionamos proxy en mitad del puntero
$D.setStyle(this.moveArgs.proxy, 'display', 'block');
$D.setXY(this.moveArgs.proxy,[ ( $E.getXY(e)[0] - (reg.right - reg.left)/2 ) , ( $E.getXY(e)[1] - (reg.bottom - reg.top)/2 ) ]);
// Esto para que no se seleccione texto al mover
this.moveArgs.proxy.focus();
// El otro sólo lo posicionamos, pero no lo mostramos
$D.setX(this.moveArgs.locator, reg.left);
// Creamos la cache de region de todas las filas
this.moveArgs.rowRegionCache = new Array();
// Agregamos primero el contenedor para agilizar la detección de que estamos fuera:
var tb = this.dlg.cplTBody;
this.moveArgs.rowRegionCache.push([$D.getRegion(tb),null]);
for (var i=0; i < tb.rows.length; i++) this.moveArgs.rowRegionCache.push([$D.getRegion(tb.rows[i]), tb.rows[i]]);
// Agregamos clase para marcar que está seleccionada.
if ((' '+target.className+' ').indexOf(' m-selected ') == -1) target.className += ' m-selected';
},
// Evento en MouseMove
colMove:function(e){
$E.stopEvent(e);
// Posicionamos proxy en la mitad del puntero
var reg = $D.getRegion(this.moveArgs.proxy);
var eXY = $E.getXY(e);
$D.setXY(this.moveArgs.proxy,[ ( $E.getXY(e)[0] - (reg.right - reg.left)/2 ) , ( $E.getXY(e)[1] - (reg.bottom - reg.top)/2 ) ]);
var rowAct = this.getRowByMousePos(e);
if (!rowAct){
$D.setStyle(this.moveArgs.locator, 'display', 'none');
this.moveArgs.visible = false;
return;
}
if (!this.moveArgs.visible){
$D.setStyle(this.moveArgs.locator, 'display', 'block');
this.moveArgs.visible = true;
}
// Posicionamos el indicador entre dos filas, es decir, a partirde la fila donde estamos, obtenemos si estamos más cerca de arriba o de abajo.
var yCentroRow = rowAct[0].top + (rowAct[0].bottom - rowAct[0].top)/2;
$D.setY(this.moveArgs.locator, ( ( eXY[1] < yCentroRow ? rowAct[0].top : rowAct[0].bottom ) - 1));
},
// Evento en MouseUp
endMove:function(e){
$E.removeListener(document.body,'mousemove',this.colMove);
$E.removeListener(document.body,'mouseup',this.endMove);
var v = this.moveArgs.visible;
// Ocultamos indicadores de posición
$D.setStyle([this.moveArgs.proxy,this.moveArgs.locator], 'display', 'none');
this.moveArgs.visible = false;
if (this.moveArgs.target && (' '+this.moveArgs.target.className+' ').indexOf(' m-selected ') != -1)
this.moveArgs.target.className = $T.trim((' '+this.moveArgs.target.className+' ').replace(' m-selected ',' '));
// Si el indicador no estaba visible es que no es un destino válido.
if (!v) return;
// Obtenemos el destino de la fila
var rowAct = this.getRowByMousePos(e);
if (!rowAct) return;
var yCentroRow = rowAct[0].top + (rowAct[0].bottom - rowAct[0].top)/2;
if ($E.getXY(e)[1] < yCentroRow) rowAct[1].parentNode.insertBefore(this.moveArgs.target,rowAct[1]);
else if (rowAct[1].nextSibling) rowAct[1].parentNode.insertBefore(this.moveArgs.target,rowAct[1].nextSibling);
else rowAct[1].parentNode.appendChild(this.moveArgs.target);
// Vaciamos el cache, porque ha cambiado.
this.moveArgs.rowRegionCache = null;
this.moveArgs.target = null;
},
// Obtiene la row sobre la que se encuentra el puntero
// @param {event} e
// @returns {Array({region},{HTMLTableRow})}
getRowByMousePos:function(e){
var rowAct = null, eXY = $E.getXY(e), a = this.moveArgs.rowRegionCache, cReg = a[0][0];
// Si no estamos en los límites del contenedor, salimos
if (cReg.left > eXY[0] || cReg.right < eXY[0] || cReg.top > eXY[1] || cReg.bottom < eXY[1]) return null;
for (var i=1; i < a.length; i++){
// Ahora miramos sólo límites verticales
if (a[i][0].top <= eXY[1] && a[i][0].bottom >= eXY[1]){
rowAct = a[i];
break;
}
}
return rowAct;
},
// Evento en Click
dlgClick: function(e){
var target = $E.getTarget(e);
if (target.tagName.toLowerCase() != 'div' || target.parentNode.className != 'tm-tc-sort') return;
var r = target.parentNode.parentNode, s = r.tmCell.tmColDfn.sort;
// Inicializamos todos menos el actual
for (var i = 0, rs = this.dlg.cplTBody.rows; i < rs.length; i++){
if (i == r.rowIndex - 1) this.changeSortState(i);
else if (!this.dlg.multiSort.checked) this.changeSortState(i,0);
}
this.rebuildMultiSort();
},
// Cambia el estado del botón de ordenación. Si no se especifica estado, rota el estado actual
changeSortState:function(pos, newState){
var r = this.dlg.cplTBody.rows[pos];
var bs = r.cells[2].firstChild, s = r.tmCell.tmColDfn.sort;
if (!s) return;
if (typeof newState == 'undefined'){
if (s.state == 1) s.state = -2;
s.state++;
} else s.state = newState;
bs.className = 'tm-sort' + s.name + (s.state < 0 ? '-dsc tm-sort-dsc' : (s.state > 0 ? '-asc tm-sort-asc' : ' tm-sort'));
bs.title = s.state ? 'Orden ' + s.title + ' ' + (s.state < 0 ? 'de mayor a menor' : 'de menor a mayor' ) : 'Sin ordenar (tipo ' + s.title + ')';
if (!s.state) s.priority = 0;
},
// Sincroniza la prioridad de ordenación
rebuildMultiSort:function(){
var aSC = [], mc = this.dlg.multiSort.checked;
for (var i=0, rs = this.dlg.cplTBody.rows; i < rs.length; i++){
var s = rs[i].tmCell.tmColDfn.sort;
if (s) aSC.push([rs[i].cells[2].firstChild.firstChild.firstChild, s]);
}
aSC.sort(function(a,b){
if (!a[1].state) return b[1].state ? 1 : 0;
else if (!b[1].state) return -1;
// Si tiene ordenación pero no prioridad, es que es la última que se ha pulsado
return a[1].priority ? a[1].priority - (b[1].priority || a[1].priority + 1) : 1;
});
// Prioridades desde 1
for (var i=0; i < aSC.length; i++){
if (aSC[i][1].state){
aSC[i][1].priority = i + 1;
aSC[i][0].data = mc ? (i + 1).toString() : '';
} else aSC[i][0].data = '';
}
},
// Sincroniza controles con el estado de la tabla
discardChanges: function(){
var rs = this.dlg.cplTBody.rows;
var arRows = [];
// Visibilidad
for (var i = 0; i < rs.length; i++){
var d = rs[i].tmCell.tmColDfn, s = d.sort;
rs[i].cells[0].firstChild.checked = d.visible;
if (!this.cfg.unsortable && s){
this.changeSortState(i,s.oldState);
s.priority = s.oldPriority;
}
arRows.push(rs[i]);
}
if (!this.cfg.unsortable){
this.dlg.multiSort.checked = this.dlg.multiSort.defaultChecked;
this.rebuildMultiSort();
}
// Distribución filas
arRows.sort(function(a,b){return a.tmCell.cellIndex - b.tmCell.cellIndex;});
$T.makeChildren(arRows,this.dlg.cplTBody);
},
// Sincroniza la tabla con el estado de los controles
applyChanges:function(){
$D.setStyle(document.body,'cursor','wait');
var rs = this.dlg.cplTBody.rows;
var tagVis = false, cfgVis = [], tagPos = false, tagSort = false, cfgSort = [];
for (var i = 0; i < rs.length; i++){
var c = rs[i].tmCell, d = c.tmColDfn;
if (rs[i].cells[0].firstChild.checked != d.visible){
tagVis = true;
cfgVis[c.cellIndex] = {changed: true, value: rs[i].cells[0].firstChild.checked};
} else cfgVis[c.cellIndex] = null;
if (c.cellIndex != i) tagPos = true;
d.newIndex = i;
if (!this.cfg.unsortable && d.sort){
if (d.sort.state == 0){ // Si dejan de estar ordenadas todas, no haremos nada. En ese caso actualizamos aquí las propiedades
d.sort.oldState = 0;
d.sort.oldPriority = 0;
} else {
cfgSort.push(c);
if (d.sort.state != d.sort.oldState) tagSort = true;
}
}
}
if (!this.cfg.unsortable) this.dlg.multiSort.defaultChecked = this.dlg.multiSort.checked;
// ocultando el elemento parece que es más rápido
$D.setStyle(this.table,'display','none');
var ex = null;
try{
if (tagVis) this.applyVisibility(cfgVis);
if (tagPos) this.applyColumnPosition();
if (tagSort) this.applySort(cfgSort);
}catch(e){
ex = e;
}
$D.setStyle(this.table,'display','');
$D.setStyle(document.body,'cursor','');
this.dlg.hide();
if (ex) throw ex;
},
// Aplica cambios de visibilidad en columnas de la tabla
applyVisibility:function(cfgVis){
for (var i = 0, rs = this.table.rows; i < rs.length; i++){
for (var j = 0, cs = rs[i].cells; j < cs.length; j++){
if (!cfgVis[j] || !cfgVis[j].changed) continue;
$D.setStyle(cs[j], 'display', (cfgVis[j].value ? '' : 'none'));
// Actualizamos la configuración
if (i == 0) cs[j].tmColDfn.visible = cfgVis[j].value;
}
}
},
// Aplica cambios de posición de columnas
applyColumnPosition:function(){
// Al revés
for (var i = this.table.rows.length - 1, rs = this.table.rows; i >= 0; i--){
var arCol = [];
// Agregamos las celdas de cada fila para después ordenarlas por su nuevo índice
for (var j=0, cs = rs[i].cells; j < cs.length; j++) arCol.push(cs[j]);
arCol.sort(function(a,b){
var r0 = a.parentNode.parentNode.parentNode.rows[0];
return r0.cells[a.cellIndex].tmColDfn.newIndex - r0.cells[b.cellIndex].tmColDfn.newIndex;
});
// Las volvemos a insertar en la fila con el nuevo orden
$T.makeChildren(arCol,rs[i]);
}
},
// Aplica ordenación de filas
applySort:function(cfgSort){
var aRows = [],bottomRows = [];
cfgSort.sort(function(a,b){return a.tmColDfn.sort.priority - b.tmColDfn.sort.priority});
// Determinamos orden, dejando al final las filas de class sortbottom
for (var i=1, rs = this.table.rows; i < rs.length; i++){
if ((' '+rs[i].className+' ').indexOf(' sortbottom ') != -1) bottomRows.push(rs[i]);
else aRows.push(rs[i]);
}
// En la función de ordenación queda feo pasarle un array siempre con los mimos parámetros. Las variables que necesitamos las creamos a nivel global y luego las eliminamos.
window.__tm_cfgSort = cfgSort;
window.__tm_this = this;
aRows.sort(this.rowSortFunction);
// Eliminamos las variables temporales
delete window.__tm_cfgSort;
delete window.__tm_this;
// Aplicamos orden
$T.makeChildren(aRows, this.table.tBodies[0]);
$T.makeChildren(bottomRows, this.table.tBodies[0]);
// Actualizamos la configuración
for (var i=0, cs = this.table.rows[0].cells; i < cs.length; i++){
var s = cs[i].tmColDfn.sort;
if (s){
s.oldState = s.state;
s.oldPriority = s.priority;
}
}
},
// Función de ordenación intermedia para permitir ordenar por varias columnas
rowSortFunction:function(a,b){
var res = 0, cfgSort = window.__tm_cfgSort, thisArg = window.__tm_this;
for (var i=0; i < cfgSort.length; i++){
var c = cfgSort[i], s = c.tmColDfn.sort, A = thisArg.getTextContent(a.cells[c.cellIndex]), B = thisArg.getTextContent(b.cells[c.cellIndex]);
var es = thisArg.cfg.emptysort, ss = s.state;
// Si uno de los dos está vacío, determinamos si deben ordenarse aparte. Si los dos son vacío, pasamos a la siguiente función pues son técnicamente iguales.
if (es != 0 && (A != '' || B != '')){
if (A == '') return (-1) * es;
if (B == '') return es;
}
if (!s.sortFn) continue;
// Se llama a la función de ordenación. Parámetros: row, texto de celda (trim), índice de row (negativo si orden descendente), celda
res = ss * s.sortFn([ a, A, ss*a.rowIndex, a.cells[c.cellIndex] ], [ b, B, ss*b.rowIndex, b.cells[c.cellIndex] ]);
// Si los dos son iguales, pasamos a la siguiente columna de ordenación
if (res) break;
}
return res;
}
};
/**** END TABLEMANAGER ****/
if (window.postloadFunctionData && postloadFunctionData['tablemanager'] !== null) {
for (var i = 0; i < postloadFunctionData['tablemanager'].length; i++) {
var t = postloadFunctionData['tablemanager'][i];
if ( $UT.hasClass(t, 'movnivel') ) {
new TableManager(t, {emptysort:-1});
} else {
new TableManager(t);
}
}
}
// </pre>