/*********************************************\
  Extensions for standard Prototype
\*********************************************/

if ( window.location.protocol == "https:" ) {
	Prototype.blankSrc = "/*****";
} else {
	Prototype.blankSrc = "javascript:void(0)";
}


//////////////////
// Include the ability to react to the loading of the actual DOM tree, BEFORE the loading
// of all embedded content (eg. images, objects, etc.)
// Assembled from a non-Prototype solution first built by Dean Edwards, Matthias Miller, and John Resig
// originally found at http://dean.edwards.name/weblog/2006/06/again/?full#comments
Object.extend( Event, {
	DOMReadyObserved: false,
	DOMReadyObservers: false,
	DOMReadyBrowserDevice: false,
	
	observeDOMReady: function ( observer, priority ) {
		// If the DOM is *already* ready, run the observer immediately.
		if (Event.DOMReadyObserved) {
			observer();
		} else {
	    	if ( ! Event.DOMReadyObservers ) { Event.DOMReadyObservers = new Array(); };
			if (priority) {
				Event.DOMReadyObservers.unshift( observer );
			} else {
				Event.DOMReadyObservers.push( observer );
			}
		};
	},
	
	runDOMReady: function () {
		if ( Event.runDOMReady.running ) { // only one at a time
			window.setTimeout(Event.runDOMReady, 40);
			return;
		}
		Event.runDOMReady.running = true;
		if ( ! Event.DOMReadyObservers ) { Event.DOMReadyObservers = new Array(); };
		for(var i=0; i<Event.DOMReadyObservers.length; i++) {
			Event.DOMReadyObservers[i]();
		}
		
		// Only run these ONCE.
		Event.DOMReadyObservers.clear();
		Event.runDOMReady.running = false;
	}
});

do {
	/* All browsers - also takes care of this happening too fast */
	Event.observe(window, 'load', Event.runDOMReady );

	/* Internet Explorer */
	/*@cc_on @*/
	/*@if (@_win32)
		document.write("<sc" + "ript id='__ie_observeDOMReady' defer='defer' src='" + Prototype.blankSrc + "'><\/sc" + "ript>");
		Event.DOMReadyBrowserDevice = document.getElementById("__ie_observeDOMReady");
		Event.DOMReadyBrowserDevice.onreadystatechange = function() {
			if (this.readyState == "complete") {
				Event.runDOMReady(); // call the onload handler
			}
		};
		break;
	/*@end @*/
	
	/* Safari/Konqueror */
	/* WebKit must be tested for before DOM2 standard because WebKit supports document.addEventListener but NOT the 
	   DOMContentLoaded event. */
	if (/WebKit/i.test(navigator.userAgent)) { 
		Event.DOMReadyBrowserDevice = setInterval(function() {
			if (/loaded|complete/.test(document.readyState)) {
				clearInterval(Event.DOMReadyBrowserDevice);
				Event.runDOMReady(); 
			}
		}, 10);
		break;
	}
	
	/* Mozilla/Opera9/DOM2 Compliant browsers */
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", Event.runDOMReady, false);
		break;
	}

} while(0);

//////// End of DomContentLoaded observer

// Inserting support for 'mousewheel' event.
// Should now be possible to perform a normal "observe" with a name value of 'mousewheel'
Event.observe = function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

	if ( (name == 'mousewheel') && element.addEventListener && (!window.opera)) {
		name = 'DOMMouseScroll';
	} else if ( (name == 'DOMMouseScroll') &&  element.attachEvent) {
		name = 'mousewheel';
	}

    Event._observeAndCache(element, name, observer, useCapture);
}

// Crossbrowserizes the mousewheel event: passing a mousewheel event to this
// returns -1 for scroll up, 1 for scroll down, and 0 for none or unknown.
Event.mouseScrollDirection = function(event) {
	var r = 0;
	if (event.wheelDelta) {
		r = event.wheelDelta * ( window.opera ? 1 : -1 );
	} else {
		r = event.detail || 0;
	}
	return ( (r<0) ? (-1) : ( (r>0)? 1: 0 ) );
}


