					
						var Grid = Class.create();
						Grid.prototype = {

								downimage: "/img/bullet_arrow_down.png",
								upimage: "/img/bullet_arrow_up.png",
								thcss: "text-decoration:none",
								linktemplate: '<a href="" onclick="#{js}">#{text}</a>',
								prev: '<< Seite #{page}',
								next: 'Seite #{page} >>',
								itemsperpage: 10,

								initialize: function ( id, config ) {
										config = config || { hook: 'grid' };

										// hide the table initially until we paginate
										//$(id).hide();

										this.content = new Array();
										this.type = new Array();
										this.indexes = new Array();
										this.id = id;
										this.page = 1;

										// cycle through thead descendents and make them links
										var theads = $(id + '_auctions' ).getElementsBySelector('thead');

										for ( var i = 0, theads_len = theads.length; i < theads_len; ++i ) {
												var thead = theads[i];
												var ths = thead.getElementsBySelector('th');

												for ( var j = 0, ths_len = ths.length; j < ths_len; ++j ) {
														var th = ths[j];
														var text = th.innerHTML;

														this.type.push( null );

														th.innerHTML = '<a style="' + Grid.prototype.thcss + '" href="javascript:' + config.hook + '.sort(' + j + ')">' + text + '</a>';

												}
										}
										this.table = $(id + '_auctions' ).getElementsBySelector('tbody').shift();

										// cycle through trs and shove parsed contents into an array
										var trs = this.table.getElementsBySelector('tr');
										for ( var i = 0, trs_len = trs.length; i < trs_len; ++i ) {
												var tr = trs[i];
												var css = tr.readAttribute('class');
												var tds = tr.getElementsBySelector('td');
												
												this.content.push( $A( new Array ) );

												// store the raw HTML for sorting / filtering purposes
												this.content[i][tds.length] = '<tr class="' + css +'">' + tr.innerHTML + '</tr>';

												// flag for filtering purposes
												this.content[i][tds.length + 1] = 1;

												for ( var j = 0, tds_len = tds.length; j < tds_len; ++j ) {
														var td = tds[j];
														var text = '';

														var html = new String( td.innerHTML );
														text = html.gsub( /<\/?[^>]+>/, '' );

														// parse each column and store as rich object 
														switch (true) {
														case  parseTimer( html, false ):

																// note: we are parsing the HTML, not the stripped text
																this.content[i][j] = parseTimer( html, true );
																this.type[j] = this.type[j] != undefined ? this.type[j] : new Timer();
																break;

														case parseCurrency( text, false ):
																this.content[i][j] = parseCurrency( text, true );
																this.type[j] = this.type[j] != undefined ? this.type[j] : new Currency();
																break;

														case parseState( html, false ):

																// note: we are parsing the HTML, not the stripped text
																this.content[i][j] = parseState( html, true );
																this.statecol = i;
																this.type[j] = this.type[j] != undefined ? this.type[j] : new State();
																break;

														case !isNaN( parseInt( text ) ):
																this.content[i][j] = new Number( parseInt( text ) );
																this.type[j] = this.type[j] != undefined ? this.type[j] : new Number();
																break;

														default:
																this.content[i][j] = text;
																this.type[j] = this.type[j] == undefined ? new String() : this.type[j];
																break;
														}
												}
										}

										this.render();
										// debug - if you decide to nick this scriptlet, this bit is where
										// most things go wrong...
										
										/*
										this.type.each( function( t ) {
														switch (true) {
														case t instanceof Number:
																trace( "Number" );
																break;
														case t instanceof String:
																trace( "String" );
																break;
														case t instanceof Currency:
																trace( "Currency" );
																break;
														case t instanceof Timer:
																trace( "Timer" );
																break;
														case t instanceof State:
																trace( "State" );
																break;
														default:
																trace( "Type not known" );
																}
														});
										*/
								},

								// sorts based on column T -- each type must have a compare method
								sort: function ( T ) {
										var N = this.content.length;

										// sets a flag to do reverse sorting on the next lookup
										var desc = ( this.rev == T ) ? true : false;
										this.rev = ( this.rev == T ) ? null : T;

										// shell sort ( credit to wikipedia, adapted from java example )
										for ( var k = parseInt( this.content.length / 2 ); k > 0;	k = ( k == 2 ? 1 : Math.round( k / 2.2 ) ) ) {
												for ( var i = k; i < this.content.length; i++ ) {
														for ( var j = i; j >= k && this.type[T].compare( this.content[j - k][T], this.content[j][T] ); j -= k ) {
																var temp = this.content[j];
																this.content[j] = this.content[j - k];
																this.content[j - k] = temp;
														}
												}
										}

										for ( var i = 0; i < this.content.length; i++ ) {
												this.togglesortimage( i, Grid.prototype.upimage );
										}

										if (desc) {
												this.content.reverse();
										} else {
												this.togglesortimage( T, Grid.prototype.downimage );
										} 

										this.render();

								},

								togglesortimage: function( T, img ) {
										var tag = $( this.id + '_sort_' + T );
										if ( tag != undefined ) {
												if ( img != undefined ) {
														tag.src = img;
												} 
										}
							 },

								// this is a filter function for the status of auctions, which takes a
								// function name as argument.  the status class is called with this
								// function for each row, to determine how to sort the table. the table
								// is then rendered.
								
								show: function ( func ) {

										// this obscurity retrieves the column for state objects
										var col = this.type.indexOf( this.type.select( function (c) { return c instanceof State;  } ).shift() ) ;
										var tds_len = this.type.length;

										// cycle through our content array and generate a new table
										for ( var i = 0, content_len = this.content.length; i < content_len; ++i ) {
												var item = this.content[i];
												if ( item instanceof Array && item[col] != null ) {
														try{

																// the evaluated function call returns true or false, and the
																// table row "visibility" flag is toggled correspondingly.
																
																if ( eval( 'item[col].' + func + '()' ) ) {
																		this.content[i][tds_len + 1] = 1;
																} else {
																		this.content[i][tds_len + 1] = 0;
																}

														} catch ( e ) {
																//alert("Unsupported browser: sorry, you are too old.. meh" );
																//break;
														}
												}

										}
										this.render();
								},

								// the render method cycles through the content array and replaces
								// tds of the table individually. TODO: find a more performant innerHTML
								
						render: function ( page ) { 
																		
										page = page == undefined ? 1 : page;
										this.page = page;

										var tds_len = this.type.length;

										// remove all trs from the table
										var trs = this.table.getElementsBySelector('tr');
										for ( var i = 0, trs_len = trs.length; i < trs_len; ++i ) {
												var tr = trs[i];
												tr.remove();
										}

										//for ( var i = 0, content_len = this.content.length; i < content_len; ++i ) {
										//var i = ( page - 1 ) * Grid.prototype.itemsperpage + 1;
										
										// cycle through our content array and generate a filtered table
										var pseudotable = new Array();
										for ( var i = 0; i < this.content.length; ++i ) {
												if ( this.content[i][tds_len + 1] ) {
														pseudotable.push( this.content[i] );
												}
										}

										// the following statements modify the associated html entities
										// depending on which page we are on

										if ( pseudotable.length > page * Grid.prototype.itemsperpage ) {
												// not the last page
											
												var content_len = page * Grid.prototype.itemsperpage;
												var items_this_page = Grid.prototype.itemsperpage;

												// add the link to switch to the next page
												$( 'nextpage_' + this.id ).innerHTML =
														new Template( Grid.prototype.linktemplate ).evaluate( 
																{ 
																		js: "var table = eval( 'grid_" + this.id + "' ); table.render( table.page + 1); return false;",
																		text: new Template( Grid.prototype.next ).evaluate( { page: this.page + 1 } )
																} 
														);

										} else {
												// last page
												
												var content_len = pseudotable.length
												var items_this_page = content_len - ( ( page - 1 ) * Grid.prototype.itemsperpage );
												//var items_this_page = ( page * Grid.prototype.itemsperpage ) - content_len - 2;
														
												// remove the link to switch to the next page
												$( 'nextpage_' + this.id ).innerHTML = '';
										}

										if ( page > 1 ) {
												// not the first page
												
												// add the link to switch to the next page
												$( 'prevpage_' + this.id ).innerHTML =
														new Template( Grid.prototype.linktemplate ).evaluate( 
																{ 
																		js: "var table = eval( 'grid_" + this.id + "' ); table.render( table.page - 1); return false;",
																		text: new Template( Grid.prototype.prev ).evaluate( { page: this.page - 1 } )
																} 
														);
										} else {
												// first page
												
												// remove the link to switch to the next page
												$( 'prevpage_' + this.id ).innerHTML = '';
										}

										// cycle through our content array and generate a new table
										for ( var i = 0, content_len, items_this_page; i < items_this_page; ++i) {
												var item = pseudotable[content_len - i - 1];
												if ( item != undefined ) {
														if ( item[tds_len + 1] ) {
																new Insertion.Top( this.table, item[tds_len] );
																i = i++;
														}
												}
										}


								}
						};

						// Currency class - ported from adsix currency model
						var Currency = Class.create();
						Currency.prototype = {
								initialize: function ( n ) {
										if ( n != undefined ) {
												n = n.toString();
												this.num = this.convert( n );
										}
								},

								toString: function () {
										if ( this.num != 0 ) {
												return this.format();
										} else {
												return "0,00&euro;";
										}
								},

								toFloat: function () {
										return parseFloat( this.num );
								},

								format: function () {
										var cents = Math.floor( ( this.num - Math.floor( this.num ) ) * 100.00 );
										var euros = parseInt( Math.floor( this.num ) ).toString(); 

										euros = this.dots( euros );
										if ( cents != 0 ) {
												cents = cents.toString();
												if ( cents.length == 1 ) {
														cents = '0' + cents;
												}
												return ( euros + ',' + cents) + '&euro;';
										} else {
												return euros + ',00&euro;';
										}
								},

								convert: function ( arg ) {
										arg = arg.toString();
										if ( /[\.,]/.test( arg ) ) {
												arg = arg.gsub( /\./, "" );
												arg = arg.gsub( /,/, "." );
										}
										if ( /[^0-9]$/.test( arg ) ) {
												arg = arg.substring( 0, arg.length - 1 );
										}
										return parseFloat( arg );
								},

								dots: function ( n ) {
										for ( var i=1, n_len = Math.ceil( n.length / 3.0 ); i < n_len; ++i ) {
												n = n.toString().substring( 0, n.length + 1 - i * 4 ) 
																		+ '.'  
																		+ n.toString().substring( n.length + 1 - i * 4 ); 
										}
										return n;
								},

								compare: function( a, b ) {
										return a.num > b.num;
								}
						};

						// a generic test to see if a string can be parsed as a currency
						function parseCurrency( str, bool ) {
								//str = str.escapeHTML();
								if ( /[0-9\.]+,[0-9]*./.test( str ) ) {
										return ( bool ) ? new Currency( str.strip() ) : true;
								} else {
										return false;
								}
						}


						// Timer class - handles "??h ??m"  (does not work currently, 
						// string compare works fine for timers.. do we still need this?)
						var Timer = Class.create();
						Timer.prototype = {
								initialize: function ( t ) {
										if ( t != undefined ) {
												t = t.toString();
												this.time = this.convert( t );
										}
								},

								convert: function ( arg ) {
										return parseFloat( arg.match( /<[^>]+duration=([0-9\.]+)/ )[1] );
								},

																	/*
								toString: function () {
										var now = new Date();
										var hours = parseInt( Math.floor ( ( this.time.getTime() - now.getTime() ) / 1000 / 3600 ) );
										var minutes =	Math.round( ( ( this.time.getTime() - now.getTime() ) / 1000 / 3600 - parseFloat( hours ) ) * 60 );
										return hours + 'h ' + minutes +'m';
								},
								*/

								compare: function( a, b ) {
										return a.time > b.time;
								}
						};

						// a test to see if a string can be parsed as a timer - we look
						// for the comment <!-- duration=Float -->
						
						function parseTimer ( str, bool ) {
								if ( /<[^>]*duration=[0-9\.]+[^>]*>/.test( str ) ) {
										return ( bool ) ? new Timer( str ) : true;
								} else {
										return false;
								}
						}

						
						// Handles Auction States
						var State = Class.create();
						State.prototype = {
								initialize: function ( s ) {
										if ( s != undefined ) {
												s = s.toString();
												var name = this.convert( s );
												var num = 0;
												switch (true) {
												case name == "none":
														num = 1;
														break;
												case name == "grey":
														num = 2;
														break;
												case name == "green":
														num = 3;
														break;
												case name == "orange":
														num = 4;
														break;
												case name == "red":
														num = 5;
														break;
												}

												this.name = name;
												this.num = num;
										}
								},

								convert: function ( arg ) {
										return arg.match( /<[^>]+jstate=([A-Za-z]+)/ )[1];
								},

								compare: function( a, b ) {
										return a.num < b.num;
								},


								// a bit of a misnomer -- returns whether this state is in the "all" tab
								show_all: function() {
																				return ( this.num > 0 );
																		},

								// a bit of a misnomer -- returns whether this state is in the "running" tab
								show_running: function() {
																				return ( this.name == "none" );
																		},

								// a bit of a misnomer -- returns whether this state is in the "won" tab
								show_won: function() {
																				return ( this.num > 1 );
																		}

						}

						function parseState( str, bool ) {
								if ( /<[^>]*jstate=[A-Za-z]+[^>]*>/.test( str ) ) {
										return ( bool ) ? new State( str ) : true;
								} else {
										return false;
								}
						}

						Object.extend( Number.prototype, {
								compare: function( a, b ) {
										return a > b;
								}
						});

						Object.extend( String.prototype, {
								compare: function( a, b ) {
										a = a.toString();
										b = b.toString();
										return a > b;
								}
						});


						function trace( arg ) {
								if ( document.body ) {
										var body = document.body;
								} else {
										var body = document.documentElement;
								}
								new Insertion.Top( body, '<pre>' + arg + '</pre>' );
								return true;
						}

	
