/*	ADD IMAGE SWAP -------------------------------------------------
	adds a JS mouse over swap to all image inputs and image links
	in the document with '_off' or '_on' as the last bit of the
	filename before the extension; only works if the images for both
	states are in the same folder

	REQUIRED JAVASCRIPT
	- prototype.js version  1.5.0_rc0 or higher (can be obtained
	  by downloading script.aculo.us at http://script.aculo.us)
	  this is just used to add the event handlers, including the
	  ONLOAD handler used to run the script
	- a patch to prototype to fix the bug described at:
	  http://dev.rubyonrails.org/ticket/4642
	- getAttr - script to paper over the differences in accessing
	  attributes x-browser

	REQUIRED HTML
	- A elments with IMG children or INPUTs of type IMAGE; the SRC
	  attribute of the IMG or INPUTS needs to be a filename ending
	  with _off.ext where 'ext' is the file extension, and the 'on'
	  state needs to be an image with the same filename save that
	  _off is replaced with _on & it needs to live in the same
	  directory as the off state image
*/
function Addswap() {	
//	declare variables and get all input elements
	var	inputs = document.getElementsByTagName('input'),
		hyperlinks = document.getElementsByTagName('a'),
		numInputs = inputs.length,
		numLinks = hyperlinks.length,
		childImages,
		numImages,
		i,
		j;
//	loop through the inputs
	for (i = 0; i < numInputs; i++) {
//		find the ones that are of type 'image'
		if (getAttr(inputs[i], 'type').toLowerCase() == 'image') {
//			pass image inputs to the function that adds the swap
			addSwap(inputs[i]);
		}
	}

//	loop through the anchors
	for (i = 0; i < numLinks; i++) {
//		find the ones that have image children
		childImages = hyperlinks[i].getElementsByTagName('img');
		numImages = childImages.length;
		if (numImages > 0) {
//			pass the images to the function that adds the swap
			for (j = 0; j < numImages; j++) {
				addSwap(childImages[j]);
			}
		}
	}
	return true;

	function addSwap(el) {
		var src = getAttr(el, 'src');
		if ((typeof src == 'string') && (src.indexOf('_off.') != -1)) {
//			if the image name contains 'off' set the src of off state img to the current
//			src and swap the 'off' with 'on' to get the src for the on state
			el.offImg = new Image();
			el.offImg.src = src;
			el.onImg = new Image();
			el.onImg.src = src.replace(/_off\./,'_on.');

//			add mouseover and mouseout functions
			Event.observe( el, 'mouseover', function() { el.src = el.onImg.src; return true; }, false);
			Event.observe( el, 'mouseout', function() { el.src = el.offImg.src; return true; }, false);
			Event.observe( window, 'unload', function() { el.offImg = null; el.onImg = null; return true; }, false);
		} else return false;
		return true;
	}

}
Event.observe( window, 'load', function() {Addswap(); return true;}, false);

