/////////////////////////////////////////////////////////////////////////////
//
// Gladiator Components
//
// http://www.gladiatorweb.com
//
// (c) 2006 by Edward H. Trager .  All Rights Reserved
//
// NAME: gladiatorScrollableTable.js
//
// DESCRIPTION: Implements a scrollable table
//
// AUTHORS: Edward H. Trager and Ritu Khanna
//
// LAST UPDATE: 2006.10.17
//
// NOTE: SEE "LICENSE" FILE FOR LICENSING TERMS
//
//////////////////////////////////////////////////////////////////////////////

//
// gScrollableTable -- version 1.0 --
//
// Version 1.0 currently (2006.10.17) only works in Firefox.
//
// The gladiator ScrollableTable class converts an ordinary XHTML table into
// a scrollable table with:
//
//   (1) A vertically-fixed, always-viewable header row created
//       from an initial row of <th> tags in the table
//   (2) Zero or more horizontally-fixed, always-viewable index columns on the
//       left side of the table.
//
// The scrollableTable adopts the CSS styling of the original XHTML table.
// In the current version, there are some limitations:  
//
//   (1) The borders of the <th> and <td> cells should be left at 1px width only.
//       Other widths currently still lead to improper behaviour.
//   (2) The CSS for the table should set whitespace to no-wrap.
//   (3) Set an arbitrarily narrow width on the table -- something like 100px --
//       to effectively disable the browser's table width-resizing code.
//       
// To control the size of the scrollable table, place it inside of a gladiator Window
// or a <div>.  The default CSS of the scrollableTable will make the table fill the
// rectangular region of an enclosing <div>.
//
// The gladiatorScrollableTable and the gladiatorTableEditor have been designed to work
// hand-in-hand with one another.  The gladiatorTableEditor turns an ordinary XHTML table
// into an editable table.
//
// The proper way of setting up a scrollable and editable table inside of a gladiator
// window is shown below:
//
// ======= SAMPLE JAVASCRIPT CODE FOR SETTING UP A SCROLLABLE AND EDITABLE TABLE ===
// 
//  var theTable       = document.getElementById("myXHTMLTable"); // the table
//  var theDestination = document.getElementById("contents");     // 
//
//  // How many fixed columns do you want?
//  var fixedColumnCount=3;
//  
//  //
//  // Create a new scrollable table with an id of "scrollTable" : attach it
//  // to the "contents" div for now:
//  //
//  var gst = new gScrollableTable("scrollTable", theTable, fixedColumnCount, theDestination );
//  
//  //
//  // Create the gladiator window to enclose the scrollable table:
//  //
//  var theTableWindow = new gWindow("Window Title","windowId",100,300,900,500,theScrollableTable);
//  theTableWindow.allowClose(false); // Don't let user close the window
//
//  //
//  // Make the existing table editable by instantiating a gladiatorTableEditor
//  // on it.  In this example, we want to allow the third column to also be editable
//  // even though it is one of the fixed columns.  So the second parameter below is
//  // set to 2 instead of 3, meaning that the first 2 (index) columns are not editable.
//  //
//  var gte = new gTableEditor(theTable,fixedColumnCount-1,gst);
//  //
//  // Finally, just call the activate method which assigns event handlers on the table
//  // and you are done! :
//  //
//  gte.activateTable(theTable);
//
// ======= END OF SAMPLE JAVASCRIPT CODE FOR SETTING UP A SCROLLABLE AND EDITABLE TABLE ===
//