Object.debugdump = function ( obj, depth, maxprops, ind ) {
	ind = ind || "";
	if (depth == null) { depth = 1; }
	if (maxprops == null) { maxprops = 0; }
	var childmaxprops = Math.abs(maxprops);
	if (obj == null) { return '<null>'; };
	if (typeof(obj) == 'undefined' ) { return '<undefined>'; }; 
	if (typeof(obj) == 'function' ) { return '<function>'; }; 
	if (typeof(obj) == 'string' ) { return '"' + obj + '"'; }; 
	if ( !(isNaN(parseFloat(obj))) ) { return '' + obj; }
	else {
		if (depth < 1) {
			return "<object>";
		} else {
			res = "{\n";
			var pc = 1;
			for( k in obj ) {
				if ((maxprops > 0) && (pc > maxprops) ) { res += ind + "   <other props...>\n"; break; }
				res += ind + "   " + k + ": " + Object.debugdump( obj[k], depth-1, childmaxprops , ind + "   ") + ",\n";
				pc++;
			}
			res += ind + "}";
		}
	};
	return res;
}

Form.Element.decimalpoint = (1.11).toLocaleString().charAt(1);

// Upgrades for the Form.Element object methods
Object.extend( Form.Element.Methods, {

	addSelectOptions: function(element, selectoptionshash, options) {
		element = $(element);
		if (element.nodeName.toUpperCase() != "SELECT") {
			// Can't do this except to SELECTs.
			return element;
		}
		
		options = Object.extend({ selectedvalue:null }, options);
		selectoptionshash = $H(selectoptionshash);
		var selectedvalue = options.selectedvalue;
		
		var option;
		selectoptionshash.each( function( pair ) {
			pair.value = pair.value.toString();
			pair.key = pair.key.toString();
			if ( pair.value.length < 1 ) { pair.value = pair.key; }
			option = document.createElement('option');
			option.innerHTML = pair.value;
			option.value = pair.key;
			option.selected = (selectedvalue === pair.key);
			element.appendChild( option );
		});
		
		return element;
	},
	
	loadSelectOptions: function( element, url, options ) {
		element = $(element);
		options = Object.extend({ clear:false, selectedvalue:null, prependoptions:null, appendoptions:null }, options);
		if (element.nodeName.toUpperCase() != "SELECT") {
			// Can't do this except to SELECTs.
			return element;
		}
		if ( options.clear ) {
			element.truncate();
		}

		var ajax = new Ajax.Request(url, {
			method: 'get',
			onSuccess: function(transport) {
				var selectoptionshash = null;
				try {
					eval( "selectoptionshash = " + transport.responseText);
				} catch (e) {
					alert( "Could not read the select list: " + e.message );
				}
				if ( options.prependoptions ) { element.addSelectOptions( options.prependoptions, { selectedvalue: options.selectedvalue} ) };
				element.addSelectOptions( selectoptionshash, { selectedvalue: options.selectedvalue} );
				if ( options.appendoptions ) { element.addSelectOptions( options.appendoptions, { selectedvalue: options.selectedvalue} ) };
			},
			onFailure: function(transport) {
				alert("Could not load the select list - the response was " + transport.status + " " + transport.statusText );
			}
		});	
	},
	
	getValueFixed: function (element, decimalplaces) {
		decimalplaces = (decimalplaces==null) ? 2 : decimalplaces;
		return parseFloat( parseFloat( '0' + element.value.replace(/[^0-9.]/g,'')).toFixed(decimalplaces) );
	},

	getValuePercent: function (element, smallestpercent, decimalplaces) {
		smallestpercent = smallestpercent || 0;
		decimalplaces = (decimalplaces==null) ? 2 : decimalplaces;
		var p = parseFloat( '0' + element.value.replace(/[^0-9.]/g,''));
		if (Math.abs(p) < smallestpercent) { p *= 100.0; }
		return parseFloat( p.toFixed(decimalplaces) );
	},
	
	formatValueAsMoney: function (element, currencysymbol) {
		currencysymbol = (currencysymbol || "$").toString();
		element.value = currencysymbol + (element.getValueFixed(2) ).toLocaleString();
		
		var decimalat = element.value.indexOf(Form.Element.decimalpoint);
		if ( decimalat < 0 ) {
			element.value += Form.Element.decimalpoint + "00";
		} else if (decimalat > (element.value.length-3) ) {
			element.value += "0"
		}
	},
	
	formatValueAsPercent: function (element, smallestpercent, decimalplaces) {
		element.value = element.getValuePercent(smallestpercent, decimalplaces).toFixed(decimalplaces) + " %";
	}

});