/*	NEW WINDOW LINK ------------------------------------------------
	object which will add a checkbox preference to a page to open
	links in new windows, then remembers that preference via a 
	cookie; links to open in new windows are specified w/a class
	either on a link or on a containing element (1 class per link,
	the link to apply the 'new window' function to must be the 1st
	link in the container)
	
	PARAMETERS
	
	- controllerID = string; id within which the controller form will
	  be appended
	- resultsClass = string; class of the A elements to open in new
	  windows (or their parents); need 1 per A; if class is on a
	  parent, the A must be the first A child of the container
	- resultsId = string; optional ID of an element containing all
	  the results -- just speeds up the query finding the results

	REQUIRED HTML
	- block-level element with an ID supplied as controllerID
	- A or parent of an A with a class of resultsClass; if a
	  parent, then the A must be the first A child of the parent
	- element containing all A elements to be modified with an id
	  of resultsId (optional)

	REQURIED JAVASCRIPT
	
	- prototype.js version  1.5.0_rc0 or higher (can be obtained
	  by downloading script.aculo.us at http://script.aculo.us)
	  this is just used to add the event handlers, including the
	  ONLOAD handler used to run the script
	- a patch to prototype to fix the bug described at:
	  http://dev.rubyonrails.org/ticket/4642
	- getAttr - script to paper over the differences in accessing
	  attributes x-browser
	- setAttr - script to paper over the differences in accessing
	  attributes x-browser
	- magicCookie - cookie management object
*/
var newWindowResults = Class.create();
newWindowResults.prototype = {
	initialize : function (controllerId,resultsClass,resultsId) { var container; if (container = $(controllerId)) {
		var me = this,
//		append a FORM to the control container
			form = container.appendChild(document.createElement('form')),
//		append a LABEL to the FORM
			label = form.appendChild(document.createElement('label')),
//		get the cookie, if any
			cookie = eval(magicCookie.getCookie(resultsId));

		this.resultsId = resultsId;
		this.resultsClass = resultsClass;

//		add a method & action
		setAttr(form,'action','#');
		setAttr(form,'method','get');
//		append a text node to the LABEL
		label.appendChild(document.createTextNode('Open links in new window'));
//		append a checkbox to the LABEL
		this.checkbox = document.createElement('input');
//		need to set the TYPE attribute before appending or IE gets crabby
		setAttr(this.checkbox,'type','checkbox');
//		need to hook the LABEL & INPUT with an ID or IE won't let one click the ID to tick the box
		setAttr(this.checkbox,'id',this.resultsId + 'checkbox')
		setAttr(label,'for',this.resultsId + 'checkbox');
		this.checkbox = label.appendChild(this.checkbox);
//		if the cookie above is 'true', set the checkbox to 'checked'
//		set an event handler on the checkbox to toggle the 'new window links' on or off
		Event.observe(this.checkbox,'click',function(){ return me.toggleNewWindows(); },false);

//		if the cookie is 'true', then we should check the box + 
		if (this.checkbox.checked = cookie) this.toggleNewWindows();

		Event.observe(window,'unload',function(){ return me.cleanUp();},false);

		return this;
	} else return false; },
	cleanUp : function() {
		magicCookie.setCookie(this.resultsId,this.checkbox.checked);
		this.checkbox = null;
		return true;
	},
	toggleNewWindows : function() {
//		get the links
		var	links = document.getElementsByClassName(this.resultsClass,this.resultsId),
			me = this;
		var bhas_happened=false;
//		if the elements with the CLASS aren't A elements, find the first A child of those
//		elements instead
		links.each(function(element) {
			if (element.nodeName.toLowerCase() != 'a') element = element.getElementsByTagName('a')[0];
			var	title = getAttr(element,'title');
//			if the checkbox is checked...
			if (me.checkbox.checked) {
//				add a new window warning to the title atribute
				setAttr(element,'title',((typeof title == 'string')?title + ' ':'') + '(opens in a new window)');
				Event.observe(element,'click',function(e) { bhas_happened=true;me.inNewWindow(e,element); },false);
//				swallow the click in Safari...
				if (navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1) element.onclick = function() { return false; };
			} else {
				setAttr(element,'title',title.replace(/( )?\(opens in a new window\)/,''));
				Event.stopObserving(element,'click',me.inNewWindow,false);
				if (navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1) element.onclick = null;
			}
		});
		if(!bhas_happened){
				links.each(function(element) {
					if (element.nodeName.toLowerCase() != 'a') element = element.getElementsByTagName('a')[1];
					var	title = getAttr(element,'title');
		//			if the checkbox is checked...
					if (me.checkbox.checked) {
		//				add a new window warning to the title atribute
						setAttr(element,'title',((typeof title == 'string')?title + ' ':'') + '(opens in a new window)');
						Event.observe(element,'click',function(e) { me.inNewWindow(e,element); },false);
		//				swallow the click in Safari...
						if (navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1) element.onclick = function() { return false; };
					} else {
						setAttr(element,'title',title.replace(/( )?\(opens in a new window\)/,''));
						Event.stopObserving(element,'click',me.inNewWindow,false);
						if (navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1) element.onclick = null;
					}
			});
		}
		return true;
	},
	inNewWindow : function	(e,o) {
		if (e) Event.stop(e);
		var	win = window.open(getAttr(o,'href'));
		win.focus();
		return false;
	}
}
Event.observe(window,'load',function(){ new newWindowResults('searchPreferences','result','content');},false);
/*	COLLAPSING PANEL -----------------------------------------------
	script to create a collapsing panel; this is just a base object
	which more specialised collapser objects inherit; it's intended
	as an abstract class

	PARAMETERS

	- panel - HTML element; the element to be collapsed
	- actuator - HTML element; the element to be turned into an
	  expand/collapse button; can be an A or any element which may
	  contain an A and won't interfere with the CLICK event
	- isOpen - boolean; whether the panel should be open or
	  closed to start with

	REQUIRED JAVASCRIPT

	- prototype.js version  1.5.0_rc0 or higher (can be obtained
	  by downloading script.aculo.us at http://script.aculo.us)
	- a patch to prototype to fix the bug described at:
	  http://dev.rubyonrails.org/ticket/4642
	- scriptaculous.js version 1.6.1 or higher; also the effects.js
	  and builder.js libraries from scriptaculous, as well as
	  transitions.js which is a library of easing algorithms
	  derived from the ActionScript algorithms by Robert Penner
	- getAttr - a function to mask browser differences in getting
	  attribute values
	- setAttr - a function to mask browser differences in setting
	  attribute values
	- addStyleSheet - a function to add a stylesheet to a document
	- getHiddenDimensions - utility to get the dimensions of a
	  hidden block-level element without making it visible

	REQUIRED HTML
	
	- a block-level element to be collapsed
	- another element to serve as the 'button' which, when
	  clicked, will do the show/hide thing; if it's not an A,
	  the script will add an A around *all* children of the button
	  element
	
	note that neither of the HTML elements is found by this script;
	that's handled by clases which inherit this one
*/
var CollapsingPanel = Class.create();
CollapsingPanel.prototype = {
	initialize : function (panel, actuator, isOpen) {
		var	a,
			kids = [],
			me = this;

//		cache panel
		this.panel = panel;

//		add the anchor element to the actuator if it isn't itself an anchor & cache a reference to it
		if (actuator.nodeName.toLowerCase() != 'a') {
//			move each of the actuator's children to an array -- we need to use this half-arsed intermediate
//			array business to avoid a leak in IE, which happens when you start attaching DOM nodes to
//			a node which isn't (yet) attached to the document
			$A(actuator.childNodes).each(function (node) { return kids.push(actuator.removeChild(node)); });
//			create an anchor and add it to the actuator, then cache a reference to it; note that the URL
//			needs to use a 'return false' for Safari as that browser doesn't grok preventDefault or any 
//			other method of canceling anchor navigation wheny you use W3C methods for adding event handlers
			this.actuator = actuator.appendChild(Builder.node('a',{href:((navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1)?'javascript: return false;':'#')}));
//			put the kids back
			kids.each(function(node){ return actuator.firstChild.appendChild(node); });
			
		} else {
			if (navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple') != -1) setAttr(actuator, 'href', 'javascript: return false;');
			this.actuator = actuator;
		}

//		turn the actuator on -- the CSS file hides it to avoid a FOUC
		actuator.style.visibility = 'visible';

//		set up our click handler
		this.clickHandler = function(e){ return me.toggle(e);};

//		set the click handler on the actuator
		Event.observe(this.actuator, 'click', this.clickHandler, false);

//		if it's to be open to start, CSS should give it the proper styling, so just note that it's open
		this.isOpen = (isOpen)?true:false;
if (this.id=='retrievePassword'){
	this.isOpen=false;
}


//		set our cleanup method to run onunload
		Event.observe(window, 'unload', function(){return me.cleanUp();}, false);

//		return the created object
		return this;
	},
//	method to clean up any potential memory leakage
	cleanUp : function() {
//		remove the actuator & panel
		this.actuator = null;
		this.panel = null;
		return true;
	},
//	dummy method to swallow stray clicks while the animation is running
	doNothing : function(e) {
//		prevent event from propagating
		if (e) Event.stop(e);
		return false;
	},
	startShow : function() {
//		add the class to change the icon
		Element.addClassName(this.actuator,'expanded');
		return true;
	},
	endShow : function() {
//		the class just sets height to 'expanded' in this application, but it could do anything whatever
		Element.addClassName(this.panel,'expanded');
//		start listening to clicks again
		Event.stopObserving(this.actuator, 'click', this.doNothing, false);
		Event.observe(this.actuator, 'click', this.clickHandler, false);
		return true;
	},
	startHide : function() {
//		remove the class to change the icon
		Element.removeClassName(this.actuator,'expanded');
		return true;
	},
	endHide : function() {
		Element.removeClassName(this.panel,'expanded');
//		start listening to clicks again
		Event.stopObserving(this.actuator, 'click', this.doNothing, false);
		Event.observe(this.actuator, 'click', this.clickHandler, false);
		return true;
	},
	toggle : function(e) {
		var d,
			me = this;
//		prevent event from propagating
		if (e) Event.stop(e);

//		temporarily stop listening to clicks
		Event.stopObserving(this.actuator, 'click', this.clickHandler, false);
		Event.observe(this.actuator, 'click', this.doNothing, false);

		if (this.isOpen) {
			Effect.BlindUp(this.panel,{duration:.3,fps:40,transition:Effect.Transitions.easeInBack,beforeStart:function(){ return me.startHide(); },afterFinish:function(){ return me.endHide();}});
		} else {
			d = getHiddenDimensions(this.panel);
			Effect.BlindDown(this.panel,{duration:.3,fps:40,transition:Effect.Transitions.easeInQuadratic,scaleMode:{originalHeight:d.height,originalWidth:d.width},beforeStart:function(){return me.startShow();},afterFinish:function(){return me.endShow();}});
		}
		this.isOpen = !this.isOpen;
	
		return false;
	}
}


/*	EXPANSION PANEL ------------------------------------------------
	script that expands/collapses a single element; the key feature
	is that it adds 'Show' or 'Hide' before the first text node in
	the 'button' element depending on the panel's state

	PARAMETERS
	- panel = HTML element; block-level element to collapse
	- isOpen = boolean; indicates whether the panel should be
	  expanded or collapsed by default

	REQUIRED JAVASCRIPT

	- CollapsingPanel - object from which this inherits; provides
	  most of the basic functionality save a bit of setup and the
	  'Show'/'Hide' text munging

	REQUIRED CSS

	- expansion_panel.css - a stylesheet to set up the appearance
	  of the panel + button as well as show/hide the panel before
	  page load; the script expects it to live in a root-level
	  directory called 'css'; otherwise, the path needs to be
	  changed in the addStyleSheet call at the very end of this
	  script

	REQUIRED HTML
	
	- a block-level element to be expanded/collapsed; needs a
	  class of 'expansionPanel' and an ID of form [slug]Panel
	  where [slug] is a slug which is also used in the actuator
	  element's ID (see next item)
	- an element to serve as a button; needs an ID of the form
	  [slug]Actuator (see previous item)
*/
var ExpansionPanel = Class.create();
Object.extend(Object.extend(ExpansionPanel.prototype, CollapsingPanel.prototype), {
	_parent : CollapsingPanel.prototype,
	initialize : function (panel, isOpen) {
		this.id = getAttr(panel,'id');

//		get the actuator by munging the ID of the panel
		var	actuator = $(this.id.replace(/panel/i,'') + 'Actuator'),
//				lets us know whether to open up by default or not
				cookie = eval(magicCookie.getCookie(this.id));

//		return if we haven't found our actuator
		if (!actuator) return false;

//		find the first text child of the actuator and add 'Show' or 'Hide' to it
		Element.cleanWhitespace(actuator);
		this.label = actuator;
		do {
			this.label = this.label.firstChild;
		} while (3 != this.label.nodeType);
		if (this.label.nodeValue.toUpperCase() != 'FORGOT PASSWORD?')
		  {
			this.label.nodeValue = ((isOpen)?'Hide ':'Show ') + this.label.nodeValue;
		  }

//		initialise the parent class
		this._parent.initialize.call(this, panel, actuator, isOpen);
		
		if (eval(cookie)) {
			Event.stopObserving(this.actuator, 'click', this.clickHandler, false);
			Event.observe(this.actuator, 'click', this.doNothing, false);
			this.startShow();
			this.endShow();
			this.isOpen = true;
		}

//		return the created object
		return this;
	},
//	method to clean up any potential memory leakage
	cleanUp : function() {
		magicCookie.setCookie(this.id,this.isOpen);
//		remove the actuator & panel
		this._parent.cleanUp.call(this);
		this.label = null;
		return true;
	},
	startShow : function() {
//		change the label of the actuator
		this.label.nodeValue = this.label.nodeValue.replace(/^Show/,'Hide');
//		call the parent's startShow method to change the class
		this._parent.startShow.call(this);
	},
	startHide : function() {
//		change the label of the actuator
		this.label.nodeValue = this.label.nodeValue.replace(/^Hide/,'Show');
//		call the parent's startHide method to change the class
		this._parent.startHide.call(this);
	}
});

function expansionPanelsSetUp(panel) {
	return $A(document.getElementsByClassName('expansionPanel')).each( function(panel) { return new ExpansionPanel(panel, true); });
}

if (addStyleSheet('/css/expansion_panel.css')) Event.observe(window, 'load', expansionPanelsSetUp, false);


/*	ACCORDION PANEL ------------------------------------------------
	script that expands/collapses a single element, but set up to
	function within an accordion widget (where expanding one item
	collapses the one which had been open)

	PARAMETERS

	- panel = HTML element; block-level element to collapse
	- accordion = object; the accordion object which controls the
	  accordion widget of which this panel is a part

	REQUIRED JAVASCRIPT

	- CollapsingPanel - object from which this inherits; provides
	  most of the basic functionality save a bit of setup and the
	  'Show'/'Hide' text munging
	- Accordion - the controller which knits multiple panels
	  together into a complete widget
*/
var AccordionPanel = Class.create();
Object.extend(Object.extend(AccordionPanel.prototype, CollapsingPanel.prototype), {
	_parent : CollapsingPanel.prototype,
	initialize : function (panel, accordion) {
		var	actuator;
			isOpen = (getAttr(panel.parentNode,'class').match(/\bcurrent\b/))?true:false;

//		clean whitespace from the panel's parent
		Element.cleanWhitespace(panel.parentNode);
//		get the actuator
		actuator = panel.previousSibling;

//		initialise the parent class
		this._parent.initialize.call(this, panel, actuator, isOpen);

		this.accordion = accordion;

//		return the created object
		return this;
	},
	startShow : function() {
		this.accordion.currentPanel.toggle();
		this.accordion.currentPanel = this;
		Element.addClassName(this.panel.parentNode, 'current');
//		call the parent's startShow method to change the class
		this._parent.startShow.call(this);
	},
	endHide : function() {
		Element.removeClassName(this.panel.parentNode, 'current');
//		call the parent's startHide method to change the class
		this._parent.endHide.call(this);
	},
	toggle : function(e) {
		var d,me = this;
//		prevent event from propagating
		if (e) Event.stop(e);
//		just return if we're the current panel
		if (e && this == this.accordion.currentPanel) return false;

//		temporarily stop listening to clicks
		Event.stopObserving(this.actuator, 'click', this.clickHandler, false);
		Event.observe(this.actuator, 'click', this.doNothing, false);
		if (this.isOpen) {
			Effect.BlindUp(this.panel,{duration:.2,fps:40,beforeStart:function(){ return me.startHide(); },afterFinish:function(){ return me.endHide();}});
		} else {
			d = getHiddenDimensions(this.panel);
			Effect.BlindDown(this.panel,{duration:.2,fps:40,scaleMode:{originalHeight:d.height,originalWidth:d.width},beforeStart:function(){return me.startShow();},afterFinish:function(){return me.endShow();}});
		}
		this.isOpen = !this.isOpen;
	
		return false;
	}
});


/*	ACCORDION ------------------------------------------------------
	object which creates an accordion widget (click an item and it
	expands while the currently open one collapses)

	PARAMETERS

	- accordion - parent element which contains the panels &
	  actuators

	REQUIRED SCRIPTS

	- AccordionPanel - object instantiated to create the panels

	REQUIRED CSS

	- rules to 'collapse' any panels which do not have a parent with
	  a class of 'current'

	REQUIRED HTML

	- parent element which contains all panel/actuator
	  pairs; needs a class of 'accordion'
	- each panel/actuator pair (see CollapsingPanel in the
	  AccordionPanel requirements) needs to be contained in their
	  own parent, and one parent needs a class of 'current' to
	  serve as the panel expanded by default
	- each panel needs a class of 'accordionPanel'
	- the actuator must be the element immediately preceeding
	  the panel
*/
var Accordion = Class.create();
Accordion.prototype = {
	_parent : CollapsingPanel.prototype,
	initialize : function (accordion) {
		var	panels = $A(document.getElementsByClassName('accordionPanel',accordion)),
			me = this;

		this.element = accordion;
		this.panels = panels.collect( function(panel) { return new AccordionPanel(panel, me); });
		this.currentPanel = $A(this.panels).detect(function(panel) { return panel.isOpen; });
	}
}

function accordionsSetUp() {
	return $A(document.getElementsByClassName('accordion')).each(function(accordion) { return new Accordion(accordion); });
}

Event.observe(window, 'load', accordionsSetUp, false);


/*	GET ATTR -------------------------------------------------------
	gets an attribute value; works around the IE class/className,
	for/htmlfor & camel-case debacles

	PARAMETERS
	- o = object; HTML element on which the attribute resides
	- a = string; attribute name

	REQUIRED SCRIPTS
	- translateAttrName; utility to translate from standard to
	  MS-specific attribute names

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function getAttr(o,a) {
	var attr;
//	if the.getAttributeNode method exists, use it
	if (o.getAttributeNode) return (attr = o.getAttributeNode(a))?attr.nodeValue:'';
//	otherwise, try getAttribute with translation
	else if (o.getAttribute) return o.getAttribute(translateAttrName(a));
//	if all else fails, try just accessing it as a property
	else return o[a];
}

/*	SET ATTR -------------------------------------------------------
	sets an attribute value; works around the IE class/className,
	for/htmlfor & camel-case debacles

	PARAMETERS
	- o = object; HTML element on which the attribute resides
	- a = string; attribute name
	- v = string; attribute value

	REQUIRED SCRIPTS
	- translateAttrName; utility to translate from standard to
	  MS-specific attribute names

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function setAttr(o,a,v) {
	var attr;
//	if the getAttributeNode method exists, use it
	if (o.getAttributeNode) {
//		if the attribute already exists, we can just set the nodeValue and have done
		if (attr = o.getAttributeNode(a)) return attr.nodeValue = v;
//		otherwise...
		else { 
//			create a new attribute
			attr = document.createAttribute(a);
//			set it's nodeValue
			attr.nodeValue = v;
//			and finally add it to the element
			return o.setAttributeNode(attr);
		}
//	if there's no getAttribute node, try using setAttribute with IE translation
	} else if (o.setAttribute) return o.setAttribute(translateAttrName(a),v);
//	and if all else fails, just set it as a property
	else return o[a] = v;
}

/*	TRANSLATE ATTR NAME --------------------------------------------
	translatest from standard to IE-specific attribute names 

	PARAMETERS
	- n = string; attribute name

	LIMITATIONS
	- may not have exhaustive list of case-sensitive attributes
*/
function translateAttrName(n) {
	switch(n) {
		case 'class'		: return 'className';
		case 'for'			: return 'htmlFor';
		case 'accesskey'	: return 'accessKey';
		case 'colspan'		: return 'colSpan';
		case 'rowSpan'		: return 'rowSpan';
		case 'maxlength'	: return 'maxLength';
		case 'usemap'		: return 'useMap';
		default			: return n;
	};
};

/*	GET HIDDEN DIMENSIONS ---------------------------------------
	utility to get the dimensions of a hidden block-level element
	without making it visible

	PARAMETERS
	- o = object (node); element whose dimensions are desired
*/
function getHiddenDimensions(o) {
	var	dimensions = {},
		position = o.style.position,
		display = o.style.display,
		visibility = o.style.visibility,
		height = o.style.height,
		width = o.style.width;
//	hide the element and set it's position to 'absolute' to get it out of the flow
	o.style.position = 'absolute';
	o.style.visibility = 'hidden';
//	make sure the element's display isn't 'none' or we'll get squat for dimensions
	o.style.display = 'block';
//	set it's height & width to 'auto' so we can get natural dimensions
	o.style.height = 'auto';
	o.style.width = 'auto';
//	grab it's dimensions for the 'BlindDown' effect
	dimensions['height'] = o.offsetHeight;
	dimensions['width'] = o.offsetWidth;
//	reset the properties we messed with
	o.style.display = display;
	o.style.position = position;
	o.style.visibility = visibility;
	o.style.height = height;
	o.style.width = width;

	return dimensions;
}

/*	ADD STYLESHEET -------------------------------------------------
	adds a stylesheet prior to document load
	must be placed after any stylesheets it is to override
	
	PARAMETERS
	- path = string; path to CSS file, including filename
*/
function addStyleSheet(path) {
	var headElement,linkElement;
//		test that we have the necessary support by getting a
//		reference to the HEAD element, creating a new LINK element
//		and setting the REL attributes for a CSS stylesheet
	if (	(headElement  = document.getElementsByTagName('head')[0]) &&
			(linkElement = document.createElement('link')) &&
			(linkElement.setAttribute)) {

//		set TYPE & REL attributes for a CSS stylesheet
		linkElement.setAttribute('rel','stylesheet')
		linkElement.setAttribute('type','text/css');
//		set the HREF to the supplied cssPath + the stylesheet filename 'ticker.css'
		linkElement.setAttribute('href',path);
//		append the link to the head element, thereby adding the stylesheet to the document
		headElement.appendChild(linkElement);
//		note that the method above, while kosher in XHTML with an application/xml+xhtml MIME type,
//		does NOT work in IE5/Mac so the script gracefully degrades in that browser; if compatibility
//		with IE5/Mac is needed comment out all lines from getting the reference to the head down to
//		this commment and uncomment the line immediately following this one; note, though, that
//		document.write is verboten in XHTML sent as application/xml+xhtml
//		document.write('<link rel="stylesheet" type="text/css" href="' + path + '">');
		return true;
	}
	return false;
}

/*	MAGIC COOKIES --------------------------------------------------
	utility cookie handling object loosely based on David 
	Flannagan's cookie functions in /Javascript: The Definitive
	Guide/ published by O'Reilly
*/
magicCookie = {
//	get the domain so we can use that as a default when setting cookies
	domain : 'domain=' + document.domain + ';',
//	set a default path
	path : 'path=/;',
	expire : function() {
//		create a new date to be the default expiration date
		var expiration = new Date();
//		set the default expiration date to 1 year from now
		expiration.setFullYear(expiration.getFullYear() + 1);
		return 'expires=' + expiration.toGMTString() + ';';
	}(),
	cookies : function() {
//		get the document cookie
		var	allCookies = document.cookie.split(';'),
			numCookies = allCookies.length,
			cookieParts,
			cookies = {};
//		loop through cookies and separate them into name+value pairs housed in this.cookies object
		for (var i = 0; i < numCookies; i++) {
			cookieParts = allCookies[i].split('=');
			cookies[cookieParts[0].replace(/ /,'')] = unescape(cookieParts[1]);
		}
		return cookies;
	}(),
//	get the value of the cookie with the name equal to a string passed as an argument
	getCookie : function(cookieName) {
		if (typeof this.cookies[cookieName] != 'undefined') return this.cookies[cookieName];
		else return false;
	},
//	set a cookie with a supplied name and value
	setCookie : function(cookieName,cookieValue) {
		this.cookies[cookieName] = escape(cookieValue);
//		set a cookie with the name and value passed as arguments and the calculated domain and expiration dates
		document.cookie =  cookieName + '=' + cookieValue + ';' + this.expire + this.path + this.domain;
		return true;
	}
}

/*	GET TEXT -------------------------------------------------------
	utility to assemble and return all the text inside an element,
	stripping stripping away any child elements (but keeping their
	text content) along the way

	PARAMETERS
	
	- o = node object; node from which to extract the text
*/
function getText(o) {
	var	kids = o.childNodes,
		numKids = kids.length,
		i,
		text = '',
		currKid;

//	loop through the child nodes
	for (i = 0; i < numKids; i++) {
		currKid = kids[i];
//		if the current node is a text node, append it's value to the holder variable
		if (currKid.nodeType == 3) text += currKid.nodeValue;
//		if the current node is an element, recurse to get it's text
		else if (currKid.nodeType == 1) text += getText(currKid);
	}
	return text;
}

// PROTOTYPE PATCHES

//	Fixes bug described at: http://dev.rubyonrails.org/ticket/4642
Element.extend = function(element) {
  if (!element) return;
  if (_nativeExtensions) return element;
  if (element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
    var methods = Element.Methods, cache = Element.extend.cache;
    for (property in methods) {
      var value = methods[property];
      if (typeof value == 'function')
      element[property] = cache.findOrStore(value);
    }
  }

  element._extended = true;

  return element;
}

Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
}