//
// Class gScrollableTable:
//
//
function gScrollableTable(id,myContent,fixedColumnCount,parentNode){
	
	var _self=this;
	
	//
	//
	//
	var _minimumNumberOfRowsToCopy=1;
	
	var _baseContainer = document.createElementNS(NS.xhtml,"div");
	_baseContainer.setAttribute("class","gstBase");
	_baseContainer.id=id;
	
	appendToParentNode( _baseContainer,parentNode);
	
	var _leftOffset = 200; // Arbitrary -- changed on init
	var _topOffset  =  30; // Arbitrary -- changed on init
	
	var _rowCount=0;
	var _columnCount;
	var _fixedColumnCount = fixedColumnCount;
	var _showRowIndexTable = true;
	
	var _contentNode; // handled by setContent()
	var _contentNodeTableBody; // handled by setContent()
	var _trArray;     // handled by setContent()
	var _tableWidth;  // handled by createHeaders()
	
	var _rowIndexTable;
	var _rowIndexTableBody; // ADDED NOW
	
	var _fixedIndex  = document.createElementNS(NS.xhtml,"div");
	var _columnIndex = document.createElementNS(NS.xhtml,"div");
	var _rowIndex    = document.createElementNS(NS.xhtml,"div");
	var _pane        = document.createElementNS(NS.xhtml,"div");
	var _tray        = document.createElementNS(NS.xhtml,"div");
	_fixedIndex.setAttribute("class","gstFIndex");
	_columnIndex.setAttribute("class","gstCIndex");
	_rowIndex.setAttribute("class","gstRIndex");
	_pane.setAttribute("class","gstPane");
	_tray.setAttribute("class","gstTray");
	
	//
	// Add the special shadows to create the layered dimensional
	// effect of the scrollable table:
	//
	var _gstHzShadow = document.createElementNS(NS.xhtml,"div");
	var _gstVtShadow = document.createElementNS(NS.xhtml,"div");
	_gstHzShadow.setAttribute("class","gstHzShadow");
	_gstVtShadow.setAttribute("class","gstVtShadow");
	
	//
	// Append to DOM tree: Note that the order
	// is fixed, so don't change it unless you
	// really know what you are doing !!!
	//
	_pane.appendChild( _tray );
	_baseContainer.appendChild(_pane       );
	_baseContainer.appendChild(_rowIndex   );
	_baseContainer.appendChild(_columnIndex);
	_baseContainer.appendChild(_fixedIndex );
	_baseContainer.appendChild(_gstVtShadow);
	_baseContainer.appendChild(_gstHzShadow);
	
	//
	// Set the pane's content, if available:
	//
	
	if(myContent){
		setContent(myContent);
	}
	
	
	/////////////////////
	//
	// setContent
	//
	/////////////////////
	function setContent (myContent){
		
		
		if(isString(myContent)){
			// Assume it is html:
			//myDiv.innerHTML = myContent;
			_tray.innerHTML = myContent;
		}else{
			//
			// Assume it is a DOM object:
			// -- This is the preferred way
			// to do it:
			//
			_tray.appendChild(myContent);
		}
		
		//
		// Keep a reference to the content node:
		//
		_contentNode = _tray.firstChild;
		// Set a minimum width for the table: default 100px
		_contentNode.style.width="100px";
		_contentNodeTableBody = _contentNode.getElementsByTagName("tbody")[0];
		//
		// If the content object is a table,
		// do all the fancy stuff for tables:
		//
		if( _contentNode.tagName=="TABLE" || _contentNode.tagName=="table"){
			
			_trArray     = _contentNode.getElementsByTagName("tr");
			if(_trArray.length < _minimumNumberOfRowsToCopy){
				_minimumNumberOfRowsToCopy=_trArray.length;
			}
			createHeaders();
			
			//
			// Find out about the table's CSS
			//
			manipulateCSS();
			
		}
		
	}
	
	//
	// manipulateCSS
	//
	function manipulateCSS(){
		
		//
		// Transfer the border styling from the table to our
		// scrollable table container instead:
		//
		_baseContainer.style.borderLeftColor   = getElementStyle(_contentNode,"border-left-color"  );
		_baseContainer.style.borderLeftWidth   = getElementStyle(_contentNode,"border-left-width"  );
		_baseContainer.style.borderTopColor    = getElementStyle(_contentNode,"border-top-color"   );
		_baseContainer.style.borderTopWidth    = getElementStyle(_contentNode,"border-top-width"   );
		_baseContainer.style.borderRightColor  = getElementStyle(_contentNode,"border-right-color" );
		_baseContainer.style.borderRightWidth  = getElementStyle(_contentNode,"border-right-width" );
		_baseContainer.style.borderBottomColor = getElementStyle(_contentNode,"border-bottom-color");
		_baseContainer.style.borderBottomWidth = getElementStyle(_contentNode,"border-bottom-width");
		
		//
		// Also grab any background color on the first row's cells (which
		// should be "th" header cells):
		//
		var thElement = _contentNode.getElementsByTagName("th")[0];
		_baseContainer.style.backgroundColor = getElementStyle(thElement,"background-color");
		
		//
		// Also copy background color to the column header and fixed header containers:
		//
		_columnIndex.style.backgroundColor = _baseContainer.style.backgroundColor;
		_fixedIndex.style.backgroundColor  = _baseContainer.style.backgroundColor;
		
		//
		// Now make sure that the component "tables" have no border styling
		// (other than any special stuff that we might add):
		//
		// Border styling is set to "none" on all four tables:
		//
		_contentNode.style.border   = "none";
		_rowIndexTable.style.border = "none";
		
	}
	
	//
	// transferStyleSizeAttributes() : Wrapper around getComputedWidthAndHeight()
	//
	function transferStyleSizeAttributes(fromElement,toElement){
		
		var cellDimensions = getComputedWidthAndHeight(fromElement);
		toElement.style.width  = cellDimensions[0] + "px";
		toElement.style.height = cellDimensions[1] + "px";
		
	}
	
	//
	// createHeaders()
	//
	function createHeaders(){
		
		var clone;
		var cellsInRow;
		var width;
		var height;
		var i;
		var j;
		
		// ROW INDEX TABLE:
		_rowIndexTable    = document.createElementNS(NS.xhtml,"table");
		_rowIndexTableBody    = document.createElementNS(NS.xhtml,"tbody");
		_rowIndexTable.appendChild(    _rowIndexTableBody    );
		
		//
		// get table width:
		// 
		_tableWidth  = getElementStyle(_contentNode,"width");
		
		//////////////////
		//
		// COLUMN INDEX: Create the column index headers:
		//
		// FIXED INDEX: Is also done here in the same loop:
		//
		//////////////////
		var thArray = _contentNode.getElementsByTagName("th");
		
		var fixedIndexHeaderContainer=document.createElementNS(NS.xhtml,"div");
		_columnCount = thArray.length;
		_leftOffset=0;
		for(i=0;i<_columnCount;i++){
			//
			// The header cells are now actually a bunch of divs that
			// are floated left -- This works better than using tables
			// and table cells:
			//
			var headerCell = document.createElementNS(NS.xhtml,"div");
			headerCell.setAttribute("class","gstTHCell");
			var textLabel  = document.createTextNode(thArray[i].firstChild.nodeValue);
			headerCell.appendChild(textLabel);
			//
			// Set width and height based on the _contentNode table:
			//
			var headerCellDimensions = getComputedWidthAndHeight(thArray[i]);
			headerCell.style.width  = headerCellDimensions[0] + "px";
			headerCell.style.height = headerCellDimensions[1] + "px";
			
			//
			// Accumulate the width required for the
			// row index table:
			if(i<_fixedColumnCount){
				_leftOffset += headerCellDimensions[0];
			}
			
			//
			// Copy relevant style attributes so that the divs look just like
			// the table's original <th>s looked:
			//
			var style  = document.defaultView.getComputedStyle(thArray[i],null);
			headerCell.style.backgroundColor = style.getPropertyValue("background-color");
			headerCell.style.color           = style.getPropertyValue("color");
			headerCell.style.fontFamily      = style.getPropertyValue("font-family");
			headerCell.style.fontWeight      = style.getPropertyValue("font-weight");
			headerCell.style.fontSize        = style.getPropertyValue("font-size");
			headerCell.style.textAlign       = style.getPropertyValue("text-align");
			//
			// Since we are using floating divs, we need to
			// simulate the table "collapse" property
			// by setting one of the border widths to zero:
			// 
			headerCell.style.borderRightWidth  = style.getPropertyValue("border-right-width");
			headerCell.style.borderLeftWidth   = "0px";
			headerCell.style.borderTopWidth    = "0px";
			headerCell.style.borderBottomWidth = "0px";
			headerCell.style.borderRightColor  = style.getPropertyValue("border-right-color");
			
			//
			// Padding:
			//
			// ==> NOTE BENE: Don't pad left and right unless you want to 
			//     completely screw up the widths:
			//
			// NO-NO: headerCell.style.paddingLeft   = style.getPropertyValue("padding-left");
			// NO-NO: headerCell.style.paddingRight  = style.getPropertyValue("padding-right");
			//
			// ==> But pad top and bottom as one would expect:
			//
			headerCell.style.paddingTop    = style.getPropertyValue("padding-top");
			headerCell.style.paddingBottom = style.getPropertyValue("padding-bottom");
			
			//
			// Append to columnIndex ... :
			//
			_columnIndex.appendChild(headerCell);
			if(i<_fixedColumnCount){
				//
				// ... and also append to fixedIndex for fixed column count:
				//
				fixedIndexHeaderContainer.appendChild( headerCell.cloneNode(true) );
			
			} 
			
		}
		
		//
		// Add the width of the border to _leftOffset:
		// This is done so that the containers are (theoretically)
		// exactly wide enough:
		//
		_leftOffset += parseInt(headerCell.style.borderRightWidth);
		
		
		// The column index div needs to be a little wider than the table width
		// because otherwise the very last floated div will wrap to the next
		// line due to the width added by the border.  Adding 1000 px is not as arbitrary
		// as it looks -- This allows for multiple columns to get expanded widths
		// without having to worry about the floated divs wrapping:
		//
		_columnIndex.style.width= parseInt(_tableWidth)+1000+"px";
		
		//
		// fixedIndexHeaderContainer can be any width that is 
		// wider than the fixed column width (again due to the floated divs).  
		// Making it the same width as the column index is as good 
		// as any other choice:
		//
		fixedIndexHeaderContainer.style.width=_columnIndex.style.width;
		//
		// Append the fixedIndexHeaderContainer.  The fixedIndexHeader
		// container is used rather than the _fixedIndex directly because
		// of the issues with the floated divs wrapping.  As long as
		// _fixedIndex has CSS overflow:hidden set and fixedIndexHeaderContainer
		// is wide enough to prevent wrapping of the floated divs, it works:
		//
		_fixedIndex.appendChild(fixedIndexHeaderContainer);
		
		//////////////////
		//
		// ROW INDEX: Create the row index table:
		//
		//////////////////
		
		for(i=0;i<_trArray.length;i++){
			// Make a new row:			
			var row = document.createElementNS(NS.xhtml,"tr");
			//
			// Get the tds from this row in the original table:
			// We must use getElementsByTagName because in practice nodes
			// Other than TD and TH may occur.  The first row is assumed
			// to have THs instead of TDs:
			//
			cellsInRow = _trArray[i].getElementsByTagName(i==0?"th": "td");
			for(j=0;j<_fixedColumnCount;j++){
				//clone = _trArray[i].childNodes[j].cloneNode(true);
				clone = cellsInRow[j].cloneNode(true);
				row.appendChild(clone);
			}
			_rowIndexTableBody.appendChild(row);
		}
		
		_rowIndex.appendChild( _rowIndexTable);
		
		///////////////////////////////////////////////////////
		//
		// Calculate _topOffset ... :
		//
		// ==> NOTE BENE: _leftOffset is now calculated above.
		//
		///////////////////////////////////////////////////////
		
		_topOffset  = getComputedWidthAndHeight(_contentNode.getElementsByTagName("tr")[0])[1];
		
		//
		// ... and adjust accordingly:
		//
		
		//
		// Make the row index exactly the width of the leftmost 
		// "fixedColumnCount" columns:
		//
		_rowIndex.style.width     = _leftOffset + "px";
		_fixedIndex.style.width   = _rowIndex.style.width;
		
		//
		// Make the columnIndex height and fixedIndex height
		// equal to the height of the first row only:
		//
		_columnIndex.style.height = _topOffset + "px";
		_fixedIndex.style.height  = _topOffset + "px";
		
		//
		// Set the scrollable pane so that the columnIndex row on the top
		// and the fixedColumns on the left are showing:
		//
		_pane.style.left = _leftOffset + "px";
		_pane.style.top  = _topOffset  + "px";
		//
		// ... and as a result, the scrollable tray containing the original table
		// needs to be offset by exact distances in the *opposite* direction:
		//
		// FUDGE FACTOR #1: The "-1" is a little fudge factor.  I think there are
		// little round off patterns in Firefox, but I haven't really figured it
		// all out yet.  In any case, this works with tables with 1 px cell borders:
		//
		_tray.style.left = "-" + ( _leftOffset -1 ) + "px";
		_tray.style.top  = "-" + ( _topOffset  ) + "px";
		
		//
		// Finally, place the shadows where they should be:
		//
		_gstHzShadow.style.top   = _topOffset  + "px";
		_gstVtShadow.style.left  = _leftOffset + "px";
		
		//
		// FUDGE FACTOR #2: Again, not sure why, but this is needed to get
		// pixel-perfect visual alignment in Firefox when tables have 1px cell borders:
		//
		_fixedIndex.style.left = "-1px";
		
	}
	
	
	
	
	//
	// setScrolledPositions
	//
	function setScrolledPositions(){
		// Set row index position:
		var top  = "-" + _pane.scrollTop  + "px";
		var left = "-" + _pane.scrollLeft + "px";
		
		if( _rowIndex.style.top != top      ) _rowIndex.style.top     = top;
		if( _columnIndex.style.left != left ) _columnIndex.style.left = left;
	}
	
	//
	// setHorizontalScrolledPosition
	//
	function setHorizontalScrolledPosition(){
		_columnIndex.style.left = "-" + _pane.scrollLeft + "px";;
	}
	
	//
	// setVerticalScrolledPosition
	//
	function setVerticalScrolledPosition(){
		_rowIndex.style.top = "-" + _pane.scrollTop  + "px";
	}
	
	////////////////////////
	//
	// PUBLIC METHODS:
	//
	////////////////////////
	
	//
	// getFixedColumnCount()
	//
	this.getFixedColumnCount = function(){
		return _fixedColumnCount;
	};
	
	//
	// getRowCount()
	//
	this.getRowCount = function(){
		return _rowCount;
	};
	
	//
	// getColumnCount()
	//
	this.getColumnCount = function(){
		return _columnCount;
	};
	
	//
	// showRowIndex(): Accepts a boolean argument which
	//                 determines whether to display or
	//                 not display the rowIndexTable
	//                 (which holds the fixed columns)
	//
	this.showRowIndex = function(showIt){
		
		//
		// Don't do anything if there is no
		// change in state:
		//
		if(_showRowIndexTable == showIt) return;
		
		//
		// Get here if there was a state change:
		//
		if(showIt){
			//
			// show the row index:
			//
			_rowIndex.style.display="block";
			_pane.style.left = _leftOffset + "px";
			_tray.style.left = "-" + (_leftOffset ) + "px";
			
			_showRowIndexTable=true;
			
		}else{
			//
			// hide row index:
			//
			_rowIndex.style.display="none";
			_pane.style.left = "0px";
			_tray.style.left = "0px";
			
			_showRowIndexTable=false;
			
		}
	};
	
	//
	// setHeaderWidth
	//
	this.setHeaderWidth = function(fromNode,index){
		
		var newWidth = getComputedWidthAndHeight(fromNode)[0] + "px";
		var oldWidth = _columnIndex.childNodes[index].style.width;
		//alert(newWidth);
		if (newWidth != oldWidth){
			_columnIndex.childNodes[index].style.width = newWidth;
			
			if(index < _fixedColumnCount){
				//
				// fix the _fixedIndex header width as well:
				//
				_fixedIndex.firstChild.childNodes[index].style.width = newWidth;
				//
				// ... this requires fixing _offsetLeft as well:
				//
				var diff = parseInt(newWidth) - parseInt(oldWidth);
				_leftOffset += diff;
				//
				// ... and then updating _rowIndex, _fixedIndex, and so on:
				//     
				_rowIndex.style.width     = _leftOffset + "px";
				_fixedIndex.style.width   = _rowIndex.style.width;
				_pane.style.left          = _rowIndex.style.width;
				_tray.style.left = "-" + (_leftOffset ) + "px";
				_gstVtShadow.style.left  = _leftOffset + "px";
				
			}
		}
		_self.showRowIndex(true);
	};
	
	//
	// this.setRowIndexTableValue
	//
	this.setRowIndexTableValue = function (row,col,value){
		
		//
		// Only apply if the column is one of the fixed columns:
		//
		if(col>_fixedColumnCount-1) return;
		
		var updateRow  = _rowIndexTable.getElementsByTagName("tr");
		//
		// Add one to the row because the header row in the original
		// table does not have "row" and "column" attributes:
		//
		row = parseInt(row);
		row +=1;
		var updateCell = updateRow[row].getElementsByTagName("td");
		updateCell[col].firstChild.nodeValue = value;
		
	};
	
	
	
	this.show = function(){
		
		_baseContainer.style.display="";
		
	};
	
	this.hide = function(){
		
		_baseContainer.style.display = "none";
		
	};
	
	//
	// getNode(): returns the actual table node
	//
	this.getNode = function(){
		return _contentNode;
	};
	
	//
	// getBaseNode(): returns the scrollable table node
	//
	this.getBaseNode = function(){
		
		return _baseContainer;
		
	};
	
	this.addRow = function(tableRowNode){
		
		
		//
		// add to the underlying table:
		//
		_rowCount++;
		_contentNodeTableBody.appendChild(tableRowNode);
		
		//
		// also add the _fixedColumnCount columns to the
		// _rowIndex table:
		//
		var tdArray = tableRowNode.getElementsByTagName("td");
		var newRow  = document.createElementNS(NS.xhtml,"tr");
		for(var j=0;j<_fixedColumnCount;j++){
			newRow.appendChild( tdArray[j].cloneNode(true) );
		}
		// Copy the class attribute from the tableRowNode:
		var trClass = tableRowNode.getAttributeNS(null,"class");
		newRow.setAttributeNS(null,"class",trClass);
		// Set the id attribute of the new row:
		newRow.id = tableRowNode.id + "_ri";
		_rowIndexTableBody.appendChild(newRow);
		
	};
	
	this.updateHeaderWidths = function(){
		
		_trArray = _contentNode.getElementsByTagName("tr");
		// Update the header widths based on the first data row:
		var tdElements = _trArray[1].getElementsByTagName("td");
		
		//alert(tdElements[4].offsetParent.offsetWidth);
		var index;
		for(index=0;index<tdElements.length;index++) _self.setHeaderWidth(tdElements[index],index);
		
	};
	
	this.deleteRow = function(rowId){
		
		_contentNodeTableBody.removeChild(document.getElementById(rowId));
		var rowIndexId = rowId+"_ri";
		var tr = document.getElementById(rowIndexId);
		_rowIndexTableBody.removeChild(tr);
		_rowCount--;
		
	};
	
	this.deleteRowByIndex = function(index){
		
		if(index < _rowCount){
			_contentNodeTableBody.removeChild(_contentNodeTableBody.getElementsByTagName("tr")[index]);
			_rowIndexTableBody.removeChild(_rowIndexTableBody.getElementsByTagName("tr")[index]);
			_rowCount--;
		}
		
	};
	
	this.deleteAllDataRows = function(){
		
		if(_rowCount == 0) return;
		var trElements = _contentNodeTableBody.getElementsByTagName("tr");
		var rowIndexElements = _rowIndexTableBody.getElementsByTagName("tr");
		while(_rowCount > 0){
			_contentNodeTableBody.removeChild(trElements[1]);
			_rowIndexTableBody.removeChild(rowIndexElements[1]);
			_rowCount--;
		}
	};
	
	this.highlightRow = function(rowId,classx,scroll){
		
		var row = document.getElementById(rowId);
		if(row){
			if(scroll) row.scrollIntoView(false);
			row.className = classx;
			rowId.className = classx;
			var rowIndexId = rowId+"_ri";
			document.getElementById(rowIndexId).className = classx;
		}
		
	};
	
	this.replaceId = function(oldId,newId){
		
		var row = document.getElementById(oldId);
		if(row){
			row.id = newId;
			var rowIndexOldId = oldId + "_ri";
			var rowIndexNewId = newId + "_ri";
			document.getElementById(rowIndexOldId).id = rowIndexNewId;
			
			// Copy the contents of the fixed columns from the underlying table to the rowIndexTable:
			var tdArray = row.getElementsByTagName("td");
			var rowIndexTDArray = document.getElementById(rowIndexNewId).getElementsByTagName("td");
			for(var j=0;j<_fixedColumnCount;j++){
				rowIndexTDArray[j].firstChild.nodeValue = tdArray[j].firstChild.nodeValue;
			}
		}
	};
	
	//
	// Amazingly enough, the following actually works.  Using "setInterval"
	// means that the scrollable window now appears to update itself even on
	// mouse clicks and other things where the mouse has not "moved".  This
	// appears to be a better solution.
	//
	// NOTE BENE: If the timer is really small, like 10ms, then multiple
	// events accumulate on the stack and only get repainted when there is
	// an opportunity later.  so 10ms is too short. 100ms seems to work OK
	// without slowing down the browser too much.
	//
	window.setInterval(setScrolledPositions,100);
	
}