// Upgrades for the Element global object itself - static methods and properties
Object.extend( Element, {
});



// Upgrades for the standard Element object
Element.addMethods({
	   
	// <domelement>.eachTag( string <tagname>, function <iteratorfunction> )
	// An iterator that applies iteratorfunction to each of the tags in
	// domelement with the given tagname. If tagname is "*", operates on all tags.
	eachTag: function(element, tag, iterator) {
		element = $(element);
		element.cleanWhitespace();
		$A(element.getElementsByTagName(tag)).each(iterator);
		return element;
	},

	// <domelement>.firstTag( string <tagname> )
	// Returns the first tag of given type found in <domelement>, or null
	// if not found.
	firstTag: function(element, tag) {
		element = $(element);
		element.cleanWhitespace();
		var taglist = $A(element.getElementsByTagName(tag));
		if ( taglist.length == 0 ) {
			return null;
		} else { 
			return taglist[0];
		};
	},

	// <domelement>.firstTagWithClass( string <tagname>, string <classname> )
	// Returns the first tag of given type found in <domelement> which has the
	// given class name, or null if not found.
	firstTagWithClass: function( element, tag, classname ) {
		element = $(element);
		element.cleanWhitespace();
		var taglist = $A(element.getElementsByTagName(tag));
		
		for (var i=0; i<taglist.length; i++ ) {
			if ( Element.classNames(taglist[i]).include(classname) ) {
				return taglist[i];
			};
		};
		
		return null;
	},

	// <domelement>.absoluteLeft()
	// Returns the absolute left position of the element relative to the document
	// at large (in pixels), regardless of how the element is positioned.
	absoluteLeft: function( element ) {
		return Position.cumulativeOffset($(element))[0];
	},
 
	// <domelement>.absoluteTop()
	// Returns the absolute top position of the element relative to the document
	// at large (in pixels), regardless of how the element is positioned.
 	absoluteTop: function( element ) {
		return Position.cumulativeOffset($(element))[1];
	},

	// <domelement>.absolutePosition()
	// Returns an object with the absolute top and left position of the element relative
	// to the document at large (in pixels), regardless of how the element is positioned.
	// Return takes the form { left:<leftvalue>, top:<topvalue> }
 	absolutePosition: function( element ) {
		var pos = Position.cumulativeOffset($(element));
		return { left: pos[0], top: pos[1] };
	},

	// <domelement>.moveToElement( <targetelement>, <offsetx>, <offsety> )
	// Marks the <domelement> as positioned absolutely and moves it to the absolute
	// location of <targetelement>.
	// Options include:
	// offsetx (default 0), horizontal offset from initial position in pixels
	// offsety (default 0), horizontal offset from initial position in pixels
	// halign  (default "left"), horizontal alignment of the element relative to the target element, "left"/"center"/"right"/"before"/"after"
	// valign  (default "top"), vertical alignment of the element relative to the target element, "top"/"center"/"bottom"/"above"/"below"
	moveToElement: function( element, targetelement, optionshash ) {
		optionshash = $H( { offsetx:0, offsety:0, halign:'left', valign:'top' } ).merge( optionshash );
		element = $( element );
		targetelement = $( targetelement );
			
		var leftcalc = parseInt(optionshash['offsetx']);
		var topcalc = parseInt(optionshash['offsety']);
		
		switch( optionshash['halign'] ) {
			case 'middle':
			case 'center':
				leftcalc = leftcalc + (targetelement.getWidth()/2) - (element.getWidth() / 2);
				break;
			case 'right':
				leftcalc = leftcalc + targetelement.getWidth() - element.getWidth();
				break;
			case 'before':
				leftcalc = leftcalc - element.getWidth();
				break;
			case 'after':
				leftcalc = leftcalc + targetelement.getWidth();
				break;
		}

		switch( optionshash['valign'] ) {
			case 'middle':
			case 'center':
				topcalc = topcalc + (targetelement.getHeight()/2) - (element.getHeight() / 2);
				break;
			case 'bottom':
				topcalc = topcalc + targetelement.getHeight() - element.getHeight();
				break;
			case 'above':
				topcalc = topcalc  - element.getHeight();
				break;
			case 'below':
				topcalc = topcalc + targetelement.getHeight();
				break;
		}

		Position.clone( targetelement, element, { setLeft:true, setTop:true, setWidth:false, setHeight:false, offsetLeft: leftcalc, offsetTop: topcalc } );
		return element;
	},

	// <domelement>.appendContent( <html> )
	// Inserts the given HTML appended to the content of <domelement> and return it
	appendContent: function( element, html ) {
		element = $(element);
		element.innerHTML = element.innerHTML + html.toString();
		return element;
	},

	// <domelement>.truncate()
	// Delete all of the element's content and return it.
	truncate: function( element ) {
		element = $(element);
		var children = $A(element.childNodes).clone();
		for( var i=0; i< children.length; i++ ) { element.removeChild(children[i]) }
		return element;
	},
	
	setOpacity: function( element, opacity ) {
		if (opacity < 0.0) {opacity = 0.0;}
		if (opacity > 1.0) {opacity = 1.0;}
		
		element.style.opacity = opacity;
		element.style.filter = 'alpha(opacity=' + (opacity*100) + ')';
		element.style.zoom = element.style.zoom || '1';
		element.style.MozOpacity = opacity;
		element.KhtmlOpacity = opacity;
	}
});


