//  References:
//  http://www.howtocreate.co.uk/tutorials/javascript/objects -- JavaScript objects, private/public properties

/*

    OK. After several hours of frustrating trial-and-error, objects can now be
    "extended", while keeping their private bits private. Doing this in JavaScript
    was not hard, just unclear.
    
    Objects which can have functions dynamically added to them -- can be "extended" --
    have a meta Prototype object stored in the NPL object: NPL.Prototype.__ObjectName__
    
    You can use, "NPL.Prototype.__ObjectName__.myfunction =..." to add a function
    to that object's class. Note that any objects which are instantiated before
    the function is added will not have that function added to them. It doesn't
    work like Javascript's built-in prototype class that way.
    
    I can not find a way to get private methods and properties to work while
    keeping Javascript's built-in prototype functionality, so I have duplicated
    it.
    
    Also, dynamically-added functions do not have access to the object's private
    properties and methods. There does not appear to be a way to overcome this
    in Javascript, but it's considered correct behavior anyway.
    
    You can freely add public properties, however, which will stay local to the
    object that inherits them.

*/

if ( typeof NPL !== 'undefined' )
{
    if ( typeof _NPL_Dbg != 'undefined' ) { _NPL_Dbg('NPLib: "NPL" already defined.'); }
}
else
{
    //  NPL is a singleton which uses closures for some private properties and methods.
    //  There _is_ a way to instantiate another instance of this thing, but that would be a silly thing to do.
    //  All of the NPL functions are encapsulated here to protect them from namespace collisions.
    //  This little library is fully buzzword compliant. :-(
    var NPL = function ()
    {
        //  http://www.hunlock.com/blogs/Closing_The_Book_On_Javascript_Closures
        //  Private properties.
        var _uidcount = 1;
        //  Private methods.
        //  Public:
        return {
        
        	Browser: function ()
        	{
        		//	With help from http://www.thespanner.co.uk/2009/01/29/detecting-browsers-javascript-hacks/
        		//	I hate browser sniffing too, but occasionally it's helpful.
        		//	For example, we have to determine the correct way to find the browser window's width before the
        		//	page is fully loaded, which can cause unpredictable results.
        		//	Otherwise, whenever possible, feature detection is done instead.
    			var _mozilla=false, _mozilla2=false, _mozilla3=false, _mozilla3_5=false, _ie=false, _devsbane=false, _webkit=false, _chrome=false, _opera=false;
        		if ( /a/[-1]=='a' )
        		{
        			_mozilla = true;
        			if ( (function x(){})[-6]=='x' ) { _mozilla2 = true; }
        			else if ( (function x(){})[-5]=='x' ) { _mozilla3 = true; }
        			else if ( Object.getPrototypeOf ) { _mozilla3_5 = true; }
        		}
        		else if ( '\v'=='v' )
        		{
        			// Internet Explorer 6 lands here too.
        			if ( typeof XMLHttpRequest == 'undefined' ) { _devsbane = true; }
        			_ie = true;
        		}
        		else if ( /a/.__proto__=='//' )
        		{
        			_webkit = true;
        		}
        		else if ( /source/.test((/a/.toString+'')) )
        		{
        			_chrome = true;
        		}
        		else if  ( /^function \(/.test([].sort) )
        		{
        			//	Doesn't appear to work on Opera 8.
        			_opera = true;
        		}
        		else if ( window.opera )
        		{
        			//	Works on all versions of Opera?
        			_opera = true;
        		}
        		else
        		{
        			if ( ! XMLHttpRequest ) { _devsbane = true; }
        		}
        		return {
        			Firefox:    function(){return _mozilla;},
        			Firefox2:   function(){return _mozilla2;},
        			Firefox3:   function(){return _mozilla3;},
        			Firefox3_5: function(){return _mozilla3_5;},
        			IE:         function(){return _ie;},
        			IE6:        function(){return _devsbane;},
        			Safari:     function(){return _webkit;},
        			Chrome:     function(){return _chrome;},
        			Opera:		function(){return _opera;}
        		}
        	}(),
        
            Mouse: function ()
            {
                var _x = 0; var _y = 0;
                __get_Mouse_XY = function ( pEvent )
                {
                    if ( typeof pEvent == 'undefined' )
                    {
                        if ( typeof window.event != 'undefined' )
                        {
                            if ( typeof window.event.clientX != 'undefined' )
                            {
                                __get_Mouse_XY = function()
                                {
                                    _x = window.event.clientX+document.body.scrollLeft+document.documentElement.scrollLeft;
                                    _y = window.event.clientY+document.body.scrollTop+document.documentElement.scrollTop;
                                }
                            }
                        }
                    }
                    else
                    {
                        if ( typeof pEvent.pageX != 'undefined' )
                        {
                            __get_Mouse_XY = function(e){_x=e.pageX;_y=e.pageY;}
                        }
                    }
                };
                document.onmousemove = function (pEvent) { NPL.Mouse.update(pEvent) };
                if ( window.captureEvents ) { window.captureEvents(Event.MOUSEMOVE); }
                return {
                
                    x: function() { return _x; },
                    
                    y: function() { return _y; },
                    
                    update: function ( pEvent ) { __get_Mouse_XY(pEvent); }
                
                }
            }(),
            
            Page: function ()
            {
                //  Private properties.
                var _onload = window.onload; window.onload = function () { NPL.Page.LaunchInits() };
                var _loaded = false, _initfunctions = [], _resizefunctions = [], _watchresize = false, _windowlastx = 0, _windowlasty = 0, _windowchanged = 0, _windowlastchg = 0;
                return {
                
                	_WindowChanged: function ()
                	{
                		if ( typeof document.body.clientHeight != 'undefined' && typeof document.body.clientWidth != 'undefined' && _windowlastx == 0 && _windowlasty == 0 )
                		{
                			_windowlastx = NPL.Window.Width();
                			_windowlasty = NPL.Window.Height();
                			NPL.Page._WindowChanged = function(){
                				if ( _windowlastx != NPL.Window.Width() || _windowlasty != NPL.Window.Height() )
                				{
                					_windowlastchg = (new Date()).getTime();
                					_windowlastx = NPL.Window.Width();
                					_windowlasty = NPL.Window.Height();
                					if ( _windowchanged == 0 )
                					{
                						_windowchanged = 1;
                						//	Signal that window has *begun* changing.
                						if ( typeof _resizefunctions['begin'] != 'undefined' ) { for (var i=0;i<_resizefunctions['begin'].length;i++){_resizefunctions['begin'][i]('begin');}}
                					}
                					else
                					{
	                					//	Signal that window is *still* changing.
	                					if ( typeof _resizefunctions['active'] != 'undefined' ) { for (var i=0;i<_resizefunctions['active'].length;i++){_resizefunctions['active'][i]('active');}}
	                				}
                				}
                				else if ( _windowchanged == 0 )
                				{
                					//	Signal that window has not changed.
                					if ( typeof _resizefunctions['quiet'] != 'undefined' ) { for (var i=0;i<_resizefunctions['quiet'].length;i++){_resizefunctions['quiet'][i]('quiet');}}
                				}
                				else if ( (new Date()).getTime() - _windowlastchg > 3000 )
                				{
                					_windowchanged = 0;
                					//	Signal that window has *stopped* changing.
                					if ( typeof _resizefunctions['end'] != 'undefined' ) { for (var i=0;i<_resizefunctions['end'].length;i++){_resizefunctions['end'][i]('end');}}
                				}
                			}
                		}
                	},
                                	
                    OnLoad: function ( pInitFunction )
                    {
                        _initfunctions[_initfunctions.length] = pInitFunction;
                    },

					OnResize: function ( pResizeCode, pResizeFunction )
					{
						if ( pResizeCode == 'begin' || pResizeCode == 'end' || pResizeCode == 'active' || pResizeCode == 'quiet' )
						{
							if ( typeof _resizefunctions[pResizeCode] == 'undefined' ) { _resizefunctions[pResizeCode] = []; }
							_resizefunctions[pResizeCode][_resizefunctions[pResizeCode].length] = pResizeFunction;
							if ( _watchresize == false )
							{
								_watchresize = true;
								setInterval(function(){NPL.Page._WindowChanged()}, 1000);
							}
						}
					},
										
                    LaunchInits: function ()
                    {
                        if ( ! _loaded )
                        {
                            for ( var xInit = 0; xInit < _initfunctions.length; xInit++ ) { _initfunctions[xInit](); }
                            delete _initfunctions; 
                            if ( _onload ) { _onload(); }
                            delete _onload;
                            _loaded = true;
                        }
                    },
                    
                    Width: function ()
                    {
                    	//	Bugfix 10/12/2009: Safari's braindead Javascript engine returns "NaN" if you
                    	//	try to turn the following code into a terniary operation:
                    	//	return document.width ? parseInt(document.width) : parseInt(document.body.clientWidth);
                    	if ( typeof document.width != 'undefined' ) { return parseInt(document.width); }
                        return parseInt(document.body.clientWidth);
                    }
                }
            }(),
            
            Window: function()
            {
            	_window_height = function(){return 0;}
            	_window_width = function(){return 0;}
            	if ( typeof window.innerHeight != 'undefined' && typeof window.innerWidth != 'undefined' )
            	{
            		//	This is preferred.
            		_window_height = function(){return window.innerHeight};
            		_window_width = function(){return window.innerWidth};
            	}
            	else if ( typeof document.documentElement != 'undefined' )
            	{
            		if ( typeof document.documentElement.clientHeight != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' )
            		{
            			_window_height = function(){return document.documentElement.clientHeight};
            			_window_width = function(){return document.documentElement.clientWidth};
            		}
            	}
            	else if ( typeof document.body != 'undefined' )
            	{
	            	if ( document.body && typeof document.body.clientWidth != 'undefined' && typeof document.body.clientHeight != 'undefined' )
	            	{
	            		_window_height = function(){return document.body.clientHeight};
	            		_window_width = function(){return document.body.clientWidth};
	            	}
	            }
            	
            	return {
            		Width: function()
            		{
            			return _window_width();
            		},
            		
            		Height: function()
            		{
            			return _window_height();
            		}
            	}
            }(),
                                
            NewUID: function ()
            {
                var xDate = new Date();
                return (++_uidcount).toString() + xDate.getTime().toString().substring(6) + (1000 + ~~(Math.random() * 1000)).toString();
            },
            
            Select: function ( pSelector )
            {
                return new ElementSet(pSelector);
            },
            
            Prototype: function ()
            {
                return {
                    
                    __ElementSet__: new Object
                    
                }
            }(),
            
            Image: function ( pImagePath )
            {
            	var xImage = document.createElement('img');         //  Can't use "new Image()", Safari will choke on it later.
            	xImage.src = pImagePath;
        		//	Try attaching the image to the document tree?
        		//	Internet Explorer will mysteriously shrink the image down
        		//	to 28x28 if the image is attached to an element at any point in its
        		//	load process. *sigh*
				//	Some browsers (ahem, Safari) do an annoying job of never supplying any
				//	information on properties for an object unless it's been attached to the document
				//	tree. Sooooo .... LoadImage now has its very own hidden element to pre-attach images.
				//	Can't set display to 'none' or Safari 2 will still refuse to set properties for dynamically loaded images.
            	var xMagic = document.getElementById('__NPL__LoadImageMagic');
            	if ( ! xMagic )
            	{
        			xMagic = document.createElement('div');
        			xMagic.setAttribute('id', '__NPL__LoadImageMagic');
        			//	Try moving it offscreen instead.
        			xMagic.setAttribute('style', 'left: -5000px');
        			xMagic = NPL.Select(xMagic);
					document.body.appendChild(xMagic.element());
					//	Safari will not handle style operations on an element until it's attached to the document.
					xMagic.width(0).height(0).style('overflow', 'hidden');
        		}
        		else
        		{
        			xMagic = NPL.Select(xMagic);
        		}
				xMagic.element().appendChild(xImage);
            	return new ElementSet(xImage);
            },
            
            Channel: function ()
            {
            	return {
            		Open: function(pURI, pCallback){
            			var xChannel = {};
            			xChannel.xhr = new XMLHttpRequest();
            			xChannel.xhr.onreadystatechange = function () {
            				if ( xChannel.xhr.readyState == 4 )
            				{
            					xChannel.callback(xChannel);
            				}
            			};
            			xChannel.xhr.parent = xChannel;
            			xChannel.uri = pURI;
            			xChannel.callback = pCallback;
            			xChannel.xhr.open('GET', pURI, true);
            			xChannel.xhr.send('');
            			return xChannel;
            		}
            	}
            }(),
            
            Tasks: function ()
            {
            	_tasks = [];
            	setInterval('NPL.Tasks.Reaper()', 60000);
            	return {
            		
            		NewTask: function(pFunction) {
            			var xUID = NPL.NewUID();
            			return _tasks[_tasks.length] = new function (pUID, pFunction) {
	            			this.taskID = pUID;
	            			this.taskFunction = pFunction;
	            			this.taskName = '';
	            			this.taskTimeoutID = 0;
	            			this.taskStatus = 0;	//	0: hasn't run yet; 1: active; 8: dying; 9: dead;
	            			
	            			this.Name = function ( pName )
	            			{
	            				if ( typeof pName == 'undefined' ) { return this.taskName; }
	            				this.taskName = pName;
	            			};
	            			
	            			this.Start = function () { this.Run() };
	            			
	            			this.Stop = function () { this.taskStatus = 7 };
	            			
	            			this.Run = function ()
	            			{
						        if ( this.taskStatus < 7 )
						        {
						            if ( this.taskStatus > 0 )
						            {
						                var xTime = this.taskFunction();
						                if ( xTime > 0 )
						                {
						                    this.taskTimeoutID = setTimeout("NPL.Tasks.Run(" + this.taskID + ")", xTime);
						                }
						                else
						                {
						                    this.End();
						                }
						            }
						            else
						            {
						                this.taskStatus = 1;
						                this.taskTimeoutID = setTimeout("NPL.Tasks.Run(" + this.taskID + ")", 5);
						            }
						        }
	            			};
	            			
	            			this.End = function ()
	            			{
	            				this.taskStatus = 8;
	            				clearTimeout(this.taskTimeoutID);
	            				this.taskStatus = 9;
	            			};
	            			
            			}(xUID, pFunction);
            		},
            		
            		Get: function ( pIdentifier ) {
            			var xReturnValue = null;
            			var x = _tasks.length;
            			while ( x-- )
					    {
					        if ( _tasks[x].taskID )
					        {
					            if ( _tasks[x].taskID == pIdentifier ) { return _tasks[x]; }
					            //  Also check for named tasks.
					            if ( (xReturnValue == null) && (_tasks[x].taskName == pIdentifier) ) { xReturnValue = _tasks[x]; }
					        }
					    }
					    return xReturnValue;
            		},
            		
            		Run: function ( pTaskID ) {
            			var xTask = NPL.Tasks.Get(pTaskID);
            			if ( xTask != null ) { xTask.Run(); }
            		},
            		
            		Reaper: function () {
            			var x = 0, y = 0, z = 0;
            			while ( y + z < _tasks.length )
            			{
            				if ( _tasks[x].taskStatus >= 9 )
            				{
            					delete _tasks[x];
            					_tasks[x] = _tasks[x+1];
            					z++;
            				}
            				else
            				{
            					x++; y++;
            				}
            			}
            			_tasks.length = y;
            		}
            	}
            }()
        }
    }();
}

function ElementSet ( pSelector )
{
	this._id = '';                  //  ID of the elements we're looking for.
    this._class = '';               //  ClassName of the elements we're looking for.
	this._tag = '';                 //  TagName of the elements we're looking for.
	this._elements = [document];    //  Init xMatches to the document element.
	this._matches = 0; this.i = 0; this.x = 0;
	this._iesucks = true;			//	Yes, yes it does.
	this._counter = 0;
	//	IE 8 doesn't understand "with (this)"...
	if ( typeof pSelector == 'object' )
	{
		this._elements = [pSelector];
	}
	else if ( typeof pSelector == 'string' )
	{
		pSelector += ' ';	//	Append a space; used in the for loop below on the last selector.
		for ( this.i = 0; this.i < pSelector.length; this.i++ )
		{
			switch ( pSelector.charAt(this.i) )
			{
				case '#':
				    this._id = pSelector.substring(++this.i, this.i=pSelector.indexOf(' ', this.i));
				    this.i--;
					break;
				case '.':
				    this._class = pSelector.substring(++this.i, this.i=pSelector.indexOf(' ', this.i));
	                this.i--;
					break;
				case ' ':
	                if ( this._id )
	                {
	                    //  Enforce unique element IDs. See also: http://www.w3.org/TR/REC-html40/struct/global.html#h-7.5.2
	                    this._elements = [];
	                    this._matches = document.getElementById(this._id);
	                    if ( this._matches )
	                    {
	                        if ( (!this._tag) || (this._tag && (this._matches.tagName.toLowerCase() == this._tag.toLowerCase())) )
	                        {
	                            this._elements.push(this._matches);
	                        }
	                    }
	                }
	                else if ( this._class )
	                {
	                    //  First get all of the descendant elements of each of the currently-matched
	                    //  elements whose tag match xSearchTag.
	                    this._matches = [];
	                    if ( ! this._tag ) { this._tag = '*'; }
	                    for ( this.x in this._elements )
	                    {
	                    	this._iesucks = this._elements[this.x].getElementsByTagName(this._tag);
	                    	if ( typeof this._iesucks == 'object' )
	                    	{
	                    		//	IE sucks. :-(
	                    		//	See also http://www.codingforums.com/archive/index.php/t-152936.html
	                    		//	and http://www.nczonline.net/blog/2007/12/13/ie-com-reers-its-ugly-head/
	                    		//	Even better, I also apparently can not use "for ( y in _iesucks )",
	                    		//	because Internet Explorer does indeed munch much ass.
	                    		for ( y = 0; y < this._iesucks.length; y++ ) { this._matches.push(this._iesucks[y]); }
	                    	}
	                    	else
	                    	{
	                    		this._matches = this._matches.concat(Array.prototype.slice.call(this._iesucks));
	                    	}
	                    }
	                    //  Now clear out the old _elements heap.
	                    this._elements = [];
	                    //  ...And rebuild it from the new set of matches, where the xSearchClass matches what we're looking for.
	                    for ( this.x in this._matches )
	                    {
	                        if ( this._matches[this.x]['className'] == this._class )
	                        {
	                        	this._elements.push(this._matches[this.x]);
	                        }
	                    }
	                    //  Whew.
	                }
	                else if ( this._tag )
	                {
	                    //  Similar to above.
	                    this._matches = [];
	                    for ( this.x = 0; this.x < this._elements.length; this.x++ )
	                    {
	                    	this._iesucks = this._elements[this.x].getElementsByTagName(this._tag);
	                    	if ( typeof this._iesucks == 'object' )
	                    	{
	                    		for ( y = 0; y < this._iesucks.length; y++ ) { this._matches.push(this._iesucks[y]); }
	                    	}
	                    	else
	                    	{
	                        	this._matches = this._matches.concat(Array.prototype.slice.call(this._iesucks));
	                        }
	                    }
	                    this._elements = this._matches.slice(0);
	                }				
					this._id = this._class = this._tag = '';
					break;
				default:
	                this._tag += pSelector.charAt(this.i);
			}
		}
	}
	//  Do not return the document element.
	if ( (this._elements.length == 1) && (this._elements[0] == document) ) { this._elements = []; }

	/*
	   
	   The following section of code is a little "interesting", but not that bad
	   once you get an idea of what's going on.
	   
	   NPL is lazy -- it won't do anything unless it's asked to. On the other hand,
	   once it's asked to do something, it tries to do that thing as efficiently as
	   possible.
	   
	   So, the first time an ElementSet is created, it sets itself up with a couple
	   of functions -- __get_E_Opacity and __set_E_Opacity -- designed to bootstrap
	   opacity handling.
	   
	   The first time that the opacity() function is called, the bootstrapped functions
	   get called, which in turn determine the required opacity syntax for the host
	   browser. Then they save a copy of the new opacity functions into the NPL object,
	   and finally they overwrite themselves so that the next time they get called,
	   they use the new code tailored to the host browser.
	   
	   From that point on, any new ElementSet objects that get created will find the
	   code stored in the NPL object, and use those functions instead of the bootstrap
	   version.
	   
	*/
	
	if ( typeof NPL.__set_E_Opacity != 'undefined' )
	{
        this.__get_E_Opacity = NPL.__get_E_Opacity;
        this.__set_E_Opacity = NPL.__set_E_Opacity;
    }
    else
    {
    	this.__set_E_Opacity = function(e,o)
    	{
    	    // See if another ElementSet has set this up while this ElementSet
    	    // was instantiated.
    	    if ( typeof NPL.__get_E_Opacity != 'undefined' )
    	    {
    	        this.__get_E_Opacity = NPL.__get_E_Opacity;
    	        this.__set_E_Opacity = NPL.__set_E_Opacity;
    	        if ( typeof o != 'undefined' )
    	        {
    	           NPL.__set_E_Opacity(e,o);
    	        }
    	        else
    	        {
    	           return NPL.__get_E_Opacity(e);
    	        }
    	    }
    	    else
    	    {
                if (e.style)
                {
                    if ( typeof e.style.opacity != 'undefined' )
                    {
                        NPL.__get_E_Opacity = function(e){return e.style ? e.style.opacity ? e.style.opacity : 1 : 1};
                        NPL.__set_E_Opacity = function(e,o){if (e.style){e.style.opacity = o}};
                    }
                    else if ( typeof e.style.MozOpacity != 'undefined' )
                    {
                        NPL.__get_E_Opacity = function(e){return e.style ? e.style.MozOpacity ? e.style.MozOpacity : 1 : 1};
                        NPL.__set_E_Opacity = function(e,o){if (e.style){e.style.MozOpacity = o}};
                    }
                    else if ( typeof e.style.filter != 'undefined' )
                    {
                        NPL.__get_E_Opacity = function(e){if (e.style){var xO=e.style.filter.match(/alpha\(opacity=([0-9]+)\)/);if (xO && xO.length > 1){return xO[1] / 100;}return 1;}};
                        NPL.__set_E_Opacity = function(e,o){if (e.style){e.style.filter = "alpha(opacity=" + o*100 + ")"}};
                    }
                    else
                    {
                        NPL.__get_E_Opacity = function(e){return 1;};
                        NPL.__set_E_Opacity = function(e,o){};
                    }
                    this.__get_E_Opacity = NPL.__get_E_Opacity;
                    this.__set_E_Opacity = NPL.__set_E_Opacity;
                    if ( typeof o != 'undefined' )
                    {
                        NPL.__set_E_Opacity(e,o);
                    }
                    else
                    {
                        return NPL.__get_E_Opacity(e);
                    }
                }
            }
    	};
    	this.__get_E_Opacity = this.__set_E_Opacity;
    }
	
	if ( typeof NPL.__get_E_Style != 'undefined' )
	{
	   this.__get_E_Style = NPL.__get_E_Style;
	   this.__set_E_Style = NPL.__set_E_Style;
	}
	else
	{
	   this.__set_E_Style = function(e,a,v)     //  element, attribute, value
	   {
	       if ( typeof NPL.__get_E_Style != 'undefined' )
	       {
	           this.__get_E_Style = NPL.__get_E_Style;
	           this.__set_E_Style = NPL.__set_E_Style;
	           if ( typeof v != 'undefined' )
	           {
	               NPL.__set_E_Style(e,a,v);
	           }
	           else
	           {
	               return NPL.__get_E_Style(e,a);
	           }
	       }
	       else
	       {
    	       if ( document.defaultView && document.defaultView.getComputedStyle )
    	       {
    	            //  Notes on below:
    	            //  Tested with Firefox 1 -> 3.5. There was a previous note here that Firefox didn't
    	            //	use camelCase, but that appears to be incorrect.
    	            //  (Some versions of) Safari will literally hang if getComputedStyle returns null.
		        	NPL.__get_E_Style = function(e,a){var s = document.defaultView.getComputedStyle(e, null);a=a.replace(/\-(.)/g, function(x, y){return y.toUpperCase()});return s === null ? '' : s[a] ? s[a] : ''};
		        	NPL.__set_E_Style = function(e,a,v){a=a.replace(/\-(.)/g, function(x, y){return y.toUpperCase()});if(typeof e.style!='undefined'){e.style[a] = v}};
    	       }
    	       else if ( e.currentStyle )
    	       {
                    //  Notes on below:
                    //  The anonymous function in replace accepts the string (x) and the first sub string match (y), and then returns
                    //  the string to replace the match with. This is due to IE's (correct) use of camelCase for its css selectors. 
                    //  http://www.quirksmode.org/dom/getstyles.html
                    //  Replace: https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/String/Replace
                    NPL.__get_E_Style = function(e,a){a=a.replace(/\-(.)/g, function(x, y){return y.toUpperCase()});return e.currentStyle[a] ? e.currentStyle[a] : ''};
                    NPL.__set_E_Style = function(e,a,v){a=a.replace(/\-(.)/g, function(x, y){return y.toUpperCase()});e.style[a] = v};
    	       }
    	       else
    	       {
    	           NPL.__get_E_Style = function(e,a){return ''};
    	           NPL.__set_E_Style = function(e,a,v){};
    	       }
    	       this.__get_E_Style = NPL.__get_E_Style;
    	       this.__set_E_Style = NPL.__set_E_Style;
    	       if ( typeof v != 'undefined' )
    	       {
    	           NPL.__set_E_Style(e,a,v);
    	       }
    	       else
    	       {
    	           return NPL.__get_E_Style(e,a);
    	       }
           }
	   };
	   this.__get_E_Style = this.__set_E_Style;
	}
	
	this.__set_E_Onclick = function(e,v) { e.onclick = v; }
	
	this.__get_E_Onclick = function(e) { return e.onclick; }
	
	this.__do_E_Click = function(e) { e.onclick(); }
	
	this.__set_E_Attr = function(e,a,v) { e[a] = v; }
	
	this.__get_E_Attr = function(e,a) { return e.a ? e.a : ''; }
	
	this.__get_E_Html = function(e) { return e.innerHTML; }
	
	this.__set_E_Html = function(e,h) { e.innerHTML = h; }
	
	if ( typeof NPL.__get_E_Class != 'undefined' )
	{
	   this.__get_E_Class = NPL.__get_E_Class;
	   this.__set_E_Class = NPL.__set_E_Class;
	}
	else
	{
	   this.__set_E_Class = function(e,c)
	   {
	       if ( typeof NPL.__get_E_Class != 'undefined' )
	       {
	           this.__get_E_Class = NPL.__get_E_Class;
	           this.__set_E_Class = NPL.__set_E_Class;
	           if ( typeof c != 'undefined' )
	           {
	               NPL.__set_E_Class(e,c);
	           }
	           else
	           {
	               return NPL.__get_E_Class(e);
	           }
	       }
	       else
	       {
	       	   if ( e.getAttribute )
	       	   {
	       	   	   NPL.__get_E_Class = function(e){return e.getAttribute('class')};
	       	   	   NPL.__set_E_Class = function(e,c){e.setAttribute('class', c)};
	       	   }
	       	   else if ( e.className )
	       	   {
	       	       NPL.__get_E_Class = function(e){return e.className};
	       	       NPL.__set_E_Class = function(e,c){e.className=c;}
	       	   }
	       	   else
	       	   {
	       	   	   NPL.__get_E_Class = function(e){return ''};
	       	   	   NPL.__set_E_Class = function(e,c){};
	       	   }
    	       this.__get_E_Class = NPL.__get_E_Class;
    	       this.__set_E_Class = NPL.__set_E_Class;
    	       if ( typeof c != 'undefined' )
    	       {
    	           NPL.__set_E_Class(e,c);
    	       }
    	       else
    	       {
    	           return NPL.__get_E_Class(e);
    	       }
           }
	   };
	   this.__get_E_Class = this.__set_E_Class;
	}

	if ( typeof NPL.__get_E_Loaded != 'undefined' )
	{
	   this.__get_E_Loaded = NPL.__get_E_Loaded;
	}
	else
	{
	   this.__get_E_Loaded = function(e)
	   {
	       if ( typeof NPL.__get_E_Loaded != 'undefined' )
	       {
	           this.__get_E_Loaded = NPL.__get_E_Loaded;
	           return NPL.__get_E_Loaded(e);
	       }
	       else
	       {
	       	   if ( typeof e.complete != 'undefined' )
	       	   {
	       	   	   NPL.__get_E_Loaded = function(e){return e.complete};
	       	   }
	       	   else
	       	   {
	       	   	   NPL.__get_E_Loaded = function(e){return true};
	       	   }
    	       __get_E_Loaded = NPL.__get_E_Loaded;
    	       return NPL.__get_E_Loaded(e);
           }
	   };
	}
		
	delete this._id;
	delete this._class;
	delete this._tag;
	delete this._matches;
	delete this.i;
	
	// Merge in dynamic functions from the prototype for this object.
    for ( this.x in NPL.Prototype.__ElementSet__ )
    {
        this[this.x] = NPL.Prototype.__ElementSet__[this.x];
    }
	delete this.x;
	this._counter = 0;

	//     _for_all_do and _for_all_get are meta functions with horrible syntax
	//     and wonderful utility. Pass them a function as the first parameter and
	//     up to four other parameters, and they'll call the function with the
	//     four parameters on each of the elements in the set.
	this._for_all_do = function(f, a, b, c, d)
	{
	   for ( x in this._elements ) { f(this._elements[x], a, b, c, d); }
	};
	
	this._for_all_get = function(f, a, b, c, d)
	{
	   if ( this._elements.length < 2 ) { return this._elements.length < 1 ? [] : f(this._elements[0], a, b, c, d); }
	   var xReturnValue = [];
	   for ( x in this._elements ) { xReturnValue[x] = f(this._elements[x], a, b, c, d); }
	   return xReturnValue;
	};

    this.length = this._elements.length;
	
    this.element = function ( pElementIndex )
    {
       if ( typeof pElementIndex != 'undefined' ) { this._counter = pElementIndex; }
       if ( this._counter < this._elements.length ) { return this._elements[this._counter++]; }
       if ( this._elements.length == 1 ) { return this._elements[0]; }
       this._counter = 0;
       return null;
    };
	   
    this.elements = function ()
    {
        return this._elements.length > 0 ? this._elements.splice() : [];
    };
    
    this.width = function ( pWidth )
    {
        //  http://www.quirksmode.org/dom/getstyles.html
        if ( typeof pWidth == 'undefined' ) { return this._for_all_get(function(e){return e.offsetWidth}); }
        if ( typeof pWidth == 'number' )
        {
        	if ( pWidth >= 0 ) { this.style('width', pWidth + 'px'); /*this._for_all_do(function(e,w){e.style.width = w}, pWidth + 'px');*/ }
        }
        else
        {
        	//this._for_all_do(function(e,w){e.style.width = w}, pWidth);
        	this.style('width', pWidth);
        }
        return this;
    };
    
    this.height = function ( pHeight )
    {
        if ( typeof pHeight == 'undefined' ) { return this._for_all_get(function(e){return e.offsetHeight}); }
        if ( typeof pHeight == 'number' )
        {
        	if ( pHeight >= 0 ) { this.style('height', pHeight + 'px'); /*this._for_all_do(function(e,h){e.style.height = h}, pHeight + 'px');*/ }
        }
        else
        {
        	// this._for_all_do(function(e,h){e.style.height = h}, pHeight);
        	this.style('height', pHeight);
        }
        return this;
    };
    
    this.left = function ( pLeft )
    {
    	if ( typeof pLeft == 'undefined' ) { return this._for_all_get(function(e){var _left=e.offsetLeft; while((typeof e.offsetParent != 'undefined') && (e=e.offsetParent)){_left += e.offsetLeft}; return _left}); }
    	if ( typeof pLeft == 'number' ) { pLeft = pLeft + 'px'; }
    	this._for_all_do(function(e,l){e.style.left = l}, pLeft);
    };
    
    this.top = function ( pTop )
    {
    	if ( typeof pLeft == 'undefined' ) { return this._for_all_get(function(e){var _top=e.offsetTop; while((typeof e.offsetParent != 'undefined') && (e=e.offsetParent)){_top += e.offsetTop}; return _top}); }
    };

    this.style = function ( pStyleAttr, pStyleValue )
    {
        //  Return (or set) the values for pStyleAttr for each element.
        //  Note that IE will return "auto" for the CSS width of any element
        //  that has not actually had its width set by CSS. You might want to use
        //  the width() function instead.
        if ( typeof pStyleValue == 'undefined' ) { return this._for_all_get(function(e, a, o){return o.__get_E_Style(e, a)}, pStyleAttr, this); }
        this._for_all_do(
        	function(e, a, v, o){
        		o.__set_E_Style(e, a, v)
        	}, pStyleAttr, pStyleValue, this
        );
        return this;
    };
    
    this.classname = function ( pClass )
    //	*sigh* Safari vomits because it considers "class" to be a JavaScript reserved word.
    {
    	if ( typeof pClass == 'undefined' ) { return this._for_all_get(function(e, o){return o.__get_E_Class(e)}, this); }
    	this._for_all_do(function(e,c,o){o.__set_E_Class(e,c)}, pClass, this);
    	return this;
    };
    
    this.id = function ( pID )
    {
    	if ( typeof pID == 'undefined' ) { return this._for_all_get(function(e){return e.getAttribute('id')}); }
    	this._for_all_do(function(e,i){e.setAttribute('id',i)}, pID);
    	return this;
    };
    
    this.set = function ( pAttr, pValue )
    {
    	if ( typeof pValue == 'undefined' ) { return this._for_all_get(function(e,o){return o.__get_E_Attr(e)}, this); }
    	this._for_all_do(function(e,a,v,o){o.__set_E_Attr(e,a,v,o)}, pAttr, pValue, this);
    	return this;
    };
    
    this.opacity = function ( pOpacity )
    {
        if ( typeof pOpacity == 'undefined' ) { return this._for_all_get(function(e,o){return o.__get_E_Opacity(e)}, this); }
        this._for_all_do(function(e,o,o2){o2.__set_E_Opacity(e,o)}, pOpacity, this);
        return this;
    };
    
    this.onclick = function ( pFunction )
    {
    	if ( typeof pFunction == 'undefined' ) { return this._for_all_get(function(e,o){return o.__get_E_Onclick(e)}, this); }
    	this._for_all_do(function(e,v,o){o.__set_E_Onclick(e,v)}, pFunction, this);
    	return this;
    };
    
    this.onload = function ( pFunction )
    {
    	if ( this.isImage() )
    	{
    		if ( this.loaded() )
    		{
    			pFunction(this);
    		}
    		else
    		{
    			var xTask = NPL.Tasks.NewTask(function(){if (this._image.loaded()){this._onloadf(this._image);return 0;}else{return 250;}});
    			xTask._image = this;
    			xTask._onloadf = pFunction;
    			xTask.Start();
    		}
    	}
    	else
    	{
    		pFunction(this);
    	}
    };
    
    this.click = function ()
    {
    	this._for_all_do(function(e,o){o.__do_E_Click(e)}, this);
    	return this;
    };
    
    this.scrollWidth = function ()
    {
    	return this._for_all_get(function(e){return e.scrollWidth});
    };
    
    this.scrollHeight = function ()
    {
    	return this._for_all_get(function(e){return e.scrollHeight});
    };
    
    this.htmlTag = function ()
    {
    	return this._for_all_get(function(e){return e.tagName});
    };
    
    this.innerHtml = function ( pHTML )
    {
    	if ( typeof pHTML == 'undefined' ) { return this._for_all_get(function(e,o){return o.__get_E_Html(e)}, this); }
    	this._for_all_do(function(e,h,o){o.__set_E_Html(e,h)}, pHTML, this);
    	return this;
    };
    
    this.isImage = function ()
    {
    	var xReturnValue = this._for_all_get(function(e){return e.tagName.toLowerCase() == 'img'});
    	if ( typeof xReturnValue == 'boolean' ) { return xReturnValue; }
    	if ( typeof xReturnValue.length != 'undefined' )
    	{
    		for ( var x = 0; x < xReturnValue.length; x++ )
    		{
    			if ( xReturnValue[x] == 'false' ) { return false; }
    		}
    		return true;
    	}
    	return false;
    };
    
    this.loaded = function ()
    {
    	if ( this.isImage() )
    	{
    		var xReturnValue = this._for_all_get(function(e,o){return o.__get_E_Loaded(e)}, this);
    		if ( typeof xReturnValue == 'boolean' ) { return xReturnValue; }
    		if ( typeof xReturnValue.length != 'undefined' )
    		{
    			for ( var x = 0; x < xReturnValue.length; x++ )
    			{
    				if ( xReturnValue[x] == 'false' ) { return false; }
    			}
    			return true;
    		}
    	}
    	return true;
    };
    
    return this;
}

function NoIE6 ()
{
	if ( NPL.Browser.IE6() )
	{
		var xNo6Text = "<h1>Sorry, your browser is not supported</h1>The website developer has gone to great efforts to make this website compatible with as many different web browsers as time and money allowed.<br>\n<br>\nUnfortunately, Internet Explorer 6 is both the most difficult and the most restrictive browser to develop for.<br>\n<br>\nYou are seeing this message because this website uses features which are supported by almost all other web browsers, except for Internet Explorer 6.<br>\n<br>\n<br>\nYour options are:<ul><li>Email the website developer: <a href='mailto:rob@associatedtechs.com'>rob@associatedtechs.com</a></li><br>\n<li><a href='http://getfirefox.com/'>Get Firefox</a>: A faster, more secure, widely-used alternative to Internet Explorer. Enjoy advanced website features, and if you also install <a href='http://adblockplus.org/'>AdBlock Plus</a>, you won't even have to download or view advertising on websites or be tricked again into downloading a virus.</li><br>\n<li>Upgrade to <a href='http://www.microsoft.com/windows/internet-explorer/ie7/'>Internet Explorer 7</a> or <a href='http://www.microsoft.com/windows/Internet-explorer/default.aspx'>Internet Explorer 8</a>.</li><br>\n</ul><br><br>I sincerely apologize for the trouble; unfortunately, it's no longer practical to continue to support this browser. There are widespread efforts underway to get the remaining Internet Explorer 6 users to upgrade.";
		NPL.Select('body').innerHtml(xNo6Text);
		NPL.Select('body').style('padding', '40px').style('font-size', '14px').style('font-family', 'Verdana, Geneva, sans-serif').style('width', '80%').style('height', '80%');
	}
}