function agentMatches( str ) {
	return (navigator.userAgent.toLowerCase().indexOf( str.toLowerCase() )
		!= -1);
	}

function isIE( )
	{ return agentMatches( 'MSIE' ); }

function isSafari( )
	{ return agentMatches( 'Safari' ); }

function isFirefox( )
	{ return agentMatches( 'Firefox' ); }

function isOpera( )
	{ return agentMatches( 'Opera' ); }

function isMac( )
	{ return agentMatches( 'Mac' ); }
	
function getBrowserVersion( ) {
	if( isIE( ) ) {
		var p = navigator.userAgent.toLowerCase().indexOf( 'msie' );
		return parseInt( navigator.substring( p ) );
		}
	else {
		return parseInt( navigator.userAgent );
		}
	}

function auri( path ) {
	if( !isset( 'AURI' ) ) {
		alert( 'No AURI defined!' );
		}
	return AURI + path;
	}

function keys( o ) {
	var r = [];
	for( var k in o ) {
		r.push( k );
		}
	return r;
	}

function isset( v ) {
	return ( typeof( window[ v ] ) != 'undefined' );
}
	
function getObj( id ) {
	return document.getElementById( id );
	}

function getObjX( obj ) {
	var x = 0;
	if( obj.offsetParent ) {
		x = obj.offsetLeft;
		while( obj = obj.offsetParent ) {
			x += obj.offsetLeft;
			}
		}
		
	return x;
	}

function getObjY( obj ) {
	var y = 0;
	if( obj.offsetParent ) {
		y = obj.offsetTop;
		while( obj = obj.offsetParent ) {
			y += obj.offsetTop;
			}
		}
	return y;
	}

function getObjWidth( obj ) {
	return obj.offsetWidth;
	}

function getObjHeight( obj ) {
	return obj.offsetHeight;
	}
	
function getSourceObject( e ) {
	return e.srcElement;
	}
	
function getEventX( e ) {
	var e = getEvent( e );

	if( e && e.clientX )
		return e.clientX;
	else
		return false;
	}

function getEventY( e ) {
	var e = getEvent( e );

	if( e && e.clientY )
		return e.clientY;
	else
		return false;
	}

function getEventShift( e ) {
	return e.shiftKey;
	}
	
function getEvent( e ) {

	if( window.event )
		return window.event;
		
	if( isFirefox( ) && e )
		return e;
	
	return null;
	}

function chainHandlers( first, last ) {
	return f = function( e ) {
		var r = true;

		if( first ) {
			this.__t_call = first;
			r = this.__t_call( e );
			if( r === false ) {
				return r;
				}
			}

		if( last ) {
			this.__t_call = last;
			r = this.__t_call( e );
			}

		return r;
		}
	}

function abortEvent( e ) {

	e = getEvent( e );
	e.cancelBubble = true;
	if( e.stopPropagation )
		e.stopPropagation();
	
	return false;
	}

function setCookie( n, v ) {
	document.cookie = n + '=' + v + '; path=/';
	}

function getCookie( n ) {
	var nameEQ = n+'=';
	var ca = document.cookie.split(';');
	for( var ii = 0; ii < ca.length; ii++ ) {
		var c = ca[ii];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0)
			return c.substring(nameEQ.length,c.length);
		}

	return null;
	}

function eraseCookie(name) {
	setCookie(name,'');
	}


function getText( obj ) {

	if( obj.innerText )
		return obj.innerText;
	
	return null;
	}

function setText( obj, text ) {
	obj.innerText = text;
	}

function getHTML( obj ) {
	if( obj.innerHTML )
		return obj.innerHTML;
	
	return null;
}

function setHTML( obj, html ) {
	obj.innerHTML = html;
	}

var _lockedObjects = null;

function adjustLockedPositions( ) {
	if( typeof( _lockedObjects ) == 'object' ) {
		var dx = getScrollOffsetX( );
		var dy = getScrollOffsetY( );
		for( var k in _lockedObjects ) {
			_lockedObjects[ k ].style.top = z = 
				(_lockedObjects[ k ]._lockBaseOffsetY + dy) + 'px';
			_lockedObjects[ k ].style.left =
				(_lockedObjects[ k ]._lockBaseOffsetX + dx) + 'px';
			}
		}
	}

function lockPosition( obj ) {
	
	if( typeof( _lockedObjects ) != 'array' ) {

		window.onscroll = chainHandlers(
			adjustLockedPositions, window.onscroll
			);

		_lockedObjects = [];
		}
	
	if( !in_array( obj, _lockedObjects ) ) {
		obj._lockBaseOffsetX = getObjX( obj.style.top );
		obj._lockBaseOffsetY = getObjY( obj.style.left );
		_lockedObjects.push( obj );
		adjustLockedPositions( );
		}
	}