// New feature for Ajax.Updater - if the option "stripoutertags" is true, then
// the response text will be searched for enclosing <body> tags, and 
// they and anything outside them will be removed. Allows compliant
// HTML pages to be embedded in compliant HTML pages without breaking the 
// many compliance rules about duplicate heads and bodies and illegal tags.
// This pretty much requires remaking the Updater. Includes an AJAX utility
// function to perform the string manipulation on the response itself.

Object.extend(Ajax, {
  stripOuterTags: function( response ) {
	var bodycontentbegin = response.indexOf(">", response.search( /[.\n]*<body[^>]*>/im )) + 1 ;
	var bodycontentlength = response.search( /[.]*<\/body[^>]*>/im ) - bodycontentbegin;
	return response.substr( bodycontentbegin, bodycontentlength );
  }	
});

Object.extend(Ajax.Updater.prototype, {
  initialize: function(container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);
	this.responseText = null;

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, param) {
	  if ( this.options.stripoutertags ) { this.stripOuterTags(); };
      this.updateContent();
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },
  
  stripOuterTags: function( ) {
	this.responseText = Ajax.stripOuterTags( this.responseText || this.transport.responseText );
  },

  updateContent: function() {
    var receiver = this.container[this.success() ? 'success' : 'failure'];
    var response = this.responseText || this.transport.responseText;

    if (!this.options.evalScripts) response = response.stripScripts();

    if (receiver = $(receiver)) {
      if (this.options.insertion)
        new this.options.insertion(receiver, response);
      else
        receiver.update(response);
    }

    if (this.success()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
  
});

// Expand Position.Clone - adding the height and width offsets
Position.clone = function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0,
      offsetWidth:  0,
      offsetHeight: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = (0 + source.offsetWidth + options.offsetWidth) + 'px';
    if(options.setHeight) target.style.height = (0 + source.offsetHeight + options.offsetHeight) + 'px';
  };
 
/*******************************************************************\
   Completely new object for creating and operating on Stylesheets
\*******************************************************************/

var StyleSheets = Class.create();


