//primero empezemos con algo de browser detection
//asi nuestro codigo funcionara tanto en mozilla como en IE
var standar = null;
if (typeof document.addEventListener != 'undefined') {
//aca preguntamos si el navegador soporta W3C DOM (osea todo navegador competente)
standar = true;
} else if (typeof document.attachEvent != 'undefined') {
//sino bueno es IE DOM
standar = false;
}
//ahora crearemos un objecto que lo unico que hara es tener un par de
//variables y funciones para no tener que estar repitiendo codigo
var GblElems = {
init: function() {
//aca levantamos todos los inputs (submit, text, checkbox, radios, ect)
var allInputs = document.getElementsByTagName("input");
//creamos un array que utilizaremos para guardar unicamente
//los checkboxes de las filas, excluyendo al checkbox global, el que esta
//en el <thead>
var chkBoxes = [];
for (var i=0,len=allInputs.length;i<len;i++) {
if (allInputs[i].type.toLowerCase() == "checkbox" && allInputs[i].id != "mainChkBox") {
//aqui preguntamos si es de tipo checkbox, hacemos lowercase por las dudas
//y preguntamos si NO ES el checkbox que esta en el <thead>
chkBoxes[chkBoxes.length] = allInputs[i];
}
}
//aca guardamos una referencia de todos estos checkbox y los guardamos en el document object
//como propiedad, para acceder en cualquier momneto
document._chkBoxes = chkBoxes;
allTrTags = chkBoxes = null; //como ya cumplieron su cometido, no es necesario que sigan ocupando memoria
//ahora vamos a buscar las tr que esten el tbody
//esto parecera medio estupido ya que tr y thead ya de por si
//no son los misomo, pero IE no piensa lo mismo
//asi que para no agarrar esa tr, que en realidad es una thead
//hay que empezar discriminar los trs...
var allTrTags = document.getElementsByTagName("tr");
var trsClassRows = [];
for (var i=0,len=allTrTags.length;i<len;i++) {
//... si se fijan en nuestro markup html todas las trs que estan
//tbody tiene la clase rows, asi que solo tenemos que preguntar
//si tal tr tiene tal clase, y ya sabemos que es una tr que esta en tbody
if (allTrTags[i].className.toLowerCase() == "rows") {
trsClassRows[trsClassRows.length] = allTrTags[i];
}
}
//como hicimos anteriormente guardamos una referencia en el document,
//para haceder a estas trs en cualquer momento
document._trsClassRows = trsClassRows;
allTrTags = trsClassRows = null;
},
//ahora es tiempo de funciones
//esta funcion lo que hace es buscar si un elemento
//tiene como padre a un tr en este caso, porque es esto?
//bien debido a que IE maneja de diferente manera
//los eventos (que explico un toque mas abajo),
//tenemos que hacer esto, ya entederan porque
searchParent: function(node, parent) {
//osea para que entiendan a aca va a entrar
//un <td> y un parent a buscar que va a se "tr"
//una vez que lo que encontramos ya sabemos que <tr>
//tenemos que highlightear, selectear y/o viceversa
var test = node.parentNode;
while (test) {
if (test.nodeName.toLowerCase() == parent) {
return test;
}
test = test.parentNode;
}
return null;
},
searchSelectiveParent: function(node, parent) {
//entra node = input (string),
//entra parent = tr (node)
//y devuelvo ese input vinculado a ese tr
var allNodes = document.getElementsByTagName(node);
for (var i=0,len=allNodes.length;i<len;i++) {
var test = allNodes[i].parentNode;
while (test) {
if (test == parent) {
return allNodes[i];
}
test = test.parentNode;
}
}
return null;
},
//esta funcion se fija si estan todos los checkboxes
//tildados si es asi entonces nuestro checkbox principal
//deberia tildarse, o viceversa
checkChecked: function() {
//aca levantamos todos los checkboxes
//para saber cuantos hay tildados
var checkChkBoxes = document._chkBoxes;
var countChecked = 0;
for (var i=0,len=checkChkBoxes.length;i<len;i++) {
if (checkChkBoxes[i].checked == true) {
countChecked++; //vamos contando la cantidad de tildados
if (len === countChecked) {
SelAllChkBoxes.mainChkBox.checked = true;
}
} else {
SelAllChkBoxes.mainChkBox.checked = false;
break;
}
}
//aca esta el truco si la cantidad de tildados es igual
//a la cantidad de checkbox entonces nuestros checkbox global
//deberia estar tildado, sino entonces tiene que estar destildado
checkChkBoxes = null; //limpiamos memoria
}
};
//ahora empezemos con el objecto que se encargara de tildar/detildar todos los input
//de una saque, osea me refiero al checkbox que esta en <thead>
var SelAllChkBoxes = {
init: function() {
//aca creo una propiedad publica que utlizare luego para saber
//si esta checked o no lo esta
SelAllChkBoxes.mainChkBox = document.getElementById("mainChkBox");
//se acuerdan de mi variable standar, la que reconocia si era mozilla o ie,
//pues como ambos manejan de diferente manera los eventos, pregunto que onda,
//y asi se como debo declarar mi eventListener en mi elemento
if (standar) { SelAllChkBoxes.mainChkBox.addEventListener("click", SelAllChkBoxes.clickListener, false); } //W3C DOM
else { SelAllChkBoxes.mainChkBox.attachEvent("onclick", SelAllChkBoxes.clickListener); } //IE DOM
},
clickListener: function(evt) {
//aqui mediante la propiedad publica que declare arriba, que no es mas que una variable
//veo si el checkbox que esta en <thead> esta selecionado o no lo esta
var checked = (SelAllChkBoxes.mainChkBox.checked == true) ? true : false;
//se acuerdan de mi referencia a los checkbox que cree mas arriba en GblElems ??,
//bueno aqui me la guardo en la variable chkBoxInputs y empiezo...
var chkBoxInputs = document._chkBoxes;
for (var i=0,len=chkBoxInputs.length;i<len;i++) {
if (checked) {
//... si el checkbox global esta tildado lo que debo hacer
//es tildartodos los checkbox que estan en <tbody> y poner
//todos los <tr> con la .class "clickedRow"
//primero uso la funcion que cree en GblElems
//que buscamos si tal elemento tiene tal parent
var trTag = GblElems.searchParent(chkBoxInputs[i], "tr");
//una vez encontrado ese <tr> guardo por las dudas cualquier
//classname que tenga o sino no,
var curClass = trTag.className || "";
//ahora aplico la clase "clickedRow" + si es que habia otra case anteriormente
//es importante que haya un espacio " clickedRow" sino romperemos todo =P
trTag.className = curClass + " clickedRow";
//ahora el input de este <tr> lo ponemos en true y listo
chkBoxInputs[i].checked = true;
} else {
//aca hacemos el proceso inverso,
//buscamos el <tr> de este <input>, obviamente
var trTag = GblElems.searchParent(chkBoxInputs[i], "tr");
//ya encontrado borramos la clase "clickedRow" mediante el uso de
//una expresion regular, con el metodo replace
trTag.className = trTag.className.replace(/ clickedRow/g, "");
//destildamos el checkbox
chkBoxInputs[i].checked = false;
}
}
//esto es para que el evento click no suba (bubble up), sino sabes a lo que me refiero
//estas en serios en problemas =P, pero basicamente es que no quero
//que este click relacionado con este elemento no se me confunda
//con otro elemento que tambien tenga un evento click relacionado, obivamente en IE es diferente,
//pero para hicimos nuestro browser detection
if (standar) { evt.stopPropagation(); } else { evt.cancelBubble = true; }
}
};
//este objecto sera el encargado de que cuando hagamos mouseover y mouseout,
//las filas cambien su backgroundColor, ademas click para cuando clickeemos
//sobre una determinada fila y quede seleccionada o viceversa
var HighLightRows = {
init: function() {
//aqui traemos la referencia de los trs que estan en <tbody>
var trsClassRows = document._trsClassRows;
for (var i=0,len=trsClassRows.length;i<len;i++) { //empezaremos agregando los respectivos eventos
if (standar) {
//esto es para W3C DOM
trsClassRows[i].addEventListener("mouseover", HighLightRows.mouseoverListener, false);
trsClassRows[i].addEventListener("mouseout", HighLightRows.mouseoutListener, false);
trsClassRows[i].addEventListener("click", HighLightRows.clickListener, false);
} else {
//esto es para IE DOM
trsClassRows[i].attachEvent("onmouseover", HighLightRows.mouseoverListener);
trsClassRows[i].attachEvent("onmouseout", HighLightRows.mouseoutListener);
trsClassRows[i].attachEvent("onclick", HighLightRows.clickListener);
}
}
//ahora traigo la referencia a los checkbox que esta en <tbody>
//para agregarles los respectivos eventos
var chkBoxes = document._chkBoxes;
for (var i=0,len=chkBoxes.length;i<len;i++) {
if (standar) { chkBoxes[i].addEventListener("click", HighLightRows.clickListener2, false); } //W3C
else { chkBoxes[i].attachEvent("onclick", HighLightRows.clickListener2); } //IE
}
trsClassRows = chkBoxes = null;
},
//algo simple cuando el mouse este sobre un fila, esta highlightee
mouseoverListener: function(evt) {
var target = evt.target || evt.srcElement;
//hacer esto es super estupido, basicamente porque tranquilamente
//podira usar this, pero this en IE apunta pa cualquier lado,
//y tendria que agregar un par de lineas de codigo para que this
//apunte al <tr> pero pa que te voy a complicar la vida...
//basicamente lo que hago es guardar en target, el elemento
//donde estoy haciendo mouseover, que puede ser un <td>
//o el <input>, srcElement es para IE, el otro es para Mozilla
//pero como nostros debemos highlightear el tr y no el td o el input
//tenemos que encontrarlo, para eso uso mi funcion buscar parent
var target = GblElems.searchParent(target, "tr");
//este if lo unico que hace es ver si ya previamente la fila
//a la que le hacemos mouseover esta clickeada (.clickedRow)
//si es asi, no es necesaria highlightearla y salimos, y al hacer este
//procesdimiento nuestro codigo sera mas eficiente.
if (/ clickedRow/.test(target.className)) {
return;
}
var curClass = target.className || ''; //guardo si es que tiene una clase previa por la dudas
target.className = curClass + " hightLightRow"; //aplico mi .class "hightLightRow",
//importante dejar un espacio
},
//este hace el inverso que el anterior, al hacer mouseout
//debemos quitar nuestra clase hightLightRow, nada
//de otro mundo el codigo
mouseoutListener: function(evt) {
var target = evt.target || evt.srcElement;
var target = GblElems.searchParent(target, "tr");
target.className = target.className.replace(" hightLightRow", "");
},
//esta funcion sera la que usaremos para aplicar
//nuestra clase .clickedRow cuando hacemos
//click en una fila, y ademas tildar su respectivo input,
//o hacer el proceso inverso
clickListener: function(evt) {
var target = evt.target || evt.srcElement;
//aca buscamos al <tr> de esta fila donde se esta haciendo click
var target = GblElems.searchParent(target, "tr");
var chkBox = GblElems.searchSelectiveParent("input", target);
chkBox.checked = (chkBox.checked == false) ? true : false;
//una vez encontrado hay que ver si esta seleccionado o no lo esta
//para saber si hay que destilar o tildar tal input
if (/ clickedRow/.test(target.className)) {
//aca preguntamos si tiene la clase clickedRow mediante una
//expresion regular con el metodo test, porque de este modo sabremos que tenemos
//que desclickear dicho tr, y simplemente lo hacemos,
//borrando tal clase
target.className = target.className.replace(/ clickedRow/g, "");
} else {
//sino tiene dicha clase, entonces hay que agregarsela,
//nada de otro mundo.
var curClass = target.className || "";
target.className = curClass + " clickedRow";
}
//se acuerdan de esta funcion, lo que hace
//es contar si la cantidad de tildados es igual
//a la cantidad de checkbox, si es asi,
//nuestro checkbox global deberia tildarse
GblElems.checkChecked();
//para evitar el bubbling-up
if (standar) { evt.stopPropagation(); } else { evt.cancelBubble = true; }
},
//esta funcion es la que se encargar de hacer lo mismo que la funcion
//de arriba, pero aqui es cuando hacemos click en el input y no en una celda
clickListener2: function(evt) {
SelAllChkBoxes.mainChkBox.checked = false;
//fijense que no pregunto si tiene de child un input
//porque ya estoy en ese input, solo tengo que encontrar
//el parent <tr> para aplicarle la clase o viceversa
var target = evt.target || evt.srcElement;
var target = GblElems.searchParent(target, "tr");
if (/ clickedRow/.test(target.className)) {
target.className = target.className.replace(/ clickedRow/g, "");
} else {
var curClass = target.className || "";
target.className = curClass + " clickedRow";
}
GblElems.checkChecked();
if (standar) { evt.stopPropagation(); } else { evt.cancelBubble = true; }
}
};
//ya estamos eh....
//este objecto e encarga de unicmanete desseleccionar
//filas con la clase "clickedRow", para eso usamos el boton con id "clrSelRows" con accionario
var ClrSelRows = {
init: function() {
var btnClr = document.getElementById("clrSelRows"); //lo encontramos
//y le agregamos el evento click segun W3C o IE
if (standar) { btnClr.addEventListener("click", ClrSelRows.clickListener, false); }
else { btnClr.attachEvent("onclick", ClrSelRows.clickListener); }
},
//ahora simplemente la funcion que se encargara de quitar
//la clase clickeRow
clickListener: function(evt) {
//primero llamamos a nuestra referencia en document
//de nuestros checkboxes que estan en el <tbody>
var chkBoxes = document._chkBoxes;
for (var i=0,len=chkBoxes.length;i<len;i++) {
//solo debemos descelecionar los checkboxes que esten
//true, obvious capitan obvious
if (chkBoxes[i].checked == true) {
chkBoxes[i].checked = false; //destildamos
var trParent = GblElems.searchParent(chkBoxes[i], "tr"); //buscamo <tr> de dicho <input>
trParent.className = trParent.className.replace(/ clickedRow/g, ""); //quitamos la clase
}
}
//obivamente al estar desleccionando checkboxes,
//nuestro checkbox global, no deberia estar tildado
SelAllChkBoxes.mainChkBox.checked = false;
//esto es para evitar el bubbling, y tambien
//para que el navegador NO siga el boton, a que me refiero?
//pues como nuestro boton esta dentro de un form
//al hacerle click recargaria la pagina,
//con js podemos hacer que estas accionaes predeterminadas
//no sucedan. Obviamente hay una para W3C browses based y otra para IE.
if (standar) {
evt.stopPropagation();
evt.preventDefault();
} else {
evt.cancelBubble = true;
evt.returnValue = false;
}
}
};
//listooooooo, siemplemtente tenemos que llamar
//a todos nuestros objectos cuando el documento se termine de bajar
//ya que manipulan el DOM, ya que por el contrario tendriamos errores
//de todos los colores
window.onload = function() {
GblElems.init();
SelAllChkBoxes.init();
HighLightRows.init();
ClrSelRows.init();
};