function clearNode( node ) {
	if( node.removeChild ) {
		while( node.firstChild ) {
			node.removeChild( node.firstChild );
			}
		}
	return node;
	}

function removeNode( node ) {
	if( node ) {
		node.style.display = 'none';
		if( node.parentNode ) {
			if( node.parentNode.removeChild ) {
				node.parentNode.removeChild( node );
				}
			}
		}
	}

function replaceNode( node, new_node ) {
	if( node && new_node && node.parentNode ) {
		if( node.parentNode.replaceChild ) {
			node.parentNode.replaceChild( new_node, node );
		}
	}
}

var warn = function warn( ) { }

function is_array( v ) {
	return ( is_object( v ) && v.constructor == Array );
}

function is_string( v ) {
	return ( typeof( v ) == 'string' );
}

function is_object( v ) {
	return ( typeof( v ) == 'object' );
}

function in_array( obj, arr ) {
	for( var k in arr ) {
		if( arr[k] == obj )
			return true;
		}
	return false;
	}

function objlen( obj ) {
	var N = 0;
	for( var k in obj ) N++;
	return N;
	}

function getWindowWidth( ) {
	if( window
	&&	window.innerWith ) {
		return window.innerWidth;
		}
	else
	if( document.documentElement
	&&	document.documentElement.clientWidth ) {
		return document.documentElement.clientWidth;
		}
	else
	if( document.body
	&&	document.body.clientWidth ) {
		return document.body.clientWidth;
		}
	}

function getWindowHeight( ) {
	if( window
	&&	window.innerHeight ) {
		return window.innerHeight;
		}
	else
	if( document.documentElement
	&&	document.documentElement.clientHeight ) {
		return document.documentElement.clientHeight;
		}
	else
	if( document.body
	&&	document.body.clientHeight ) {
		return document.body.clientHeight;
		}
	}

function getDocumentHeight( ) {

	if( document
	&&	document.body
	&&	document.body.scrollHeight ) {
		return document.body.scrollHeight;
		}
	else
	if(	document
	&&	document.documentElement
	&&	document.documentElement.scrollHeight ) {
		return document.documentElement.scrollHeight;
		}

	}
	
function getDocumentWidth( ) {
	if(	document
	&&	document.body ) {
		return document.body.clientWidth;
		}
	}
	
function getScrollOffsetY( ) {
	if( document
	&&	document.body
	&&	document.body.scrollTop )
		{ return document.body.scrollTop; }

	if( document
	&&	document.documentElement
	&&	document.documentElement.scrollTop )
		{ return document.documentElement.scrollTop; }

	if( window.pageYOffset )
		{ return window.pageYOffset; }
	
	return 0;
	}

function getScrollOffsetX( ) {
	if( document
	&&	document.body
	&&	document.body.scrollTop )
		{ return document.body.scrollLeft; }

	if( document
	&&	document.documentElement
	&&	document.documentElement.scrollTop )
		{ return document.documentElement.scrollLeft; }

	if( window.pageYOffset )
		{ return window.pageXOffset; }
	
	return 0;
	}


function getObjWidth( obj ) {
	return obj.offsetWidth;
	}

function getObjHeight( obj ) {
	return obj.offsetHeight;
	}

function emulate_onclick( obj ) {
	if( obj ) {
		if( obj.onclick ) {
			obj.onclick( );
			}
		}
	}



String.prototype.trim = function( ) {
	return this.replace( /^\s+|\s+$/g, '' );
	}
	
function appendSibling( o, n ) {
	o.parentNode.insertBefore( n, o.nextSibling );
	return n;
	}

if( isFirefox() ) {
	HTMLElement.prototype.__defineGetter__(
		 'innerText'
		, function () { return this.textContent; }
	);
	
	HTMLElement.prototype.__defineSetter__(
		 'innerText'
		, function (v) { return this.textContent = v; }
	);
}



var popped = new Object();
function open_pop( name, uri, width, height, features ) {
	pop = popped[ name ];
	pop = window.open(
		  uri
		, name
		, 'width=' + width + ',height=' + height + ',' + features
		);
	
	/**
	 *	@browser ?
	 *	Catches instance where opener is refreshed and loses link to pop-up.
	 */
	pop.resizeTo( width, height );
	pop.focus();
	
	popped[name] = pop;
	
	return pop;
	}

function close_pop( name ) {
	popped[name].close();
	}
	
