/* Handy Objects
--------------------------------------------------------------------------*/
var Cookie = {
get: function(name) {
var cookies = document.cookie.split('; ');
for (var x=0; x<cookies.length; x++) {
var cookie = cookies[x].split('=');
if (cookie[0] == name) return cookie[1];
}
return false;
},

set: function(name, value, expDate) {
document.cookie = name+"="+escape(value) + ";expires="+expDate.toGMTString();
},

remove: function(name) {
Cookie.set(name, '', new Date(Date.parse('Thu, 01-Jan-1970 00:00:01 GMT')));
}
};

var Request = {
getVar: function(name) {
var getvars = document.location.search.substring(1).split('&');
for (var x=0; x<getvars.length; x++) {
var getvar = getvars[x].split('=');
if (getvar[0] == name) return getvar[1];
}
return false;
}
};

// Legacy function names
var getCookie = Cookie.get;
var getGet = Request.getVar;

/* Extensions to native JS objects
--------------------------------------------------------------------------*/
Object.extend(String.prototype, {
/**
 * Extends the .substring() method to allow negative numbers to
 * reference indices from the end of the string rather than the beginning.
 */
substring: function(start, end) {
if (start < 0) start = this.length + start;
if (end < 0) end = this.length + end;
if (end !== 0 && !end) end = this.length;
var newString = '';
for (var ssi=start; ssi<end; ssi++) {
newString += this.charAt(ssi);
}
return newString;
},

// Deprecated
// See Prototype's String.escapeHTML()
// http://www.prototypejs.org/api/string/escapeHTML
/**
 * Converts special characters to their HTML entities
 */
 htmlEntities: function() {
var chars = {
'&': 'amp', '<': 'lt', '>': 'gt', '\"': 'quot'
};

var newString = this;
for (var chacacter in chars) {
var regExp = new RegExp();
regExp.compile(chacacter,'g');
newString = newString.replace(regExp, '&'+chars[chacacter]+';');
}
return newString;
},

// Deprecated
// Don't hide JS data inside HREF ever again.
// Use DOM.getCommentData() instead.
/**
 * Get Url Argument
 * Turns a URL into an argument for links which function
 * purely as event launchers.
 */
getUrlArgument: function() {
// Parses a URL like "javascript:void(42);" and returns "42"
if (val = this.match(/[Jj]avascript:void\(\'?(.*?)\'?\);?/i)) {
if (val[1]) return val[1];
}
return this;
},

// Deprecated by Prototype's String.strip()
// See: http://www.prototypejs.org/api/string/strip
/**
 * Trims a string
 */
trim: function() {
return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
},

/**
 * Is Numeric
 * checks if a string is a number
 */
isNumeric: function () {
return !this.match(/\D/);
},

toDOMNodes: function() {
var wrapper = document.createElement('div');
wrapper.innerHTML = this;
return $(wrapper).immediateDescendants();
}
});

Object.extend(Form.Element, {
setValue: function(inputElement, newValue) {
inputElement = $(inputElement);
switch(inputElement.type) {
case 'text':
case 'password':
inputElement.value = newValue;
break;
case 'select':
case 'select-one':
for (var x=0; x<inputElement.options.length; x++) {
if (inputElement.options[x].value == newValue) {
inputElement.selectedIndex = x;
return true;
}
}
return false;
break;
case 'checkbox':
inputElement.checked = bool(newValue);
break;
default:
alert('setValue() can\'t yet handle input type: '+inputElement.type);
return false;
break;
}
return true;
}
});

Object.extend(Form.Element, {
getForm: function(element) {
    element = $(element);
    while (element.tagName != 'FORM') {
     element = element.parentNode;
     if (element == document) return false;
    }
    return element;
}
});

Object.extend(Number.prototype, {
// Deprecated
// See Prototype's Number.toPaddedString(length)
// http://www.prototypejs.org/api/number/toPaddedString
/**
 * Pads a number with zeroes until it is the desired length (digits)
 * (Caution: Returns a string, not a float, out of necessity)
 * Example: (7).zeroPad(3) == '007'
 */
zeroPad: function(digits) {
var str = this.toString();
while (str.length < digits) {
str = '0'+str;
}
return str;
}
});

Object.extend(Array.prototype, {
// Deprecated
// See Prototype's Array.without(Array)
// http://www.prototypejs.org/api/array/without
/**
 * Deletes any occurrences of needle from Array
 * Not recommended for use on multidimensional arrays.
 */
deleteVals: function(needle) {
var newValue = [];
if (this.length > 0) {
for (var n=0; n<this.length; n++) {
if (this[n] != needle) newValue.push(this[n]);
}
}
return newValue;
},

// Deprecated
// See Prototype's Array.uniq()
// http://www.prototypejs.org/api/array/uniq
/**
 * Weeds out duplicate values in an array.
 * Not recommended for use on multidimensional arrays.
 */
unique: function() {
newArray = new Array();
for (key in this) {
if (typeof(this[key]) != 'function') {
if (newArray.inArray(this[key]) == -1) newArray.push(this[key]);
}
}
return newArray;
},

// Deprecated
// See JavaScript's native Array.indexOf()
/**
 * In Array
 * Finds a value in an array
 */
inArray: function(needle) {
if (this.length > 0) {
for (var n=0; n<this.length; n++) {
if (this[n] == needle) return n;
}
}
return -1;
}
});



/* Patches to Prototype
--------------------------------------------------------------------------*/
Object.extend(Position, {
distanceBetween: function(coords0, coords1) {
// Determines the straight distance between two points.
// coords1 = [x: 1, y: 1]
// or
// coords1 = [1,1]
if (coords0 instanceof Array) {
var x0=coords0[0];
var y0=coords0[1];
} else {
var x0=coords0.x;
var y0=coords0.y;
}
if (coords1 instanceof Array) {
var x1=coords1[0];
var y1=coords1[1];
} else {
var x1=coords1.x;
var y1=coords1.y;
}
return Math.sqrt((x0-x1)*(x0-x1) + (y0-y1)*(y0-y1))
}
});

Object.extend(Object, {
extendProperties: function(originalObject, extendingObject) {
// Tries to intelligently extend an object's properties based
// on direction given in the property names of the extending
// object.
for (var originalKeyName in extendingObject) {
var modifier = originalKeyName.substring(0,1);
var keyName = originalKeyName.substring(1);
if (extendingObject[originalKeyName] instanceof Array) {
// - Prune: Remove from it
// + Merge: Add to it
// ! Overwrite: Replace it entirely
switch(modifier) {
case '-':
for (var x=0; x<extendingObject[originalKeyName].length; x++) {
originalObject[keyName] = originalObject[keyName].deleteVals(extendingObject[originalKeyName][x]);
}
break;
case '+':
originalObject[keyName] = originalObject[keyName].concat(extendingObject[originalKeyName]);
break;
default:
keyName = originalKeyName; // No valid modifier, so we need to restore the wrongly clipped keyName
case '!':
originalObject[keyName] = extendingObject[originalKeyName];
break;
}
} else if (extendingObject[originalKeyName] instanceof Object) {
// - Prune: Remove from it
// + Merge: Add to it or overwrite preexisting conflicting values.
// ! Overwrite: Replace it entirely
switch(modifier) {
case '-':
for (var key in extendingObject[originalKeyName]) {
if (typeof(Object.prototype[key]) == 'undefined') {
delete(originalObject[keyName][key]);
}
}
break;
case '+':
Object.extend(originalObject[keyName], extendingObject[originalKeyName]);
break;
default:
keyName = originalKeyName; // No valid modifier, so we need to restore the wrongly clipped keyName
case '!':
originalObject[keyName] = extendingObject[originalKeyName];
break;
}
} else {
originalObject[originalKeyName] = extendingObject[originalKeyName];
}
}
}
});

Object.extend(Element, {
/**
 * Tons faster and simpler than Prototype's
 */
hasClassName: function(element, className) {
return Element.manipulateClass(element, className, 'find');
},

addClassName: function(element, className) {
return Element.manipulateClass(element, className, 'add');
},

removeClassName: function(element, className) {
return Element.manipulateClass(element, className, 'remove');
},

clearClassNames: function(element) {
element.className = '';
return element;
},

manipulateClass: function(element, className, action) {
if (typeof(element.tagName) != 'undefined') {
var classes = element.className;
if (action == 'add') {
if (!Element.hasClassName(element, className)) {
element.className += ' '+className;
return element;
}
}
var cNames = classes.split(' ');
if (cNames.length > 0) {
if (action == 'remove') {
cNames = cNames.deleteVals(className);
} else if (action == 'find') {
return (cNames.indexOf(className) != -1);
}
}
if (action == 'add') {
if (typeof(className) == 'array') {
cNames = cNames.concat(className);
} else {
cNames.push(className);
}
}
if (action == 'add' || action == 'remove') element.className = cNames.join(' ').trim();
if (action == 'find') return false;
} else {
return false;
}
},

/**
 * Element.visible
 * By Kramer
 *
 * I was disappointed that Prototype's Element.visible really only
 * reports on the status of element.style.display. What I really
 * wanted to know was "can the user see this element, or is it
 * contained inside a hidden element?" This extension fixes that with
 * the new "recursive" option.
 *
 * Recursive option added to seek up the tree to make sure
 * all parent nodes are visible, too. This should effectively
 * return a true/false indicating whether the given element is
 * indeed VISIBLE TO THE USER.
 */
visible: function(element, recursive) {
if (!recursive) return $(element).style.display != 'none';
var search = element;
while (search = search.parentNode) {
if ($(search).style.display == 'none') return false;
}
return true;
},
insertedNodes: [],
insertNode: function(node, tagName, key){
for(var x=0; x<Element.insertedNodes.length; x++){
if(Element.insertedNodes[x].node == node){
return false;
}
}
var insertedNode = {
node: node,
key: (key)? key : null
};

/* Create and place container */
insertedNode.container = document.createElement('span');
Element.addClassName(insertedNode.container, "iFrameHackContainer");
insertedNode.node.parentNode.insertBefore(insertedNode.container, insertedNode.node);
insertedNode.container.appendChild(insertedNode.node);

/* Create and place throbber */
insertedNode.newNode = document.createElement(tagName);
Element.addClassName(insertedNode.newNode, "newNode");
insertedNode.container.appendChild(insertedNode.newNode);

var dims = Element.getDimensions(insertedNode.container);

/* Add essential styles to container and throbber */
Object.extend(insertedNode.container.style, {
position: "relative",
display: "block",
zoom: "1"
});
Object.extend(insertedNode.newNode.style, {
position: "absolute",
top: "0",
left: "0",
width: (document.all)?dims['width']+"px" : "100%",
height: (document.all)?dims['height']+"px" : "100%"
});

Element.insertedNodes.push(insertedNode);
return insertedNode;
},
removeNode: function(node, key){
var insertedNode = null;
for(var x=0; x<Element.insertedNodes.length; x++){
if(Element.insertedNodes[x].node == node && ((!key && !Element.insertedNodes[x].key) || Element.insertedNodes[x].key == key)){
insertedNode = Element.insertedNodes.splice(x, 1);
break;
}
}
if(!insertedNode){
return false;
}
insertedNode = insertedNode[0];
insertedNode.container.parentNode.insertBefore(node, insertedNode.container);
insertedNode.container.parentNode.removeChild(insertedNode.container);
return true;
},
iFrameHacks: [],
iFrameHack: function(node){
if(!document.all){
return false;
}
var iFrameHack = Element.insertNode(node, 'iframe', 'iFrameHack');
if(!iFrameHack){
return false;
}
Element.addClassName(iFrameHack.container, "iFrameContainer");
Element.addClassName(iFrameHack.newNode, "iFrame");
Object.extend(iFrameHack.newNode.style, {
filter: "alpha(opacity=0)",
opacity: "0",
zIndex: "2"
});
if(parseInt(iFrameHack.node.style.zIndex) > 1){
Object.extend(iFrameHack.newNode.style, {
zIndex: parseInt(iFrameHack.node.style.zIndex)+1
});
}
return iFrameHack;
},
cancelIFrameHack: function(node){
if(!document.all){
return false;
}
Element.removeNode(node, 'iFrameHack');
},
parseClasses: function(el, parseChildren){
var classes = String(el.className).split(" ");
var keepClasses = [];
for(var x=0; x<classes.length; x++){
if(classes[x].indexOf(':') != -1){
var matches = classes[x].match(/^(\w*):{1,2}\{(.*)\}$/);
var ob = {};
var vars = matches[2].split(",");
for(var y=0; y<vars.length; y++){
vars[y] = vars[y].split(/::?/);
if(!vars[y][1]) vars[y][1] = '';
ob[vars[y][0]] = vars[y][1];
}
el[matches[1]] = ob;
}else{
keepClasses.push(classes[x]);
}
}
el.className = keepClasses.join(" ");
if(parseChildren){
var eles = el.getElementsByTagName('*');
for(var x=0; x<eles.length; x++){
this.parseClasses(eles[x]);
}
}
}
});


Event._observe = Event.observe;
Object.extend(Event, {
/**
 * Returns the keycode related to an event, browser-independent
 */
keyCode: function(event) {
if (typeof(event.which) == 'undefined') return event.keyCode;
return Math.max(event.which, event.keyCode);
},

/**
 * Event.observe now returns an ID which can be sent to Event.stopObserving to terminate that event handler.
 */
registry: [],

id: 0,

observe: function(element, name, observer, useCapture) {
Event.id++;
var regEntry = {
id: Event.id,
element: element,
name: name,
observer: observer,
useCapture: useCapture
};
Event.registry.push(regEntry);
Event._observe(element, name, observer, useCapture);
return regEntry;
},

unObserve: function(regEntry) {
for (var x=0; x<Event.registry.length; x++) {
var a = Event.registry[x];
if (a.id == regEntry.id) {
Event.registry.splice(x, 1);
Event.stopObserving(a.element, a.name, a.observer, a.useCapture);
return true;
}
}
return false;
},

fire: function(el, name) {
for(var x=0; x<Event.registry.length; x++){
if(Event.registry[x].element == el && Event.registry[x].name == name){
Event.registry[x].observer();
}
}
}
});


/* Plain old functions
--------------------------------------------------------------------------*/
document.getElementsBySelector = function(selectors, withinNodes) {
if (!(selectors instanceof Array)) selectors = [selectors];
if (typeof(withinNodes) == 'undefined') {
var withinNodes = [document.getElementsByTagName('body')[0]];
} else if (!(withinNodes instanceof Array)) {
var withinNodes = [withinNodes];
}
var resultNodes = [];
for (var cSel=0; cSel<selectors.length; cSel++) {
for (var cNodes=0; cNodes<withinNodes.length; cNodes++) {
var resultNodes = resultNodes.concat(Element.getElementsBySelector(withinNodes[cNodes], selectors[cSel]));
}
}
return resultNodes.unique();
};

function getCoordsCenter(coords) {
return {
x: coords.x/2,
y: coords.y/2
};
};
function getViewportSize() {
var retVal = {
x: self.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth),
y: self.innerHeight || (document.documentElement.clientHeight || document.body.clientHeight)
};
// Legacy property names
retVal.height = retVal.y;
retVal.width = retVal.x;
return retVal;
};
function getViewportCenter() {
return getCoordsCenter(getViewportSize());
};
function getScreenSize() {
var screenW = 640, screenH = 480;
if (parseInt(navigator.appVersion)>3) {
 screenW = screen.width;
 screenH = screen.height;
}
else if (navigator.appName == "Netscape"
    && parseInt(navigator.appVersion)==3
    && navigator.javaEnabled()
   )
{
 var jToolkit = java.awt.Toolkit.getDefaultToolkit();
 var jScreenSize = jToolkit.getScreenSize();
 screenW = jScreenSize.width;
 screenH = jScreenSize.height;
}

return {
x: screenW,
y: screenH
}
};
function getScreenCenter() {
return getCoordsCenter(getScreenSize());
}

/**
 * Changes pretty much any variable into its boolean equivalent
 * Strings like yes, no, true, false, y, n
 * Numbers like 0, 1, 2...
 * @return bool
 */
function bool(arg) {
// Returns true or false
// Does everything concievable to make arg into a boolean value
if (typeof(arg) == 'undefined') return false;
if (typeof(arg) == 'boolean') return arg;
switch(arg.toLowerCase()) {
case 'yes':
case 'true':
case 'y':
return true;
break;
case 'false':
case 'no':
case 'n':
return false;
break;
default:
if (arg === true) return true;
if (arg === false) return false;
if (parseInt(arg) > 0) return true;
if (parseInt(arg) == 0) return false;
break;
}
return null; // Inconclusive
}

/**
 * Converts a UNIX timestamp into a JS Date object
 */
function unixToDate(unixtime) {
var time = new Date();
time.setTime(unixtime*1000);
return time;
}

// Deprecated (partially)
// See Prototype's each()
// http://www.prototypejs.org/api/hash/each
// Although I don't think each() is recursive, not sure.
/**
 * For-each within an object. Recursive if you want it to be.
 **/
function foreach(obj, func, recursive) {
for (var key in obj) {
if (!Object.prototype[key]) {
if (obj[key] instanceof Object && recursive) {
foreach(obj[key], func);
}
func(obj[key], obj);
}
}
}

/**
 * Executes the given command within a different thread
 **/
Function.prototype.newThread = function(delay) {
if (typeof(delay) == 'undefined') delay = 1;
setTimeout(this, delay);
};
newThread = function(toExec, delay) {
toExec.newThread(delay);
};

/* Adam added 5-22-07 to fix getElementsBySelector in IE */
Object.extend(Selector.prototype, {
buildMatchExpression: function() {
var params = this.params, conditions = [], clause;

if (params.wildcard)
conditions.push('true');
if (clause = params.id)
conditions.push('Element.readAttribute(element, "id") == ' + clause.inspect());
if (clause = params.tagName)
conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
if ((clause = params.classNames).length > 0)
for (var i = 0, length = clause.length; i < length; i++)
conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
if (clause = params.attributes) {
clause.each(function(attribute) {
var value = 'Element.readAttribute(element, ' + attribute.name.inspect() + ')';
var splitValueBy = function(delimiter) {
return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
}
switch (attribute.operator) {
case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
case '|=':      conditions.push(splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()); break;
case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
case '':
case undefined: conditions.push('Element.hasAttribute(element, ' + attribute.name.inspect() + ')'); break;
default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
}
});
}
return conditions.join(' && ');
}
});
/* End added */