Object.extend( StyleSheets, {
	// Static methods/properties
	extend: function(ss) {
		if ( ss._extended ) { return ss; };
		
		if ( ss.rules ) {
			Object.extend( ss, StyleSheets.IE );
		} else {
			Object.extend( ss, StyleSheets.W3C );
		};
		Object.extend( ss, StyleSheets.Base );
		
		ss._extended = true;
		return ss;
	}
	
});


StyleSheets.Base = {
	// Instance methods/properties
	addCSS: function( CSS ) {
		if (typeof CSS != 'string') { return; }
		
		var stanzalist = CSS.split("}");
		var sheet = this;
		stanzalist.each( function(entry) {
			var entrysplit = entry.replace(/\n/g, " ").split("{");
			var rule = entrysplit[1];
			var selectors = entrysplit[0].split(",");
			for( var i=0; i<selectors.length; i++ ) {
				sheet.addRule( selectors[i], rule );
			}
		});
	}
};

StyleSheets.IE = {
	// Instance methods/properties
};

StyleSheets.W3C = {
	// Instance methods/properties
	addRule: function( selector, rule ) {
		this.insertRule( selector + " { " + rule + " }" );
	}
};

/******************************************************************\
   Completely new object for automagically rewriting DOM segments
\******************************************************************/

var Rewriter = Class.create();

Rewriter.prototype = {
	// Instance methods/properties
	initialize: function ( tagname, classname, iterator ) {
		if ( classname ) {
			if ( classname.any ) {
				var classselector = function(e) { return (e.className)?( classname.any( function(cn) { if(!cn){ return false; }; var EcN = Element.classNames(e); return EcN ? EcN.include( cn ) : false; } ) ) : false; };
			} else {
				var classselector = function(e) { return (Element.classNames(e).include( classname )); };
			}
		} else {
			var classselector = function (e) { return true; };
		}

		if ( tagname ) {
			var elementselector = function() { return $A(document.body.getElementsByTagName(tagname)); };
		} else {
			var elementselector = function() { return $A(document.body.getElementsByTagName('*')); };
		}
		
		Event.observeDOMReady( function() { 
			elementselector().findAll( classselector ).each( iterator );
		} );
	}
};	


/*******************************************************\
   Completely new object for interacting with cookies!
\*******************************************************/

var Cookies = Class.create();

Object.extend( Cookies, {
	// Static methods/properties

	_readcookies: function () {
		var cookiestring = "" + document.cookie;
		
		Cookies._collection = {};
		
		cookiestring.split("; ").each( function( singlecookiestr ) {
			var singlearray = singlecookiestr.split("=");
			Cookies._collection[ singlearray[0] ] = singlearray[1];
		});
	},
	
	get: function( key ) {
		return Cookies._collection[ key ];
	},

	count: function( ) {
		var c = 0;
		for( var key in Cookies._collection ) {
			c++;
		}
		return c;
	},

	debugdump: function( ) {
		return Object.debugdump(Cookies._collection);
	},

	set: function( key, value, days, hours, minutes, path ) {
		key = "" + key;
		if (key == "") { return false; };
		
		value = "" + value;
		
		var expirystring = "";
		
		if ( days || hours || minutes ) {
			var expiry = new Date();
			
			expiry.setTime( expiry.getTime() + ((0+days)*24*60*60*1000) + ((0+hours)*60*60*1000) + ((0+minutes)*60*1000) );
			expirystring = "; expires="+expiry.toGMTString();
		};
		
		var pathstring = "; path=";
		if ( path ) {
			pathstring += "" + path;
		} else {
			pathstring += "/";
		};
		
		document.cookie = "" + key + "=" + value + expirystring + pathstring;
		Cookies._readcookies();
		return true;
	},
	
	remove: function(key) {
		document.cookie = "" + key + "=; expires=" + Cookies._removedatestring + "; path=/";
		Cookies._readcookies();
	},

	clear: function() {
		for( var key in Cookies._collection ) {
			document.cookie = "" + key + "=; expires=" + Cookies._removedatestring + "; path=/";
		};
		Cookies._readcookies();
	}

});

Cookies._removedatestring = ( new Date(0) ).toGMTString();
Cookies._readcookies();