function getProperty( id ) {
	return this[id];
	}
	
function map( arr, method /*, arg, arg, arg */) {

	var args = [];
	for( var ii = 2; ii < arguments.length; ii++ ) {
		args.push( arguments[ii] );
		}

	var l = arr.length;
	var r = [];
	if( isFunction( method ) ) {
		for( ii = 0; ii < l; ii++ ) {
			r.push( method.apply( arr[ii], args ) );
			}
		}
	else {
		for( ii = 0; ii < l; ii++ ) {
			r.push( arr[ii][method].apply( arr[ii], args ) );
			}
		}
	return r;
	}

function bind( obj, method /*, arg, arg, arg */ ) {

	var args = new Array( );
	for( var ii = 2; ii < arguments.length; ii++ ) {
		args.push( arguments[ii] );
		}
	
	if( obj ) {
		f = function( ) {
		
			/**
			 *	@browser Firefox2
			 *	Firefox 2 now has different scoping or reference rules or
			 *	something, if we do `var a = args' then it persists the
			 *	additional args and we end up with arbitrarily many arguments.
			 */
			var a = [];
			for( var ii = 0; ii < args.length; ii++ ) {
				a.push( args[ii] );
				}

			for( var ii = 0; ii < arguments.length; ii++ ) {
				a.push( arguments[ii] );
				}

			if( is_string( method ) ) {
				return obj[method].apply( obj, a );
				}
			else {
				return method.apply( obj, a );
				}

			return rval;
			}
		return f;
		}
	else {
		return function( ) {

			var a = [];
			for( var ii = 0; ii < args.length; ii++ ) {
				a.push( args[ii] );
				}

			for( var ii = 0; ii < arguments.length; ii++ ) {
				a.push( arguments[ii] );
				}

			if( is_string( method ) ) {
				return window[method].apply( obj, a );
				}
			else {
				return method.apply( obj, a );
				}
			}
		}
	}

function extend( dst, src ) {
	for( k in src ) {
		dst[ k ] = src[ k ];
		}
	return dst;
	}
	
function extendAndChain( dst, src ) {
	for( k in src ) {
		if( isFunction( dst[ k ] ) ) {
			dst[ k ] = chainHandlers( dst[ k ], src[ k ] );
			}
		else {
			dst[ k ] = src[ k ];
			}
		}
	return dst;
	}
		
function dprint( str ) {
	var o = getObj( 'debugConsole' );
	if( o ) {
		setText( o, str + "\n" + getText( o ) + "\n" );
		}
	}

document.getElementsByClass = function( searchClass, tag ) {
	var e = [];
	var p = new RegExp( "(^|\\s)" + searchClass + "(\\s|$)" );

	var n = this.getElementsByTagName( '*' );
	var l = n.length;
	
	for( ii = 0; ii < l; ii++ ) {
		if( p.test( n[ii].className ) ) {
			e.push( n[ii] );
			}
		}

	return e;
	}

function binary_search( sortedList, target, cmp ) {

	var l	= 0;
	var h	= sortedList.length - 1;
	var a	= 0;

	while( l <= h ) {
		a = parseInt( (l + h)/2 );
	
		var r = cmp( sortedList[ a ], target );
	
		if( r < 0 ) {
			l = a + 1;
			continue;
			}

		if( r > 0 ) {
			h = a - 1;
			continue;
			}
		
		return a;
		}

	return a;
	}

function debug_outline( obj ) {
	outline = document.body.appendChild(
		 document.createElement( 'div' )
		);
	
	outline.style.position	= 'absolute';
	outline.style.top		= getObjY( obj )		+ 'px';
	outline.style.left		= getObjX( obj )		+ 'px';
	outline.style.width		= getObjWidth( obj )	+ 'px';
	outline.style.height	= getObjHeight( obj )	+ 'px';
	outline.style.border	= '1px solid orange';
	}


var __oldViewportX = null;
var __oldViewportY = null;
function hasWindowReallyResized( e ) {
	e = getEvent( e );
	
	if( isIE( ) ) {
		if( __oldViewportX === null ) {
			__oldViewportX = getWindowWidth( );
			__oldViewportY = getWindowHeight( );
			return abortEvent( e );
			}
		}

	if( getWindowWidth( ) != __oldViewportX
	||	getWindowHeight( ) != __oldViewportY ) {
		__oldViewportX = getWindowWidth( );
		__oldViewportY = getWindowHeight( );
		return true;
		}
	
	return abortEvent( e );
	}

function auri( path ) {
        if( !isset( 'AURI' ) ) {
                console.warn( 'No AURI defined!' );
                }
        return AURI + path;
        }

