1/*====================================================
2	- HTML Table Filter Generator v1.6
3	- By Max Guglielmi
4	- mguglielmi.free.fr/scripts/TableFilter/?l=en
5	- please do not change this comment
6	- don't forget to give some credit... it's always
7	good for the author
8	- Special credit to Cedric Wartel and
9	cnx.claude@free.fr for contribution and
10	inspiration
11=====================================================*/
12
13// global vars
14var TblId, SearchFlt, SlcArgs;
15TblId = new Array(), SlcArgs = new Array();
16
17
18function setFilterGrid(id)
19/*====================================================
20	- Checks if id exists and is a table
21	- Then looks for additional params
22	- Calls fn that generates the grid
23=====================================================*/
24{
25	var tbl = grabEBI(id);
26	var ref_row, fObj;
27	if(tbl != null && tbl.nodeName.toLowerCase() == "table")
28	{
29		if(arguments.length>1)
30		{
31			for(var i=0; i<arguments.length; i++)
32			{
33				var argtype = typeof arguments[i];
34
35				switch(argtype.toLowerCase())
36				{
37					case "number":
38						ref_row = arguments[i];
39					break;
40					case "object":
41						fObj = arguments[i];
42					break;
43				}//switch
44
45			}//for
46		}//if
47
48		ref_row == undefined ? ref_row=2 : ref_row=(ref_row+2);
49		var ncells = getCellsNb(id,ref_row);
50		tbl.tf_ncells = ncells;
51		if(tbl.tf_ref_row==undefined) tbl.tf_ref_row = ref_row;
52		tbl.tf_Obj = fObj;
53		if( !hasGrid(id) ) AddGrid(id);
54	}//if tbl!=null
55}
56
57function AddGrid(id)
58/*====================================================
59	- adds a row containing the filtering grid
60=====================================================*/
61{
62	TblId.push(id);
63	var t = grabEBI(id);
64	var f = t.tf_Obj, n = t.tf_ncells;
65	var inpclass, fltgrid, displayBtn, btntext, enterkey;
66	var modfilter_fn, display_allText, on_slcChange;
67	var displaynrows, totrows_text, btnreset, btnreset_text;
68	var sort_slc, displayPaging, pagingLength, displayLoader;
69	var load_text, exactMatch, alternateBgs, colOperation;
70	var rowVisibility, colWidth, bindScript;
71
72	f!=undefined && f["grid"]==false ? fltgrid=false : fltgrid=true;//enables/disables filter grid
73	f!=undefined && f["btn"]==true ? displayBtn=true : displayBtn=false;//show/hides filter's validation button
74	f!=undefined && f["btn_text"]!=undefined ? btntext=f["btn_text"] : btntext="go";//defines button text
75	f!=undefined && f["enter_key"]==false ? enterkey=false : enterkey=true;//enables/disables enter key
76	f!=undefined && f["mod_filter_fn"] ? modfilter_fn=true : modfilter_fn=false;//defines alternative fn
77	f!=undefined && f["display_all_text"]!=undefined ? display_allText=f["display_all_text"] : display_allText="";//defines 1st option text
78	f!=undefined && f["on_change"]==false ? on_slcChange=false : on_slcChange=true;//enables/disables onChange event on combo-box
79	f!=undefined && f["rows_counter"]==true ? displaynrows=true : displaynrows=false;//show/hides rows counter
80	f!=undefined && f["rows_counter_text"]!=undefined ? totrows_text=f["rows_counter_text"] : totrows_text="Displayed rows: ";//defines rows counter text
81	f!=undefined && f["btn_reset"]==true ? btnreset=true : btnreset=false;//show/hides reset link
82	f!=undefined && f["btn_reset_text"]!=undefined ? btnreset_text=f["btn_reset_text"] : btnreset_text="Reset";//defines reset text
83	f!=undefined && f["sort_select"]==true ? sort_slc=true : sort_slc=false;//enables/disables select options sorting
84	f!=undefined && f["paging"]==true ? displayPaging=true : displayPaging=false;//enables/disables table paging
85	f!=undefined && f["paging_length"]!=undefined ? pagingLength=f["paging_length"] : pagingLength=10;//defines table paging length
86	f!=undefined && f["loader"]==true ? displayLoader=true : displayLoader=false;//enables/disables loader
87	f!=undefined && f["loader_text"]!=undefined ? load_text=f["loader_text"] : load_text="Loading...";//defines loader text
88	f!=undefined && f["exact_match"]==true ? exactMatch=true : exactMatch=false;//enables/disbles exact match for search
89	f!=undefined && f["alternate_rows"]==true ? alternateBgs=true : alternateBgs=false;//enables/disbles rows alternating bg colors
90	f!=undefined && f["col_operation"] ? colOperation=true : colOperation=false;//enables/disbles column operation(sum,mean)
91	f!=undefined && f["rows_always_visible"] ? rowVisibility=true : rowVisibility=false;//makes a row always visible
92	f!=undefined && f["col_width"] ? colWidth=true : colWidth=false;//defines widths of columns
93	f!=undefined && f["bind_script"] ? bindScript=true : bindScript=false;
94
95	// props are added to table in order to be easily accessible from other fns
96	t.tf_fltGrid			=	fltgrid;
97	t.tf_displayBtn			= 	displayBtn;
98	t.tf_btnText			=	btntext;
99	t.tf_enterKey			= 	enterkey;
100	t.tf_isModfilter_fn		= 	modfilter_fn;
101	t.tf_display_allText 	= 	display_allText;
102	t.tf_on_slcChange 		= 	on_slcChange;
103	t.tf_rowsCounter 		= 	displaynrows;
104	t.tf_rowsCounter_text	= 	totrows_text;
105	t.tf_btnReset 			= 	btnreset;
106	t.tf_btnReset_text 		= 	btnreset_text;
107	t.tf_sortSlc 			=	sort_slc;
108	t.tf_displayPaging 		= 	displayPaging;
109	t.tf_pagingLength 		= 	pagingLength;
110	t.tf_displayLoader		= 	displayLoader;
111	t.tf_loadText			= 	load_text;
112	t.tf_exactMatch 		= 	exactMatch;
113	t.tf_alternateBgs		=	alternateBgs;
114	t.tf_startPagingRow		= 	0;
115
116	if(modfilter_fn) t.tf_modfilter_fn = f["mod_filter_fn"];// used by DetectKey fn
117
118	if(fltgrid)
119	{
120		var fltrow = t.insertRow(0); //adds filter row
121		fltrow.className = "fltrow";
122		for(var i=0; i<n; i++)// this loop adds filters
123		{
124			var fltcell = fltrow.insertCell(i);
125			//fltcell.noWrap = true;
126			i==n-1 && displayBtn==true ? inpclass = "flt_s" : inpclass = "flt";
127
128			if(f==undefined || f["col_"+i]==undefined || f["col_"+i]=="none")
129			{
130				var inptype;
131				(f==undefined || f["col_"+i]==undefined) ? inptype="text" : inptype="hidden";//show/hide input
132				var inp = createElm( "input",["id","flt"+i+"_"+id],["type",inptype],["class",inpclass] );
133				inp.className = inpclass;// for ie<=6
134				fltcell.appendChild(inp);
135				if(enterkey) inp.onkeypress = DetectKey;
136			}
137			else if(f["col_"+i]=="select")
138			{
139				var slc = createElm( "select",["id","flt"+i+"_"+id],["class",inpclass] );
140				slc.className = inpclass;// for ie<=6
141				fltcell.appendChild(slc);
142				PopulateOptions(id,i);
143				if(displayPaging)//stores arguments for GroupByPage() fn
144				{
145					var args = new Array();
146					args.push(id); args.push(i); args.push(n);
147					args.push(display_allText); args.push(sort_slc); args.push(displayPaging);
148					SlcArgs.push(args);
149				}
150				if(enterkey) slc.onkeypress = DetectKey;
151				if(on_slcChange)
152				{
153					(!modfilter_fn) ? slc.onchange = function(){ Filter(id); } : slc.onchange = f["mod_filter_fn"];
154				}
155			}
156
157			if(i==n-1 && displayBtn==true)// this adds button
158			{
159				var btn = createElm(
160										"input",
161										["id","btn"+i+"_"+id],["type","button"],
162										["value",btntext],["class","btnflt"]
163									);
164				btn.className = "btnflt";
165
166				fltcell.appendChild(btn);
167				(!modfilter_fn) ? btn.onclick = function(){ Filter(id); } : btn.onclick = f["mod_filter_fn"];
168			}//if
169
170		}// for i
171	}//if fltgrid
172
173	if(displaynrows || btnreset || displayPaging || displayLoader)
174	{
175
176		/*** div containing rows # displayer + reset btn ***/
177		var infdiv = createElm( "div",["id","inf_"+id],["class","inf"] );
178		infdiv.className = "inf";// setAttribute method for class attribute doesn't seem to work on ie<=6
179		t.parentNode.insertBefore(infdiv, t);
180
181		if(displaynrows)
182		{
183			/*** left div containing rows # displayer ***/
184			var totrows;
185			var ldiv = createElm( "div",["id","ldiv_"+id] );
186			displaynrows ? ldiv.className = "ldiv" : ldiv.style.display = "none";
187			displayPaging ? totrows = pagingLength : totrows = getRowsNb(id);
188
189			var totrows_span = createElm( "span",["id","totrows_span_"+id],["class","tot"] ); // tot # of rows displayer
190			totrows_span.className = "tot";//for ie<=6
191			totrows_span.appendChild( createText(totrows) );
192
193			var totrows_txt = createText(totrows_text);
194			ldiv.appendChild(totrows_txt);
195			ldiv.appendChild(totrows_span);
196			infdiv.appendChild(ldiv);
197		}
198
199		if(displayLoader)
200		{
201			/*** div containing loader  ***/
202			var loaddiv = createElm( "div",["id","load_"+id],["class","loader"] );
203			loaddiv.className = "loader";// for ie<=6
204			loaddiv.style.display = "none";
205			loaddiv.appendChild( createText(load_text) );
206			infdiv.appendChild(loaddiv);
207		}
208
209		if(displayPaging)
210		{
211			/*** mid div containing paging displayer ***/
212			var mdiv = createElm( "div",["id","mdiv_"+id] );
213			displayPaging ? mdiv.className = "mdiv" : mdiv.style.display = "none";
214			infdiv.appendChild(mdiv);
215
216			var start_row = t.tf_ref_row;
217			var row = grabTag(t,"tr");
218			var nrows = row.length;
219			var npages = Math.ceil( (nrows - start_row)/pagingLength );//calculates page nb
220
221			var slcPages = createElm( "select",["id","slcPages_"+id] );
222			slcPages.onchange = function(){
223				if(displayLoader) showLoader(id,"");
224				t.tf_startPagingRow = this.value;
225				GroupByPage(id);
226				if(displayLoader) showLoader(id,"none");
227			}
228
229			var pgspan = createElm( "span",["id","pgspan_"+id] );
230			grabEBI("mdiv_"+id).appendChild( createText(" Page ") );
231			grabEBI("mdiv_"+id).appendChild(slcPages);
232			grabEBI("mdiv_"+id).appendChild( createText(" of ") );
233			pgspan.appendChild( createText(npages+" ") );
234			grabEBI("mdiv_"+id).appendChild(pgspan);
235
236			for(var j=start_row; j<nrows; j++)//this sets rows to validRow=true
237			{
238				row[j].setAttribute("validRow","true");
239			}//for j
240
241			setPagingInfo(id);
242			if(displayLoader) showLoader(id,"none");
243		}
244
245		if(btnreset && fltgrid)
246		{
247			/*** right div containing reset button **/
248			var rdiv = createElm( "div",["id","reset_"+id] );
249			btnreset ? rdiv.className = "rdiv" : rdiv.style.display = "none";
250
251			var fltreset = createElm( 	"a",
252										["href","javascript:clearFilters('"+id+"');Filter('"+id+"');"] );
253			fltreset.appendChild(createText(btnreset_text));
254			rdiv.appendChild(fltreset);
255			infdiv.appendChild(rdiv);
256		}
257
258	}//if displaynrows etc.
259
260	if(colWidth)
261	{
262		t.tf_colWidth = f["col_width"];
263		setColWidths(id);
264	}
265
266	if(alternateBgs && !displayPaging)
267		setAlternateRows(id);
268
269	if(colOperation)
270	{
271		t.tf_colOperation = f["col_operation"];
272		setColOperation(id);
273	}
274
275	if(rowVisibility)
276	{
277		t.tf_rowVisibility = f["rows_always_visible"];
278		if(displayPaging) setVisibleRows(id);
279	}
280
281	if(bindScript)
282	{
283		t.tf_bindScript = f["bind_script"];
284		if(	t.tf_bindScript!=undefined &&
285			t.tf_bindScript["target_fn"]!=undefined )
286		{//calls a fn if defined
287			t.tf_bindScript["target_fn"].call(null,id);
288		}
289	}//if bindScript
290}
291
292function PopulateOptions(id,cellIndex)
293/*====================================================
294	- populates select
295	- adds only 1 occurence of a value
296=====================================================*/
297{
298	var t = grabEBI(id);
299	var ncells = t.tf_ncells, opt0txt = t.tf_display_allText;
300	var sort_opts = t.tf_sortSlc, paging = t.tf_displayPaging;
301	var start_row = t.tf_ref_row;
302	var row = grabTag(t,"tr");
303	var OptArray = new Array();
304	var optIndex = 0; // option index
305	var currOpt = new Option(opt0txt,"",false,false); //1st option
306	grabEBI("flt"+cellIndex+"_"+id).options[optIndex] = currOpt;
307
308	for(var k=start_row; k<row.length; k++)
309	{
310		var cell = getChildElms(row[k]).childNodes;
311		var nchilds = cell.length;
312		var isPaged = row[k].getAttribute("paging");
313
314		if(nchilds == ncells){// checks if row has exact cell #
315
316			for(var j=0; j<nchilds; j++)// this loop retrieves cell data
317			{
318				if(cellIndex==j)
319				{
320					var cell_data = getCellText(cell[j]);
321					// checks if celldata is already in array
322					var isMatched = false;
323					for(w in OptArray)
324					{
325						if( cell_data == OptArray[w] ) isMatched = true;
326					}
327					if(!isMatched) OptArray.push(cell_data);
328				}//if cellIndex==j
329			}//for j
330		}//if
331	}//for k
332
333	if(sort_opts) OptArray.sort();
334	for(y in OptArray)
335	{
336		optIndex++;
337		var currOpt = new Option(OptArray[y],OptArray[y],false,false);
338		grabEBI("flt"+cellIndex+"_"+id).options[optIndex] = currOpt;
339	}
340
341}
342
343function Filter(id)
344/*====================================================
345	- Filtering fn
346	- gets search strings from SearchFlt array
347	- retrieves data from each td in every single tr
348	and compares to search string for current
349	column
350	- tr is hidden if all search strings are not
351	found
352=====================================================*/
353{
354	showLoader(id,"");
355	SearchFlt = getFilters(id);
356	var t = grabEBI(id);
357	t.tf_Obj!=undefined ? fprops = t.tf_Obj : fprops = new Array();
358	var SearchArgs = new Array();
359	var ncells = getCellsNb(id);
360	var totrows = getRowsNb(id), hiddenrows = 0;
361	var ematch = t.tf_exactMatch;
362	var showPaging = t.tf_displayPaging;
363
364	for(var i=0; i<SearchFlt.length; i++)
365		SearchArgs.push( (grabEBI(SearchFlt[i]).value).toLowerCase() );
366
367	var start_row = t.tf_ref_row;
368	var row = grabTag(t,"tr");
369
370	for(var k=start_row; k<row.length; k++)
371	{
372		/*** if table already filtered some rows are not visible ***/
373		if(row[k].style.display == "none") row[k].style.display = "";
374
375		var cell = getChildElms(row[k]).childNodes;
376		var nchilds = cell.length;
377
378		if(nchilds == ncells)// checks if row has exact cell #
379		{
380			var cell_value = new Array();
381			var occurence = new Array();
382			var isRowValid = true;
383
384			for(var j=0; j<nchilds; j++)// this loop retrieves cell data
385			{
386				var cell_data = getCellText(cell[j]).toLowerCase();
387				cell_value.push(cell_data);
388
389				if(SearchArgs[j]!="")
390				{
391					var num_cell_data = parseFloat(cell_data);
392
393					if(/<=/.test(SearchArgs[j]) && !isNaN(num_cell_data)) // first checks if there is an operator (<,>,<=,>=)
394					{
395						num_cell_data <= parseFloat(SearchArgs[j].replace(/<=/,"")) ? occurence[j] = true : occurence[j] = false;
396					}
397
398					else if(/>=/.test(SearchArgs[j]) && !isNaN(num_cell_data))
399					{
400						num_cell_data >= parseFloat(SearchArgs[j].replace(/>=/,"")) ? occurence[j] = true : occurence[j] = false;
401					}
402
403					else if(/</.test(SearchArgs[j]) && !isNaN(num_cell_data))
404					{
405						num_cell_data < parseFloat(SearchArgs[j].replace(/</,"")) ? occurence[j] = true : occurence[j] = false;
406					}
407
408					else if(/>/.test(SearchArgs[j]) && !isNaN(num_cell_data))
409					{
410						num_cell_data > parseFloat(SearchArgs[j].replace(/>/,"")) ? occurence[j] = true : occurence[j] = false;
411					}
412
413					else
414					{
415						// Improved by Cedric Wartel (cwl)
416						// automatic exact match for selects and special characters are now filtered
417						// modif cwl : exact match automatique sur les select
418						var regexp;
419						if(ematch || fprops["col_"+j]=="select") regexp = new RegExp('(^)'+regexpEscape(SearchArgs[j])+'($)',"gi");
420						else regexp = new RegExp(regexpEscape(SearchArgs[j]),"gi");
421						occurence[j] = regexp.test(cell_data);
422					}
423				}//if SearchArgs
424			}//for j
425
426			for(var z=0; z<ncells; z++)
427			{
428				if(SearchArgs[z]!="" && !occurence[z]) isRowValid = false;
429			}//for t
430
431		}//if
432
433		if(!isRowValid)
434		{
435			row[k].style.display = "none"; hiddenrows++;
436			if( showPaging ) row[k].setAttribute("validRow","false");
437		} else {
438			row[k].style.display = "";
439			if( showPaging ) row[k].setAttribute("validRow","true");
440		}
441
442	}// for k
443
444	t.tf_nRows = parseInt( getRowsNb(id) )-hiddenrows;
445	if( !showPaging ) applyFilterProps(id);//applies filter props after filtering process
446	if( showPaging ){ t.tf_startPagingRow=0; setPagingInfo(id); }//starts paging process
447}
448
449function setPagingInfo(id)
450/*====================================================
451	- Paging fn
452	- calculates page # according to valid rows
453	- refreshes paging select according to page #
454	- Calls GroupByPage fn
455=====================================================*/
456{
457	var t = grabEBI(id);
458	var start_row = parseInt( t.tf_ref_row );//filter start row
459	var pagelength = t.tf_pagingLength;
460	var row = grabTag(t,"tr");
461	var mdiv = grabEBI("mdiv_"+id);
462	var slcPages = grabEBI("slcPages_"+id);
463	var pgspan = grabEBI("pgspan_"+id);
464	var nrows = 0;
465
466	for(var j=start_row; j<row.length; j++)//counts rows to be grouped
467	{
468		if(row[j].getAttribute("validRow") == "true") nrows++;
469	}//for j
470
471	var npg = Math.ceil( nrows/pagelength );//calculates page nb
472	pgspan.innerHTML = npg; //refresh page nb span
473	slcPages.innerHTML = "";//select clearing shortcut
474
475	if( npg>0 )
476	{
477		mdiv.style.visibility = "visible";
478		for(var z=0; z<npg; z++)
479		{
480			var currOpt = new Option((z+1),z*pagelength,false,false);
481			slcPages.options[z] = currOpt;
482		}
483	} else {/*** if no results paging select is hidden ***/
484		mdiv.style.visibility = "hidden";
485	}
486
487	GroupByPage(id);
488}
489
490function GroupByPage(id)
491/*====================================================
492	- Paging fn
493	- Displays current page rows
494=====================================================*/
495{
496	showLoader(id,"");
497	var t = grabEBI(id);
498	var start_row = parseInt( t.tf_ref_row );//filter start row
499	var pagelength = parseInt( t.tf_pagingLength );
500	var paging_start_row = parseInt( t.tf_startPagingRow );//paging start row
501	var paging_end_row = paging_start_row + pagelength;
502	var row = grabTag(t,"tr");
503	var nrows = 0;
504	var validRows = new Array();//stores valid rows index
505
506	for(var j=start_row; j<row.length; j++)
507	//this loop stores valid rows index in validRows Array
508	{
509		var isRowValid = row[j].getAttribute("validRow");
510		if(isRowValid=="true") validRows.push(j);
511	}//for j
512
513	for(h=0; h<validRows.length; h++)
514	//this loop shows valid rows of current page
515	{
516		if( h>=paging_start_row && h<paging_end_row )
517		{
518			nrows++;
519			row[ validRows[h] ].style.display = "";
520		} else row[ validRows[h] ].style.display = "none";
521	}//for h
522
523	t.tf_nRows = parseInt(nrows);
524	applyFilterProps(id);//applies filter props after filtering process
525}
526
527function applyFilterProps(id)
528/*====================================================
529	- checks fns that should be called
530	after filtering and/or paging process
531=====================================================*/
532{
533	t = grabEBI(id);
534	var rowsCounter = t.tf_rowsCounter;
535	var nRows = t.tf_nRows;
536	var rowVisibility = t.tf_rowVisibility;
537	var alternateRows = t.tf_alternateBgs;
538	var colOperation = t.tf_colOperation;
539
540	if( rowsCounter ) showRowsCounter( id,parseInt(nRows) );//refreshes rows counter
541	if( rowVisibility ) setVisibleRows(id);//shows rows always visible
542	if( alternateRows ) setAlternateRows(id);//alterning row colors
543	if( colOperation  ) setColOperation(id);//makes operation on a col
544	showLoader(id,"none");
545}
546
547function hasGrid(id)
548/*====================================================
549	- checks if table has a filter grid
550	- returns a boolean
551=====================================================*/
552{
553	var r = false, t = grabEBI(id);
554	if(t != null && t.nodeName.toLowerCase() == "table")
555	{
556		for(i in TblId)
557		{
558			if(id == TblId[i]) r = true;
559		}// for i
560	}//if
561	return r;
562}
563
564function getCellsNb(id,nrow)
565/*====================================================
566	- returns number of cells in a row
567	- if nrow param is passed returns number of cells
568	of that specific row
569=====================================================*/
570{
571  	var t = grabEBI(id);
572	var tr;
573	if(nrow == undefined) tr = grabTag(t,"tr")[0];
574	else  tr = grabTag(t,"tr")[nrow];
575	var n = getChildElms(tr);
576	return n.childNodes.length;
577}
578
579function getRowsNb(id)
580/*====================================================
581	- returns total nb of filterable rows starting
582	from reference row if defined
583=====================================================*/
584{
585	var t = grabEBI(id);
586	var s = t.tf_ref_row;
587	var ntrs = grabTag(t,"tr").length;
588	return parseInt(ntrs-s);
589}
590
591function getFilters(id)
592/*====================================================
593	- returns an array containing filters ids
594	- Note that hidden filters are also returned
595=====================================================*/
596{
597	var SearchFltId = new Array();
598	var t = grabEBI(id);
599	var tr = grabTag(t,"tr")[0];
600	var enfants = tr.childNodes;
601	if(t.tf_fltGrid)
602	{
603		for(var i=0; i<enfants.length; i++)
604			SearchFltId.push(enfants[i].firstChild.getAttribute("id"));
605	}
606	return SearchFltId;
607}
608
609function clearFilters(id)
610/*====================================================
611	- clears grid filters
612=====================================================*/
613{
614	SearchFlt = getFilters(id);
615	for(i in SearchFlt) grabEBI(SearchFlt[i]).value = "";
616}
617
618function showLoader(id,p)
619/*====================================================
620	- displays/hides loader div
621=====================================================*/
622{
623	var loader = grabEBI("load_"+id);
624	if(loader != null && p=="none")
625		setTimeout("grabEBI('load_"+id+"').style.display = '"+p+"'",150);
626	else if(loader != null && p!="none") loader.style.display = p;
627}
628
629function showRowsCounter(id,p)
630/*====================================================
631	- Shows total number of filtered rows
632=====================================================*/
633{
634	var totrows = grabEBI("totrows_span_"+id);
635	if(totrows != null && totrows.nodeName.toLowerCase() == "span" )
636		totrows.innerHTML = p;
637}
638
639function getChildElms(n)
640/*====================================================
641	- checks passed node is a ELEMENT_NODE nodeType=1
642	- removes TEXT_NODE nodeType=3
643=====================================================*/
644{
645	if(n.nodeType == 1)
646	{
647		var enfants = n.childNodes;
648		for(var i=0; i<enfants.length; i++)
649		{
650			var child = enfants[i];
651			if(child.nodeType == 3) n.removeChild(child);
652		}
653		return n;
654	}
655}
656
657function getCellText(n)
658/*====================================================
659	- returns text + text of child nodes of a cell
660=====================================================*/
661{
662	var s = "";
663	var enfants = n.childNodes;
664	for(var i=0; i<enfants.length; i++)
665	{
666		var child = enfants[i];
667		if(child.nodeType == 3) s+= child.data;
668		else s+= getCellText(child);
669	}
670	return s;
671}
672
673function getColValues(id,colindex,num)
674/*====================================================
675	- returns an array containing cell values of
676	a column
677	- needs following args:
678		- filter id (string)
679		- column index (number)
680		- a boolean set to true if we want only
681		numbers to be returned
682=====================================================*/
683{
684	var t = grabEBI(id);
685	var row = grabTag(t,"tr");
686	var nrows = row.length;
687	var start_row = parseInt( t.tf_ref_row );//filter start row
688	var ncells = getCellsNb( id,start_row );
689	var colValues = new Array();
690
691	for(var i=start_row; i<nrows; i++)//iterates rows
692	{
693		var cell = getChildElms(row[i]).childNodes;
694		var nchilds = cell.length;
695
696		if(nchilds == ncells)// checks if row has exact cell #
697		{
698			for(var j=0; j<nchilds; j++)// this loop retrieves cell data
699			{
700				if(j==colindex && row[i].style.display=="" )
701				{
702					var cell_data = getCellText( cell[j] ).toLowerCase();
703					(num) ? colValues.push( parseFloat(cell_data) ) : colValues.push( cell_data );
704				}//if j==k
705			}//for j
706		}//if nchilds == ncells
707	}//for i
708	return colValues;
709}
710
711function setColWidths(id)
712/*====================================================
713	- sets widths of columns
714=====================================================*/
715{
716	if( hasGrid(id) )
717	{
718		var t = grabEBI(id);
719		t.style.tableLayout = "fixed";
720		var colWidth = t.tf_colWidth;
721		var start_row = parseInt( t.tf_ref_row );//filter start row
722		var row = grabTag(t,"tr")[0];
723		var ncells = getCellsNb(id,start_row);
724		for(var i=0; i<colWidth.length; i++)
725		{
726			for(var k=0; k<ncells; k++)
727			{
728				cell = row.childNodes[k];
729				if(k==i) cell.style.width = colWidth[i];
730			}//var k
731		}//for i
732	}//if hasGrid
733}
734
735function setVisibleRows(id)
736/*====================================================
737	- makes a row always visible
738=====================================================*/
739{
740	if( hasGrid(id) )
741	{
742		var t = grabEBI(id);
743		var row = grabTag(t,"tr");
744		var nrows = row.length;
745		var showPaging = t.tf_displayPaging;
746		var visibleRows = t.tf_rowVisibility;
747		for(var i=0; i<visibleRows.length; i++)
748		{
749			if(visibleRows[i]<=nrows)//row index cannot be > nrows
750			{
751				if(showPaging)
752					row[ visibleRows[i] ].setAttribute("validRow","true");
753				row[ visibleRows[i] ].style.display = "";
754			}//if
755		}//for i
756	}//if hasGrid
757}
758
759function setAlternateRows(id)
760/*====================================================
761	- alternates row colors for better readability
762=====================================================*/
763{
764	if( hasGrid(id) )
765	{
766		var t = grabEBI(id);
767		var row = grabTag(t,"tr");
768		var nrows = row.length;
769		var start_row = parseInt( t.tf_ref_row );//filter start row
770		var visiblerows = new Array();
771		for(var i=start_row; i<nrows; i++)//visible rows are stored in visiblerows array
772			if( row[i].style.display=="" ) visiblerows.push(i);
773
774		for(var j=0; j<visiblerows.length; j++)//alternates bg color
775			(j % 2 == 0) ? row[ visiblerows[j] ].className = "even" : row[ visiblerows[j] ].className = "odd";
776
777	}//if hasGrid
778}
779
780function setColOperation(id)
781/*====================================================
782	- Calculates values of a column
783	- params are stored in 'colOperation' table's
784	attribute
785		- colOperation["id"] contains ids of elements
786		showing result (array)
787		- colOperation["col"] contains index of
788		columns (array)
789		- colOperation["operation"] contains operation
790		type (array, values: sum, mean)
791		- colOperation["write_method"] array defines
792		which method to use for displaying the
793		result (innerHTML, setValue, createTextNode).
794		Note that innerHTML is the default value.
795
796	!!! to be optimised
797=====================================================*/
798{
799	if( hasGrid(id) )
800	{
801		var t = grabEBI(id);
802		var labelId = t.tf_colOperation["id"];
803		var colIndex = t.tf_colOperation["col"];
804		var operation = t.tf_colOperation["operation"];
805		var outputType =  t.tf_colOperation["write_method"];
806		var precision = 2;//decimal precision
807
808		if( (typeof labelId).toLowerCase()=="object"
809			&& (typeof colIndex).toLowerCase()=="object"
810			&& (typeof operation).toLowerCase()=="object" )
811		{
812			var row = grabTag(t,"tr");
813			var nrows = row.length;
814			var start_row = parseInt( t.tf_ref_row );//filter start row
815			var ncells = getCellsNb( id,start_row );
816			var colvalues = new Array();
817
818			for(var k=0; k<colIndex.length; k++)//this retrieves col values
819			{
820				colvalues.push( getColValues(id,colIndex[k],true) );
821			}//for k
822
823			for(var i=0; i<colvalues.length; i++)
824			{
825				var result=0, nbvalues=0;
826				for(var j=0; j<colvalues[i].length; j++ )
827				{
828					var cvalue = colvalues[i][j];
829					if( !isNaN(cvalue) )
830					{
831						switch( operation[i].toLowerCase() )
832						{
833							case "sum":
834								result += parseFloat( cvalue );
835							break;
836							case "mean":
837								nbvalues++;
838								result += parseFloat( cvalue );
839							break;
840							//add cases for other operations
841						}//switch
842					}
843				}//for j
844
845				switch( operation[i].toLowerCase() )
846				{
847					case "mean":
848						result = result/nbvalues;
849					break;
850				}
851
852				if(outputType != undefined && (typeof outputType).toLowerCase()=="object")
853				//if outputType is defined
854				{
855					result = result.toFixed( precision );
856					if( grabEBI( labelId[i] )!=undefined )
857					{
858						switch( outputType[i].toLowerCase() )
859						{
860							case "innerhtml":
861								grabEBI( labelId[i] ).innerHTML = result;
862							break;
863							case "setvalue":
864								grabEBI( labelId[i] ).value = result;
865							break;
866							case "createtextnode":
867								var oldnode = grabEBI( labelId[i] ).firstChild;
868								var txtnode = createText( result );
869								grabEBI( labelId[i] ).replaceChild( txtnode,oldnode );
870							break;
871							//other cases could be added
872						}//switch
873					}
874				} else {
875					try
876					{
877						grabEBI( labelId[i] ).innerHTML = result.toFixed( precision );
878					} catch(e){ }//catch
879				}//else
880
881			}//for i
882
883		}//if typeof
884	}//if hasGrid
885}
886
887function grabEBI(id)
888/*====================================================
889	- this is just a getElementById shortcut
890=====================================================*/
891{
892	return document.getElementById( id );
893}
894
895function grabTag(obj,tagname)
896/*====================================================
897	- this is just a getElementsByTagName shortcut
898=====================================================*/
899{
900	return obj.getElementsByTagName( tagname );
901}
902
903function regexpEscape(s)
904/*====================================================
905	- escapes special characters [\^$.|?*+()
906	for regexp
907	- Many thanks to Cedric Wartel for this fn
908=====================================================*/
909{
910	// traite les caract�res sp�ciaux [\^$.|?*+()
911	//remplace le carct�re c par \c
912	function escape(e)
913	{
914		a = new RegExp('\\'+e,'g');
915		s = s.replace(a,'\\'+e);
916	}
917
918	chars = new Array('\\','[','^','$','.','|','?','*','+','(',')');
919	//chars.each(escape); // no prototype framework here...
920	for(e in chars) escape(chars[e]);
921	return s;
922}
923
924function createElm(elm)
925/*====================================================
926	- returns an html element with its attributes
927	- accepts the following params:
928		- a string defining the html element
929		to create
930		- an undetermined # of arrays containing the
931		couple "attribute name","value" ["id","myId"]
932=====================================================*/
933{
934	var el = document.createElement( elm );
935	if(arguments.length>1)
936	{
937		for(var i=0; i<arguments.length; i++)
938		{
939			var argtype = typeof arguments[i];
940			switch( argtype.toLowerCase() )
941			{
942				case "object":
943					if( arguments[i].length==2 )
944					{
945						el.setAttribute( arguments[i][0],arguments[i][1] );
946					}//if array length==2
947				break;
948			}//switch
949		}//for i
950	}//if args
951	return el;
952}
953
954function createText(node)
955/*====================================================
956	- this is just a document.createTextNode shortcut
957=====================================================*/
958{
959	return document.createTextNode( node );
960}
961
962function DetectKey(e)
963/*====================================================
964	- common fn that detects return key for a given
965	element (onkeypress attribute on input)
966=====================================================*/
967{
968	var evt=(e)?e:(window.event)?window.event:null;
969	if(evt)
970	{
971		var key=(evt.charCode)?evt.charCode:
972			((evt.keyCode)?evt.keyCode:((evt.which)?evt.which:0));
973		if(key=="13")
974		{
975			var cid, leftstr, tblid, CallFn, Match;
976			cid = this.getAttribute("id");
977			leftstr = this.getAttribute("id").split("_")[0];
978			tblid = cid.substring(leftstr.length+1,cid.length);
979			t = grabEBI(tblid);
980			(t.tf_isModfilter_fn) ? t.tf_modfilter_fn.call() : Filter(tblid);
981		}//if key
982	}//if evt
983}
984
985function importScript(scriptName,scriptPath)
986{
987	var isImported = false;
988	var scripts = grabTag(document,"script");
989
990	for (var i=0; i<scripts.length; i++)
991	{
992		if(scripts[i].src.match(scriptPath))
993		{
994			isImported = true;
995			break;
996		}
997	}
998
999	if( !isImported )//imports script if not available
1000	{
1001		var head = grabTag(document,"head")[0];
1002		var extScript = createElm(	"script",
1003									["id",scriptName],
1004									["type","text/javascript"],
1005									["src",scriptPath]	);
1006		head.appendChild(extScript);
1007	}
1008}//fn importScript
1009
1010
1011
1012/*====================================================
1013	- Below a collection of public functions
1014	for developement purposes
1015	- all public methods start with prefix 'TF_'
1016	- These methods can be removed safely if not
1017	needed
1018=====================================================*/
1019
1020function TF_GetFilterIds()
1021/*====================================================
1022	- returns an array containing filter grids ids
1023=====================================================*/
1024{
1025	try{ return TblId }
1026	catch(e){ alert('TF_GetFilterIds() fn: could not retrieve any ids'); }
1027}
1028
1029function TF_HasGrid(id)
1030/*====================================================
1031	- checks if table has a filter grid
1032	- returns a boolean
1033=====================================================*/
1034{
1035	return hasGrid(id);
1036}
1037
1038function TF_GetFilters(id)
1039/*====================================================
1040	- returns an array containing filters ids of a
1041	specified grid
1042=====================================================*/
1043{
1044	try
1045	{
1046		var flts = getFilters(id);
1047		return flts;
1048	} catch(e) {
1049		alert('TF_GetFilters() fn: table id not found');
1050	}
1051
1052}
1053
1054function TF_GetStartRow(id)
1055/*====================================================
1056	- returns starting row index for filtering
1057	process
1058=====================================================*/
1059{
1060	try
1061	{
1062		var t = grabEBI(id);
1063		return t.tf_ref_row;
1064	} catch(e) {
1065		alert('TF_GetStartRow() fn: table id not found');
1066	}
1067}
1068
1069function TF_GetColValues(id,colindex,num)
1070/*====================================================
1071	- returns an array containing cell values of
1072	a column
1073	- needs following args:
1074		- filter id (string)
1075		- column index (number)
1076		- a boolean set to true if we want only
1077		numbers to be returned
1078=====================================================*/
1079{
1080	if( hasGrid(id) )
1081	{
1082		return getColValues(id,colindex,num);
1083	}//if TF_HasGrid
1084	else alert('TF_GetColValues() fn: table id not found');
1085}
1086
1087function TF_Filter(id)
1088/*====================================================
1089	- filters a table
1090=====================================================*/
1091{
1092	var t = grabEBI(id);
1093	if( TF_HasGrid(id) ) Filter(id);
1094	else alert('TF_Filter() fn: table id not found');
1095}
1096
1097function TF_RemoveFilterGrid(id)
1098/*====================================================
1099	- removes a filter grid
1100=====================================================*/
1101{
1102	if( TF_HasGrid(id) )
1103	{
1104		var t = grabEBI(id);
1105		clearFilters(id);
1106
1107		if(grabEBI("inf_"+id)!=null)
1108		{
1109			t.parentNode.removeChild(t.previousSibling);
1110		}
1111		// remove paging here
1112		var row = grabTag(t,"tr");
1113
1114		for(var j=0; j<row.length; j++)
1115		//this loop shows all rows and removes validRow attribute
1116		{
1117			row[j].style.display = "";
1118			try
1119			{
1120				if( row[j].hasAttribute("validRow") )
1121					row[j].removeAttribute("validRow");
1122			} //ie<=6 doesn't support hasAttribute method
1123			catch(e){
1124				for( var x = 0; x < row[j].attributes.length; x++ )
1125				{
1126					if( row[j].attributes[x].nodeName.toLowerCase()=="validrow" )
1127						row[j].removeAttribute("validRow");
1128				}//for x
1129			}//catch(e)
1130		}//for j
1131
1132		if( t.tf_alternateBgs )//removes alterning row colors
1133		{
1134			for(var k=0; k<row.length; k++)
1135			//this loop removes bg className
1136			{
1137				row[k].className = "";
1138			}
1139		}
1140
1141		if(t.tf_fltGrid) t.deleteRow(0);
1142		for(i in TblId)//removes grid id value from array
1143			if(id == TblId[i]) TblId.splice(i,1);
1144
1145	}//if TF_HasGrid
1146	else alert('TF_RemoveFilterGrid() fn: table id not found');
1147}
1148
1149function TF_ClearFilters(id)
1150/*====================================================
1151	- clears grid filters only, table is not filtered
1152=====================================================*/
1153{
1154	if( TF_HasGrid(id) ) clearFilters(id);
1155	else alert('TF_ClearFilters() fn: table id not found');
1156}
1157
1158function TF_SetFilterValue(id,index,searcharg)
1159/*====================================================
1160	- Inserts value in a specified filter
1161	- Params:
1162		- id: table id (string)
1163		- index: filter column index (numeric value)
1164		- searcharg: search string
1165=====================================================*/
1166{
1167	if( TF_HasGrid(id) )
1168	{
1169		var flts = getFilters(id);
1170		for(i in flts)
1171		{
1172			if( i==index ) grabEBI(flts[i]).value = searcharg;
1173		}
1174	} else {
1175		alert('TF_SetFilterValue() fn: table id not found');
1176	}
1177}
1178
1179
1180
1181
1182/*====================================================
1183	- bind an external script fns
1184	- fns below do not belong to filter grid script
1185	and are used to interface with external
1186	autocomplete script found at the following URL:
1187	http://www.codeproject.com/jscript/jsactb.asp
1188	(credit to zichun)
1189	- fns used to merge filter grid with external
1190	scripts
1191=====================================================*/
1192var colValues = new Array();
1193
1194function setAutoComplete(id)
1195{
1196	var t = grabEBI(id);
1197	var bindScript = t.tf_bindScript;
1198	var scriptName = bindScript["name"];
1199	var scriptPath = bindScript["path"];
1200	initAutoComplete();
1201
1202	function initAutoComplete()
1203	{
1204		var filters = TF_GetFilters(id);
1205		for(var i=0; i<filters.length; i++)
1206		{
1207			if( grabEBI(filters[i]).nodeName.toLowerCase()=="input")
1208			{
1209				colValues.push( getColValues(id,i) );
1210			} else colValues.push( '' );
1211		}//for i
1212
1213		try{ actb( grabEBI(filters[0]), colValues[0] ); }
1214		catch(e){ alert(scriptPath + " script may not be loaded"); }
1215
1216	}//fn
1217}