function ajax_pull( in_uri ) {
        XMLReq.pullURI( in_uri, function ( ) { } );
        return true;
        }

var XMLReq = new Object( );

XMLReq.test = function( ) {
        var r = new Object( );
        if( window.XMLHttpRequest )
                r.xr = new XMLHttpRequest( );
        else
                r.xr = new ActiveXObject( 'Microsoft.XMLHTTP' );

        if( r.xr ) return true;

        return false;
        }

XMLReq.openThreads = 0;

XMLReq.openThread = function( ) {
        this.openThreads++;
        if( this.threadCallback ) {
                this.threadCallback( this.openThreads );
                }
        }

XMLReq.closeThread = function( ) {
        if( this.openThreads ) {
                this.openThreads--;
                }
        else {
                alert(
                        'XMLReq: Attempted to close a thread, but no threads are open!'
                        );
                }

        if( this.threadCallback ) {
                this.threadCallback( this.openThreads );
                }
        }

XMLReq.objectify = function( xml ) {

        if( !xml.firstChild ) return xml.nodeValue;

        var s = new Array( );
        var v = '';
        var r = null;
        var n = xml.firstChild;

        do {
                r = XMLReq.objectify( n );
                if( typeof( r ) != 'object' ) {
                        v += r;
                        }
                else {
                        s.push( r );
                        }
                } while( n.nextSibling && (n = n.nextSibling) );

        var o = new Array( );

        if( s.length == 0 ) {
                o = v;
                }
        else {
                for( var k = 0; k < s.length; k++ ) {
                        if( s[k] ) {
                                for( var l in s[k] ) {
                                        if( objlen( s[k] ) == 1 ) {
                                                if( typeof( s[k][l] ) != 'object' ) {
                                                        o[ l ] = s[k][l];
                                                        break;
                                                        }
                                                }
                                        if( !o[ l ] ) {
                                                o[ l ] = new Array( );
                                                }

                                        if( is_array( o[ l ] ) )
                                                o[ l ].push( s[k][l] );
                                        }
                                }
                        }
                }

        var x = new Array( );
        x[ xml.nodeName ] = o;

        return x;
        }

XMLReq.stateChange = function( obj ) {
        if( obj.xr.readyState == 4 ) {
                if( obj.xr.status == 200 ) {

                        if( !obj.xr.responseXML ) {
                                console.log( 'The resource did not return XML.' );
                                obj.func( null );
                                return;
                                }

                        var d = XMLReq.objectify( obj.xr.responseXML );
                        if(     d[ '#document' ]
                        &&      d[ '#document' ].response
                        &&      d[ '#document' ].response[0] ) {
                                obj.func( d[ '#document' ].response[0] );
                                }
                        else {
                                obj.func( null );
                                }

                        this.closeThread( );
                        }
                else if( obj.xr.status == 404 ) {
                        alert( 'The requested resource was not found (404).' );
                        }
                else if( obj.xr.status ) {
                        alert(
                                'An error occurred when attempting to retrieve '
                        +       'XML data: ' + obj.xr.status
                                );
                        }
                }
        }

XMLReq.newRequest = function( ) {
        var r = new Object( );
        if( window.XMLHttpRequest )
                r.xr = new XMLHttpRequest( );
        else
                r.xr = new ActiveXObject( 'Microsoft.XMLHTTP' );

        if( !r.xr ) {
                alert(
                        'This action requires XMLHTTP support, but it is not available.'
                        );
                }

        r.xr.onreadystatechange = function( ) { XMLReq.stateChange( r ); }

        return r;
        }


XMLReq.pullURI = function( uri, in_func ) {
        var obj = this.newRequest( );

        this.openThread( );
        obj.func = in_func;
        obj.xr.open( 'POST', uri, true );
        obj.xr.send( null );
        }

XMLReq.getValue = function( obj, key ) {
        if( !obj )
                return false;
        var r = obj.getElementsByTagName( key )[ 0 ];
        if( r ) {
                if( r.childNodes.length > 1 )
                        return r.childNodes[1].nodeValue;
                else
                if( r.firstChild )
                        return r.firstChild.nodeValue;
                }
        return '';
        }

XMLReq.getObjArr = function( obj, key ) {
        if( !obj || !obj.responseXML )
                return false;
        else
                return obj.responseXML.getElementsByTagName( key );
        }

function getEventTarget( e ) {
    if( e.target ) {            // Gecko
        return e.target;
    }
        
    if( e.srcElement ) {        // IE
        return e.srcElement;
    }
        
    return null; 
}    
