NPL.Velvet = (function ()
{
	//	Used in a few places.
	var _mydomain = location.hostname.toLowerCase();
	//	Create a stylesheet governing our UI elements.
	var _stylesheet = document.createElement('style');
	_stylesheet.type = 'text/css';
	var _styles = "\
																				\
		.vmenubar {																\
			position: absolute;													\
			width: 100%;														\
			background-color: white;											\
			border-top: 1px solid gray;											\
			float: left;														\
			clear: both;														\
			height: 30px;														\
			line-height: 30px;													\
			bottom: 0;															\
			font-family: 'garamond-premier-pro-caption', Garamond, Georgia, 'Times New Roman', serif;	\
			font-size: 18px;													\
			font-weight: bold;													\
			z-index: 2;															\
		}																		\
																				\
		#vmenubar_menulist_wrapper {											\
			overflow: hidden;													\
			float: left;														\
			text-align: center;													\
		}																		\
																				\
		ul.vmenubar_menulist {													\
			position: relative;													\
			display: inline;													\
			padding: 0 30px;													\
		}																		\
																				\
		ul.vmenubar_menulist li {												\
			display: inline;													\
		}																		\
																				\
		ul.vmenubar_menulist li a {												\
			display: inline-block;												\
			padding-left: 17px;													\
			padding-right: 17px;												\
			text-decoration: none;												\
			color: #1b2caa;														\
		}																		\
																				\
		#vmenubar_loginout, .vmenubar_signout, .vmenubar_signin {				\
			float: right;														\
			display: inline-block;												\
			height: 100%;														\
			width: 150px;														\
			vertical-align: center;												\
			text-align: center;													\
			border-left: 1px solid #00183e;										\
		}																		\
																				\
		.vmenubar_signout a, .vmenubar_signin a {								\
			padding: 0 30px;													\
			color: #1b2caa;														\
			text-decoration: none;												\
		}																		\
																				\
		.vmenubar_signout a:hover, .vmenubar_signin a:hover {					\
			text-decoration: underline;											\
		}																		\
																				\
		#vmenubar_submenu {														\
			width: 190px;														\
			height: 300px;														\
			position: absolute;													\
			border-radius: 10px 10px;											\
			position: absolute;													\
			background-color: #bd9e71;											\
			bottom: 40px;														\
			left: 10px;															\
			overflow: hidden;													\
		}																		\
																				\
		#vmenubar_submenu ul {													\
			list-style-type: none;												\
			padding: 5px 15px;													\
		}																		\
																				\
		#vmenubar_submenu ul li {												\
			margin-top: 12px;													\
			margin-bottom: 13px;												\
			font-family: 'ratio', Verdana, sans-serif;							\
			font-size: 13px;													\
		}																		\
																				\
		#vmenubar_submenu ul li a {												\
			color: black;														\
			text-decoration: none;												\
		}																		\
																				\
		.vwindow {																\
			position: absolute;													\
			z-index: 5;															\
		}																		\
																				\
		.vwindow_title {														\
			display: inline-block;												\
			position: relative;													\
			top: 6px;															\
			color: white;														\
			font-family: Verdana, sans-serif;									\
			font-size: 12px;													\
			font-weight: normal;												\
			margin-left: 10px;													\
			display: none;														\
		}																		\
																				\
		.vwindow_body {															\
			overflow: hidden;													\
		}																		\
																				\
		.vwindow_sidebar {														\
			display: none;														\
			float: left;														\
			margin: 8px;														\
			background-color: #bd9e71;											\
			font-size: 1em;														\
			border-radius: 15px 0px 0px 15px;									\
			max-width: 200px;													\
		}																		\
																				\
		.vwindow_sidebar ul {													\
			list-style-type: none;												\
			font-size: 1.5em;													\
			margin-left: 8px;													\
		}																		\
																				\
		.vwindow_sidebar ul li {												\
			margin-top: 1em;													\
			margin-bottom: 1em;													\
		}																		\
																				\
		.vwindow_sidebar ul li:hover {											\
			color: #1b2caa;														\
		}																		\
																				\
		.vwindow .draggable:hover {												\
			cursor: url(http://associatedtechs.com/js/npl/development/openhand.cur);\
			cursor: hand;														\
			cursor: grab;														\
			cursor: -moz-grab;													\
			cursor: -webkit-grab;												\
		}																		\
																				\
		.vwindow .dragging:hover {												\
			cursor: url(http://associatedtechs.com/js/npl/development/closedhand.cur);\
			cursor: grabbing;													\
			cursor: -moz-grabbing;												\
			cursor: -webkit-grabbing;											\
		}																		\
																				\
		.vwindow .nodrag:hover {												\
			cursor: auto;														\
		}																		\
																				\
		.vwindow_content {														\
			position: relative;													\
			min-width: 200px;													\
			min-height: 100px;													\
			border-radius: 0px 15px 0px 15px;									\
			background-color: #bd9e71;											\
			padding: 8px 5px 10px 10px;											\
			font-size: 10px;													\
			overflow: hidden;													\
			margin: 8px;														\
			font-family: 'ratio', Verdana, sans-serif;							\
			font-weight: 400;													\
		}																		\
																				\
		.vwindow_content a {													\
			color: #0b0f42;														\
		}																		\
																				\
		.vwindow_content_wrapper {												\
			position: relative;													\
			overflow-y: auto;													\
			overflow-x: hidden;													\
			margin: 7px 7px;													\
			padding-right: 10px;												\
		}																		\
																				\
		.vwindow_dockbtn {														\
			float: right;														\
			margin-top: -7px;													\
			padding-left: 10px;													\
			color: #00183e;														\
			text-decoration: none;												\
			font-size: 10px;													\
			font-family: Verdana, sans-serif;									\
		}																		\
																				\
		.vwindow_buttons {														\
			display: inline-block;												\
			float: right;														\
			padding-bottom: 3px;												\
			position: relative;													\
			top: -3px;															\
			left: -8px;															\
			margin-right: -14px;												\
		}																		\
																				\
		.vwindow_buttons ul {													\
			list-style-type: none;												\
			padding-left: 7px;													\
			padding-right: 7px;													\
		}																		\
																				\
		.vwindow_buttons ul li {												\
			display: inline-block;												\
			margin-left: 7px;													\
			margin-right: 7px;													\
		}																		\
																				\
		.vwindow_buttons ul li a {												\
			font-family: Verdana, sans-serif;									\
			font-size: 12px;													\
			font-weight: bold;													\
			color: #1b2caa;														\
			text-decoration: none;												\
		}																		\
																				\
		.vwindow_buttons ul li a:hover {										\
			text-decoration: underline;											\
		}																		\
																				\
		.vwindow_navbuttons {													\
			position: relative;													\
			top: -8px;															\
			left: -8px;															\
			float: right;														\
			height: 0;															\
			overflow: hidden;													\
		}																		\
																				\
		.vwindow_navbuttons ul {												\
			list-style-type: none;												\
		}																		\
																				\
		.vwindow_navbuttons ul li {												\
			display: inline-block;												\
		}																		\
																				\
		.vwindow_navbuttons ul li a {											\
			display: inline-block;												\
			font-size: 18px;													\
			color: black;														\
			font-family: Baskerville, serif;									\
			padding: 4px 13px;													\
			border: 1px solid gray;												\
			border-radius: 6px;													\
			margin-left: 5px;													\
			margin-right: 7px;													\
			text-decoration: none;												\
		}																		\
																				\
		.vwindow_button_navnext a {												\
			background-color: #80ba30;											\
		}																		\
																				\
		.vwindow_button_navprev a {												\
			background-color: #ba8030;											\
		}																		\
																				\
		#vdock {																\
			position: relative;													\
			width: 120px;														\
			height: auto;														\
			float: right;														\
			left: 10px;															\
			top: 10px;															\
			list-style-type: none;												\
			border: 1px solid #245f8c;											\
			background-color: #052b58;											\
			border-radius: 10px 10px;											\
			display: none;														\
		}																		\
																				\
		#vdock li {																\
			width: 100%;														\
			position: relative;													\
			height: 16px;														\
			padding-bottom: 12px;												\
			color: #245f8c;														\
		}																		\
																				\
		#vdock li:hover {														\
			color: white;														\
		}																		\
																				\
	";
	if ( _stylesheet.styleSheet )
	{
		_stylesheet.styleSheet.cssText = _styles;
	}
	else
	{
		_stylesheet.appendChild(document.createTextNode(_styles));
	}
	document.getElementsByTagName('head')[0].appendChild(_stylesheet);
	
	//	Create the dock.
	var _dock;
	NPL.Page.OnLoad((function(){
		return function(){
			_dock = document.createElement('ul');
			document.body.appendChild(_dock);
			_dock = $(_dock);
			_dock
				.id('vdock')
				.opacity(.7)
				.style('z-index', 90);
		};
	})());
	
	var _activewindows = new Array();			//	Keep track of the active windows in the work area.
	var _haveactivewindow = false;
	
	var _deactivate = function (pWindow)
	{
		var i = _activewindows.length;
		if ( i < 1 ) { return; }
		var xFoundWindow = false;
		if ( typeof pWindow == 'undefined' ) { pWindow = _activewindows[i-1]; }
		//	Remove this window from the list.
		while ( i > 0 )
		{
			if ( _activewindows[--i].uid() == pWindow.uid() )
			{
				_activewindows.splice(i, 1);
				xFoundWindow = true;
				break;
			}
		}
		if ( i < _activewindows.length )
		{
			//	Re-Z the foreground windows.
			var _z = i == 0 ? 5 : 1 + parseInt(_activewindows[i-1].z());
			while ( i < _activewindows.length )
			{
				_activewindows[i].z(_z++);
				i++;
			}
			//	De-nudge the background windows (maybe).
			//	Have to determine if the window has been closed or just backgrounded; maybe
			//	de-nudging ia a job better suited for _activate?
		}
		//	If there are no more active windows left, we have to clear the submenu.
		if ( _activewindows.length == 0 ) { NPL.Velvet.Menubar.clearSubmenu(); }
		_haveactivewindow = false;
		return xFoundWindow;
	}
	
	var _activate = function (pWindow)
	{
		var xTopmostUID = (_activewindows.length == 0 ) ? -1 : _activewindows[_activewindows.length-1].uid();
		if ( typeof pWindow == 'undefined' )
		{
			if ( _activewindows.length < 1 ) { return; }
			pWindow = _activewindows[_activewindows.length-1]
		}
		if ( pWindow.uid() != xTopmostUID || ! _haveactivewindow )
		{
			var xIsNewWindow = ! _deactivate(pWindow);
			//	Send current top-most window to background.
			if ( _activewindows.length > 0 )
			{
				var xWindow = _activewindows[_activewindows.length-1];
				xWindow.select()
					.Fade('begin: ' + xWindow.select().opacity() + '; end: .6; rate: 1.7');
				//xWindow.select('.vwindow_content').style('box-shadow', 'none');
				pWindow.z(1 + parseInt(xWindow.z()));
				//	Nudge background windows if necessary.
				if ( xIsNewWindow && ! pWindow.isModal() )
				{
					for ( var i = _activewindows.length-1; i >= 0; i-- )
					{
						xWindow = _activewindows[i];
						switch ( xWindow.ndir() )
						{
							case 0:
								xWindow.nudge(-20, -20); break;
							case 1:
								xWindow.nudge(-20, 20); break;
							case 2:
								xWindow.nudge(20, -20); break;
							case 3:
								xWindow.nudge(20, 20); break;
						}
					}
				}
			}
			_activewindows.push(pWindow);
			pWindow.select().Fade('begin: ' + pWindow.select().opacity() + '; end: 1; rate: 1.7');
			//pWindow.select('.vwindow_content').style('box-shadow', '0 0 8px 0 #74d2f3');
			//	Load the submenu associated with this window, if one is available,
			//	and if this window wasn't already the topmost window.
			var xSubmenu = pWindow.get('submenu');
			if ( typeof xSubmenu != 'undefined' && (xTopmostUID != pWindow.uid() || ! _haveactivewindow) )
			{
				pWindow.loadSubmenu();
				//NPL.Velvet.Menubar.reloadSubmenu(pWindow.title(), xSubmenu, pWindow.get('submenuCallback'));
			}
		}
		_haveactivewindow = true;
	};

	var _stateCodes = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

	var _normalizeURL = function (pURL)
	{
		//	Takes pURL and returns a URL string with the protocol removed and
		//	the current host removed, if present. Improves Firefox/Chrome compatibility.
		var xURL = pURL;
		var xCropChars = 0;
		//	Have to clean up xURL; in Chrome at least, relative links are rewritten with the current
		//	protocol and hostname and everything else, making things kind of difficult. Damnit.
		if ( xURL.substr(0, 7).toLowerCase() == 'http://' )
		{
			xCropChars = 7;
		}
		else if ( xURL.substr(0, 8).toLowerCase() == 'https://' )
		{
			xCropChars = 8;
		}
		//	Firefox and Chrome do this next part differently; they both include the current hostname, 
		//	but Chrome rewrites the links to include a relative path and Firefox does not. Yay?
		if ( xURL.substr(xCropChars, _mydomain.length).toLowerCase() == _mydomain )
		{
			xCropChars += _mydomain.length;
		}
		//	If the next character in the link is a "/", we can trim that too.
		if ( xURL.substr(xCropChars, 1) == '/' ) { xCropChars++; }
		//	Do the substring crop before continuing with the next step (gets too complicated otherwise).
		xURL = xURL.substr(xCropChars);
		//	If the URL contains a ?, drop it and everything after it.
		//	This is mostly to deal with site-level functions passing parameters for AJAX loading.
		//	(TODO: It would be better to handle this by supporting POST requests for pages and
		//		embedding things like AJAX parameters in the variables there, keeping the URL nice and clean.)
		var xQ = xURL.indexOf('?');
		if ( xQ > -1 ) { xURL = xURL.substr(0, xQ); }
		//	If the last character in the link is a "/", trim that too.
		if ( xURL[xURL.length-1] == '/' )
		{
			return xURL.substr(0, xURL.length - 1);
		}
		else
		{
			return xURL;
		}
	};
	
	return {


		NormalizeURL: function (pURL)
		{
			return _normalizeURL(pURL);
		},


		/**********************************************************************
		*																	  *
		*	Menubar function.											  	  *
		*																	  *
		**********************************************************************/


		Menubar: (function ()
		{
			var _menubar;
			var _menusets = new Array();
			var _currentMenuState = '';
			
			var _menubarInit = function ()
			{
				_menubar = document.createElement('div');
				document.body.appendChild(_menubar);
				_menubar = $(_menubar);
				_menubar
					.opacity(0)
					.classname('vmenubar')
					.innerHtml("<div id=\"vmenubar_menulist_wrapper\"><ul class=\"vmenubar_menulist\"></ul></div><div id=\"vmenubar_loginout\" class=\"vmenubar_signout vmenubar_signin\"><a href=\"javascript:\"></a></div>")
					.select('.vmenubar_signout a')
						.onclick(function(){});
				_menubar.select('#vmenubar_menulist_wrapper').width(NPL.Window.Width() - $('#vmenubar_loginout').width() - 20);
				_menubarInit = function(){};
			};
			
			var _menuItemOnClick = function ()
			{
				//	The .onclick() function used in menubar links.
				this.style.outline = 0;
				_currentMenuState = this.__stateCode;
				if ( this.__hook )
				{
					var xSubmenuItems = this.__callback($(this).innerHtml());
					//	Your callback function here can return false, [], or an
					//	array of submenu items to display.
					//	[] causes no change in the floating submenu;
					//	false causes it to be closed;
					//	a non-empty array replaces the items in the submenu with the contents of the array.
					if ( xSubmenuItems !== false )
					{
						if ( typeof(xSubmenuItems) == 'object' )
						{
							if ( xSubmenuItems.length > 0 )
							{
								this.__vmitems = xSubmenuItems;
								_submenu.load($(this).innerHtml(), this, this.__callback, false);
							}
						}
					}
					else
					{
						_submenu.close();
					}
				}
				else
				{
					_submenu.load($(this).innerHtml(), this, this.__callback, false);
				}
				_currentMenuState = '';
			};

			var _updateSignin = function (pMenuset)
			{
				//	Called by a menuset when its signin (or signout) function changes.
				//	Ignore unless this is the currently active menuset.
				if ( pMenuset._uid() == _menusets[0]._uid() )
				{
					_menubarInit();
					if ( pMenuset._signin() !== false )
					{
						_menubar.select('#vmenubar_loginout')
							.classname('vmenubar_signin')
							.style('display', 'inline-block')
							.select('a')
								.innerHtml('Sign In')
								.onclick(function(){this.style.outline = 0; (pMenuset._signin())()});
					}
					else if ( pMenuset._signout() !== false )
					{
						_menubar.select('#vmenubar_loginout')
							.classname('vmenubar_signout')
							.style('display', 'inline-block')
							.select('a')
								.innerHtml('Sign Out')
								.onclick(function(){this.style.outline = 0; (pMenuset._signout())()});
					}
					else
					{
						_menubar.select('#vmenubar_loginout').style('display', 'none');
					}
				}
			};

			var _updateMenus = function (pMenuset)
			{
				//	Called by a menuset when its menu items changes.
				//	Ignore unless this is the currently active menuset.
				if ( pMenuset._uid() == _menusets[0]._uid() )
				{
					_menubarInit();
					var xMenusetItems = pMenuset._getMenus();
					var xMenubarItems = _menubar.select('.vmenubar_menulist li');
					var xMenubarItem;
					var xMenuItemInsert = function (pPosition, pTitle, pItems, pCallback, pHook)
					{
						var xMenuItem = document.createElement('li');
						if ( pPosition == -1 || pPosition >= _menubar.select('.vmenubar_menulist li').count )
						{
							_menubar.select('.vmenubar_menulist').element().appendChild(xMenuItem);
						}
						else
						{
							_menubar.select('.vmenubar_menulist').element().insertBefore(xMenuItem, _menubar.select('.vmenubar_menulist li').element(pPosition));
						}
						xMenuItem = $(xMenuItem)
							.innerHtml('<a href="javascript:">' + pTitle + '</a>')
							.select('a').onclick(_menuItemOnClick).element();
						xMenuItem.__vmitems = pItems;
						xMenuItem.__callback = pCallback;
						xMenuItem.__hook = pHook;
					};
					//	Compare the items in xMenusetItems to xMenubarItems.
					//	Iterate over xMenubarItems, resolving to xMenusetItems.
					//	At the first discrepancy, we can either 1) insert items from xMenusetItems,
					//	or 2) delete items from xMenubarItems.
					//	If the item exists in xMenusetItems, then count how many items away it is;
					//	more than 2, it's better to just delete it and re-insert it.
					for ( var i = 0; i < xMenubarItems.length; i++ )
					{
						if ( i >= xMenusetItems.length )
						{
							//	Delete remaining items from xMenubarItems and bail.
							xMenubarItems = xMenubarItems.slice(0, ++i);
							break;
						}
						xMenubarItem = $(xMenubarItems.element(i)).select('a');
						if ( xMenubarItem.innerHtml() == xMenusetItems[i].title )
						{
							//	Just do a quick update on the internals for this element in case they've changed.
							xMenubarItem = xMenubarItem.element();
							xMenubarItem.__vmitems = xMenusetItems[i].items;
							xMenubarItem.__callback = xMenusetItems[i].callback;
							xMenubarItem.__hook = xMenusetItems[i].hook;
							xMenubarItem.__stateCode = _stateCodes[i];
						}
						else
						{
							//	More complicated here.
							//	Try to perform a lookahead to see if the next item in the menuset matches this one.
							//	TO-DO: maybe do a second lookahead? Any benefits?
							if ( i + 1 < xMenusetItems.length )
							{
								if ( xMenubarItem.innerHtml() == xMenusetItems[i+1].title )
								{
									//	Fair enough. Insert the current menuset item into the current position in the menubar.
									xMenuItemInsert(i, xMenusetItems[i].title, xMenusetItems[i].items, xMenusetItems[i].callback, xMenusetItems[i].hook);
									//	Refresh the menubar items list.
									xMenubarItems = _menubar.select('.vmenubar_menulist li');
									continue;
								}
							}
							//	Can't lookahead; no more items in the menuset.
							//	Or this menubar item is not the next item in the menuset.
							//	Delete this item from the menubar.
							_menubar.select('.vmenubar_menulist').element().removeChild(_menubar.select('.vmenubar_menulist').element(i));
							xMenubarItems = _menubar.select('.vmenubar_menulist li');
							i--;
						}
					}
					//	Insert remaining items.
					for ( ; i < xMenusetItems.length; i++ )
					{
						xMenuItemInsert(-1, xMenusetItems[i].title, xMenusetItems[i].items, xMenusetItems[i].callback, xMenusetItems[i].hook);
					}
				}
			};

			var _newMenuset = function ()
			{
				var _menuItems = new Array();
				var _signin = false;
				var _signout = false;
				var _uid = NPL.NewUID();

				return {

					_uid: function ()
					{
						return _uid;
					},

					_getMenus: function ()
					{
						return _menuItems;
					},

					_signin: function ()
					{
						return _signin;
					},

					_signout: function ()
					{
						return _signout;
					},

					enablesignin: function (pCallback)
					{
						_signin = pCallback;
						_signout = false;
						//	Tell the menubar that this menuset has changed.
						_updateSignin(this);
					},

					enablesignout: function (pCallback)
					{
						_signin = false;
						_signout = pCallback;
						//	Tell the menubar that this menuset has changed.
						_updateSignin(this);
					},

					addMenu: function (pMenuOptions)
					{
						if ( pMenuOptions.url ) { pMenuOptions.url = _normalizeURL(pMenuOptions.url); }
						_menuItems.push(pMenuOptions);
						//	Tell the menubar that this menuset has changed.
						_updateMenus(this);
					}

				}
			};

			var _submenu = (function()
			{
				var _loadlist = new Array();
				var _state = 0;					//	1: active; 0: closed; -1: closing; 2: loading;
				var _submenu_callback = function(){};
				var _loader = function(pSubmenuModule){
					if ( _loadlist.length > 0 )
					{
						var xSubmenu = $('#vmenubar_submenu');
						var xMenuStateCode = _stateCodes[xSubmenu.select('li').count];
						var xSubmenuItem = document.createElement('li');
						$('#vmenubar_submenu ul').element().appendChild(xSubmenuItem);
						xSubmenuItem = $(xSubmenuItem);
						xSubmenuItem
							.opacity(0)
							.innerHtml('<a href="#">' + _loadlist.shift() + '</a>')
							.Fade('begin: 0; end: .3; rate: 4', function(pElement){
								_loader(pSubmenuModule);
								pElement.Fade('begin: .4; end: 1; rate: 4');
							})
							.select('a')
								.onclick(function(){
									this.style.outline = 0;
									var xSavedStateCode = _currentMenuState;
									_currentMenuState = _currentMenuState + this.__stateCode;
									_submenu_callback((pSubmenuModule + '_' + $(this).innerHtml()).toLowerCase());
									_currentMenuState = xSavedStateCode;
									return false;
								})
								.element()
									.__stateCode = xMenuStateCode;
						if ( xSubmenu.scrollHeight() > xSubmenu.height() ) { xSubmenu.height(xSubmenu.scrollHeight()); }
					}
					else
					{
						_state = 1;
					}
				};
				
				return {
				
					load: function (pMenuTitle, pMenu, pCallback, pIsReload)
					{
						_submenu_callback = pCallback;
						_loadlist = pIsReload ? pMenu.slice(0) : pMenu.__vmitems.slice(0);
						//	Select the submenu element.
						if ( _state != 1 )
						{
							//	Create it if it doesn't exist.
							//	But only if we have items to load.
							if ( _loadlist.length > 0 )
							{
								//	Do this as a task in case the submenu is being
								//	closed right now.
								var xTask = NPL.Tasks.NewTask(function(){
									this.count++;
									if ( this.count > 25 )
									{
										return 0;
									}
									if ( _state != 0 ) { return 200; }
									_state = 2;
									var xSubmenu = $('#vmenubar_submenu');
									xSubmenu = document.createElement('div');
									document.body.appendChild(xSubmenu);
									xSubmenu = $(xSubmenu);
									xSubmenu
										.opacity(0)
										.id('vmenubar_submenu')
										.Fade('begin: 0; end: .9; rate: 1.7', function(){
											_loader(pMenuTitle);
										})
										.innerHtml('<ul></ul>');
									return 0;
								});
								xTask.count = 0;
								xTask.Start();
							}
						}
						else
						{
							_state = 2;
							var xSubmenu = $('#vmenubar_submenu');
							xSubmenu.select('ul')
								.Fade('begin: 1; end: 0; rate: 1.7', function(pElement){
									pElement.select('li').remove();
									pElement.opacity(1);
									_loader(pMenuTitle);
								});
						}
						//	If the submenu stays empty for more than 2 seconds, we should close it.
						//	Gets called here since everything else submenu-related calls load() at some point.
						var xTask = NPL.Tasks.NewTask(function(){
							if ( $('#vmenubar_submenu ul li').count == 0 && _state == 1 ) { _submenu.close(); }
							return 0;
						});
						xTask.Start(2000);
					},

					close: function ()
					{
						if ( _state == 1 )
						{
							_state = -1;
							var xSubmenu = $('#vmenubar_submenu');
							xSubmenu
								.Fade('begin: ' + xSubmenu.opacity() + '; end: 0; rate: 1.7', function(pElement){
									_state = 0;
									pElement.remove();
								});
						}
					}
				}
			})();
			
			_menusets[0] = _newMenuset();

			return {

				_currentStateCode: function ()
				{
					return _currentMenuState;
				},

				newMenubar: function ()
				{
					//	Return a new menuset.
					return _newMenuset();
				},

				pushMenubar: function (pMenubar)
				{
					_menubarInit();
					//	Create a new 'ul' with the new menubar's items.
					var xNewMenubar = document.createElement('ul');
					var xMenuset = pMenubar._getMenus();
					var xMenuItem;
					for ( var i = 0; i < xMenuset.length; i++ )
					{
						xMenuItem = $(document.createElement('li')).innerHtml('<a href="javascript:">' + xMenuset[i].title + '</a>');
						xNewMenubar.appendChild(xMenuItem.element());
						xMenuItem = xMenuItem.select('a').onclick(_menuItemOnClick).element();
						xMenuItem.__vmitems = xMenuset[i].items;
						xMenuItem.__callback = xMenuset[i].callback;
						xMenuItem.__hook = xMenuset[i].hook;
					}
					//	Push the new menubar into the menusets list.
					_menusets.unshift(pMenubar);
					//	Restore the signin/signout functionality (if any).
					if ( _menusets[0]._signin() !== false )
					{
						_menusets[0].enablesignin(_menusets[0]._signin());
					}
					else if ( _menusets[0]._signout() !== false )
					{
						_menusets[0].enablesignout(_menusets[0]._signout());
					}
					//	Slide out the old menubar and slide in the new one.
					_menubar.select('ul').replace($(xNewMenubar), 'slide_left');
				},

				popMenubar: function ()
				{
					if ( _menusets.length > 1 )
					{
						var xReturnValue = _menusets.shift();
						//	Create a new 'ul' with the previous menubar's items.
						var xNewMenubar = document.createElement('ul');
						var xMenuset = _menusets[0]._getMenus();
						var xMenuItem;
						for ( var i = 0; i < xMenuset.length; i++ )
						{
							xMenuItem = $(document.createElement('li')).innerHtml('<a href="javascript:">' + xMenuset[i].title + '</a>');
							xNewMenubar.appendChild(xMenuItem.element());
							xMenuItem = xMenuItem.select('a').onclick(_menuItemOnClick).element();
							xMenuItem.__vmitems = xMenuset[i].items;
							xMenuItem.__callback = xMenuset[i].callback;
							xMenuItem.__hook = xMenuset[i].hook;
						}
						//	Restore the signin/signout functionality (if any).
						if ( _menusets[0]._signin() !== false )
						{
							_menusets[0].enablesignin(_menusets[0]._signin());
						}
						else if ( _menusets[0]._signout() !== false )
						{
							_menusets[0].enablesignout(_menusets[0]._signout());
						}
						//	Slide out the old menubar and slide in the new next-oldest one.
						_menubar.select('ul').replace($(xNewMenubar), 'slide_right');
					}
					else
					{
						var xReturnValue = _menusets[0];
					}
					//	Return the menuset.
					return xReturnValue;
				},

				getMenubar: function ()
				{
					return _menusets[0];
				},

				enablesignin: function (pCallback)
				{
					//	Call the current active menuset's enablesignin() function.
					_menusets[0].enablesignin(pCallback);
				},
			
				enablesignout: function (pCallback)
				{
					//	Call the current active menuset's enablesignout() function.
					_menusets[0].enablesignout(pCallback);
				},

				show: function ()
				{
					_menubarInit();
					_menubar.style('display', 'block');
					_menubar.Fade('begin: ' + _menubar.opacity() + '; end: .8; rate: 1.7');
				},
				
				addMenu: function (pMenuOptions)
				{
					//	title: the text that will be used for the menu item.
					//	items: an array of items to be automatically loaded into the floating submenu when this item is clicked.
					//	callback: a function to call after this item is clicked.
					//	hook: "true" will "hook" the menu, allowing you to dynamically return values for a submenu when this item is clicked.
					//	url: a URL to associate with this menu item.
					//	Call the current active menuset's addMenu() function.
					_menusets[0].addMenu(pMenuOptions);
				},

				reloadSubmenu: function (pMenuTitle, pSubmenuItems, pCallback)
				{
					_menubarInit();
					_submenu.load(pMenuTitle, pSubmenuItems, pCallback, true);
				},

				clearSubmenu: function ()
				{
					_menubarInit();
					_submenu.load('', [], function(){}, true);
				},

				clear: function ()
				{
					_menubarInit();
					_menubar.select('ul').replace($(document.createElement('ul')), 'slide_left');
				},
				
				close: function ()
				{
					_menubarInit();
					_menubar.Fade('begin: ' + _menubar.opacity() + '; end: 0; rate: 1.7', function(pElement){
						pElement.style('display', 'none');
						pElement.select('li').remove();
					});
					_submenu.close();
				}
			}
		})(),
		


		
		/**********************************************************************
		*																	  *
		*	Viewport object.											  	  *
		*																	  *
		**********************************************************************/


		Viewport: (function ()
		{
			var _height = NPL.Window.Height() - $('.vmenubar').height() - 20;
			var _width = NPL.Window.Width() - 100;
			return {
				
				height: function ()
				{
					return _height;
				},

				width: function ()
				{
					return _width;
				}

			};
		})(),





		/**********************************************************************
		*																	  *
		*	Window object.													  *
		*																	  *
		**********************************************************************/


		NewWindow: function (pHTML)
		{
			var _btnFunction = function(){};
			var _dockable = true;
			var _draggable = false;			//	Gets switched on automatically later.
			var _resizable = true;
			var _store = {};
			var _uid = NPL.NewUID();
			var _ndir = _uid % 4;
			var _nudgeHistory = new Array();
			var _stateCode = NPL.Velvet.Menubar._currentStateCode();
			var _baseHref = '';
			var _contentFilter = false;
			
			//	General initialization function for navigational buttons for windows.
			var _navBtnsInit = function(){
				_window.select('.vwindow_navbuttons ul li a').opacity(0);
				_window.select('.vwindow_navbuttons')
					.height(35)
					.style('margin-top', '-38px');
				_navBtnsInit = function(){};
			};
			
			//	Setup function for navigational buttons.
			var _navBtnSetup = function(pEffectSelector){
			
				var _btnType = pEffectSelector;
				var _selector = '.vwindow_button_nav' + _btnType + ' a';
				var _keycode = _btnType == 'next' ? 13 : 27;
				var _btnCallback = function(){return ''};
							
				var _effect = function()
				{
					_window.select('.vwindow_content_wrapper')
						.Slide((_btnType == 'next' ? '-' : '') + (_window.width() + 100) + ', 0, accelerate', function(pElement){
							//	Need to hide and re-position the element back to 0,0 otherwise
							//	it will fuck up the auto-width-adjustment in _htmlContent. :-(
							pElement.opacity(0);
							pElement.left(0);
							_htmlContent(_btnCallback(_btnType));
							pElement.left((_window.width() + 100) * (_btnType == 'next' ? 1 : -1));
							pElement.opacity(1);
							pElement.Slide((_btnType == 'next' ? '-' : '') + (_window.width() + 100) + ', 0, decelerate');
						});
				};
				
				var _onKeydown = function(pEvent)
				{
					if ( pEvent.keyCode == _keycode )
					{
						pEvent.preventDefault()
						_effect();
					}
				};

				return {
					enable: function (pCallback)
					{
						//	Enable a nav button on a window. pCallback is a function to be called
						//	which must return the new contents of the window after the button
						//	is clicked.
						//	Calling enable() without a parameter will cause it to use the last
						//	given pCallback function.
						_navBtnsInit();
						if ( typeof pCallback != 'undefined' ) { _btnCallback = pCallback; }
						var xBtn = _window.select(_selector);
						xBtn
							.style('display', 'inline-block')
							.Fade('begin: ' + xBtn.opacity() + '; end: 1; rate: 1.7')
							.onclick(function(){this.style.outline = 0; _effect()});
						window.addEventListener('keydown', _onKeydown, true);
						return this;
					},
					
					enableFileSelect: function (pCallback)
					{
						_navBtnsInit();
						//	Enable this button with a file upload input. This is tricky to do.
						//	Create a form and input element and position it over this button.
						var _fileInput = document.createElement('form');
						_window.select('.vwindow_navbuttons').element().appendChild(_fileInput);
						_fileInput = $(_fileInput);
						_fileInput
							.style("{position: absolute; z-index: 2; overflow: hidden; margin: 0; padding: 0;}")
							.opacity(0)
							.height(_window.select(_selector).height())
							.width(_window.select(_selector).width())
							.innerHtml("<input type='file'>")
								.select('input')
									.height(_window.select(_selector).height())
									.style("{margin: 0; padding: 0;}");
						//	Now position this form input over this button.
						_fileInput.left(100);
						var xFudgeLeft = _fileInput.left() - 100;
						_fileInput.top(100);
						var xFudgeTop = _fileInput.top() - 100;
						var xAdjLeft = _window.select(_selector).left() - _fileInput.left();
						var xAdjTop = _window.select(_selector).top() - _fileInput.top();
						_fileInput.left(_fileInput.left() + xAdjLeft - xFudgeLeft);
						_fileInput.top(_fileInput.top() + xAdjTop - xFudgeTop);
						_fileInput.element().scrollLeft = _fileInput.element().scrollWidth - _window.select(_selector).width();
						_fileInput.select('input').element().onchange = function()
						{
							this.style.outline = 0;
							_store.selectedFile = _fileInput.select('input').element().cloneNode(true);
							//	_window.getSelectedFile()
							//	Remove the form.
							_fileInput.remove();
							_effect();
						};
						return this;
					},
					
					disable: function (pCallback)
					{
						_navBtnsInit();
						var xBtn = _window.select(_selector);
						xBtn.Fade('begin: ' + xBtn.opacity() + '; end: 0; rate: 1.7');
						window.removeEventListener('keydown', _onKeydown, true);
						return this;
					},
					
					label: function (pLabel)
					{
						_navBtnsInit();
						//	TODO: If this is a file upload button, fix the input overlay position.
						_window.select(_selector).innerHtml(pLabel);
						return this;
					}
				}
			};

			var _htmlContent = function (pContent)
			{
				var xContentWrapper = _window.select('.vwindow_content_wrapper');
				if ( typeof pContent == 'undefined' ) { return xContentWrapper.innerHtml(); }
				xContentWrapper.innerHtml(pContent);
				//	Call the content filter for this window, if one exists, before continuing further.
				//	(The filter may modify the contents of the window.)
				if ( _contentFilter !== false ) { _contentFilter(_publicFunctions); }
				//	Try to automatically resize the window to fit the content, if necessary.
				if ( _resizable )
				{
					if ( xContentWrapper.scrollWidth() > xContentWrapper.width() )
					{
						if ( xContentWrapper.scrollWidth() + 20 < NPL.Velvet.Viewport.width() )
						{
							xContentWrapper.width(xContentWrapper.scrollWidth() + 20);
						}
						else
						{
							xContentWrapper.width(NPL.Velvet.Viewport.width());
						}
					}
					//	See if further increasing the width of the element will do much for its height,
					//	up to about 5/12ths of the available viewing area.
					var xOrigHeight, xLastWidth;
					while ( xContentWrapper.width() < NPL.Velvet.Viewport.width() * 5 / 12 )
					{
						xOrigHeight = xContentWrapper.scrollHeight();
						xLastWidth = xContentWrapper.width();
						xContentWrapper.width(xContentWrapper.width() + 30);
						if ( xOrigHeight - xContentWrapper.scrollHeight() < 15 )
						{
							//	Need to return element to previous width here.
							xContentWrapper.width(xLastWidth);
							break;
						}
					}
					if ( xContentWrapper.scrollHeight() > xContentWrapper.height() )
					{
						if ( xContentWrapper.scrollHeight() > (NPL.Velvet.Viewport.height() - 110) )
						{
							xContentWrapper.height(NPL.Velvet.Viewport.height() - 110);
							//	The window will probably need to be re-adjusted for width due to the
							//	vertical scroll bar.
							if ( xContentWrapper.scrollWidth() > xContentWrapper.width() )
							{
								xContentWrapper.width(xContentWrapper.scrollWidth());
							}
							xContentWrapper.style('margin-right', '0');
						}
						else
						{
							xContentWrapper.height(xContentWrapper.scrollHeight());
						}
					}
				}
				//	Re-position the window if necessary.
				//	If the top of the window + the window's height > viewport area...
				//	...also left + width
				if ( _window.top() + _window.height() > NPL.Velvet.Viewport.height() )
				{
					_window.top(Math.max(NPL.Velvet.Viewport.height() - _window.height() - 20, 0));
				}
				if ( _window.left() + _window.width() > NPL.Velvet.Viewport.width() )
				{
					_window.left(NPL.Velvet.Viewport.width() - _window.width() + 100);
				}
				//	Scan for links in the html content and rewrite their onclick handlers (if they don't
				//	already have one) to correctly handle a windowed environment.
				var xLinkElements = xContentWrapper.select('a[href]');
				var xLinkElement, xLinkHref;
				var xMyDomain = location.hostname.toLowerCase();
				var xCroppedLink;
				for ( var i = 0; i < xLinkElements.count; i++ )
				{
					xLinkElement = $(xLinkElements.element(i));
					xCroppedLink = xLinkHref = _normalizeURL(xLinkElement.href());
					//	Compare this against the window's base href.
					if ( xLinkHref.substr(0, _baseHref.length) == _baseHref )
					{
						//	Link is relative to this window. Crop this part of the link and see what's next.
						xCroppedLink = xLinkHref.substr(_baseHref.length);
						//	Might need to trim a leading '/' again.
						if ( xCroppedLink[0] == '/' ) { xCroppedLink = xCroppedLink.substr(1); }
					}
					//	If the link is to an anchor in the same content, then it's safe to ignore it.
					if ( xLinkHref[0] == '#' || xCroppedLink[0] == '#' ) { continue; }
					//	Open all other links in a new tab.
					xLinkElement.setAttr('target', '_' + NPL.NewUID());
					//	Excellent. All set to here now.
					//	Next step is to compare xLinkHref against menubar items that might be responsible for handling it,
					//	followed by windows that might be responsible for handling it, and then finally treating it as
					//	an external link to be opened in a new tab.
					xLinkElement.onclick((function(){
						var _linkHref = xLinkHref;
						var _link = xLinkElement;
						return function () {
							var xCandidate = false;
							var xCandidateScore = 0;
							var xCandidateType = 'window';
							var x;
							//	1: try to find a best-candidate window whose base href is close to this link's URL.
							for ( var i = 0; i < _activewindows.length; i++ )
							{
								x = NPL.PercentStrCompare(_linkHref, _activewindows[i].baseHref(), false);
								if ( x > xCandidateScore )
								{
									xCandidateScore = x;
									xCandidate = _activewindows[i];
								}
							}
							//	2: Next, try each menu item. We want to prefer existing windows over menu
							//	items, so if a candidate window has been found, we'll make it harder to
							//	select a menu item instead.
							if ( xCandidateScore > 0 ) { xCandidateScore += .1; }
							if ( xCandidateScore < 1 )
							{
								var xMenus = NPL.Velvet.Menubar.getMenubar()._getMenus();
								for ( i = 0; i < xMenus.length; i++ )
								{
									if ( xMenus[i].url )
									{
										x = NPL.PercentStrCompare(xMenus[i].url, _linkHref, false);
										if ( x > xCandidateScore )
										{
											xCandidateType = 'menu';
											xCandidate = i;
											xCandidateScore = x;
										}
									}
								}
							}
							if ( xCandidateScore > 0 )
							{
								if ( xCandidateType == 'menu' )
								{
									$($('ul.vmenubar_menulist li a').element(xCandidate)).click();
								}
								else if ( xCandidateType == 'window' )
								{
									xCandidate.show();
									xCandidate.contentFromURL(_link.href());
								}
								return false;
							}
							//	3: Finally, allow the link to be opened in a new tab.
							return true;
						}
					})());
				}
				//	Automatically focus on any input elements in the window.
				var xInputElements = xContentWrapper.select('input');
				var xInputElement;
				for ( var i = 0; i < xInputElements.count; i++ )
				{
					xInputElement = xInputElements.element(i);
					if ( xInputElement.type == 'text' || xInputElement.type == 'password' )
					{
						xInputElement.focus();
						break;
					}
				}
			};
			
			var _dragFunction = function (pEvent)
			{
				if ( pEvent !== undefined )
				{
					pEvent.preventDefault();
					pEvent.stopPropagation();
				}
				_publicFunctions.show();
				$(this).addClass('dragging');
				//	Create a task to track mouse movements with this
				//	window.
				_store.mousetask = NPL.Tasks.NewTask(function(){
					var x = NPL.Mouse.x();
					var y = NPL.Mouse.y();
					this._element.move(x - this._x, y - this._y);
					this._x = x;
					this._y = y;
					return 40;
				});
				_store.mousetask._element = _window;
				_store.mousetask._x = NPL.Mouse.x();
				_store.mousetask._y = NPL.Mouse.y();
				_store.mousetask.Start();
				_store.grabbedElement = $(this);
				window.onmouseup = function(pEvent)
				{
					window.onmouseup = function(){};
					if ( pEvent !== undefined ) { pEvent.preventDefault(); }
					_window.select('.vwindow_title_active').classname('vwindow_title');
					_store.mousetask.Stop();
					_store.grabbedElement.removeClass('dragging');
				};
			};
			
			var _draggableF = function (p)
			{
				if ( typeof p === undefined ) { return _draggable; }
				if ( p != _draggable )
				{
					if ( p )
					{
						_draggable = true;
						//	Set up the dragging action.
						_window.select('.vwindow_title').addClass('draggable');
						_window.select('.vwindow_body').addClass('draggable');
						_window.select('.draggable').onmousedown(_dragFunction);
					}
					else
					{
						_draggable = false;
						_window.select('.draggable')
							.onmousedown(function(){})
							.removeClass('draggable');
					}
				}
			};
			
			//	Create the window.
			var _window = document.createElement('div');
			document.body.appendChild(_window);
			_window = $(_window);
			_window
				.opacity(0)
				.classname('vwindow')
				.innerHtml("\
					<h1 class=\"vwindow_title\"></h1>							\
					<div class=\"vwindow_body\">								\
						<div class=\"vwindow_sidebar\"></div>					\
						<div class=\"vwindow_content\">							\
							<a href=\"#\" class=\"vwindow_dockbtn nodrag\">&rsaquo;&rsaquo;</a>\
							<div class=\"vwindow_content_wrapper nodrag\">		\
							</div>												\
    					</div>													\
    					<div class=\"vwindow_navbuttons nodrag\">				\
    						<ul>												\
    							<li class=\"vwindow_button_navprev\"><a href=\"javascript:\">Back</a></li>	\
    							<li class=\"vwindow_button_navnext\"><a href=\"javascript:\">Next</a></li>	\
    						</ul>												\
    					</div>													\
						<div class=\"vwindow_buttons nodrag\">					\
						</div>													\
					</div>														\
				")
				.top((NPL.Window.Height() - _window.height() - 180) / 2)
				.left((NPL.Window.Width() - _window.width()) / 2);
			_htmlContent(pHTML);
			_window.select('.vwindow_content_wrapper')
				.width(_window.select('.vwindow_content_wrapper').width())
				.height(_window.select('.vwindow_content_wrapper').height());
			//	Set up the action for the docking button.
			_window.select('.vwindow_dockbtn').onclick(function(){
				//	Save this window's size and position settings.
				_store._beforeDock = {
					left: _window.left(),
					top: _window.top(),
					width: _window.width(),
					height: _window.height()
				};
				_deactivate(_publicFunctions);
				_activate();
				//	Roll up the window body.
				var xTask = NPL.Tasks.NewTask(function(){
					if ( this._element.height() <= 35 )
					{
						this._element.height(0);
						return 0;
					}
					this._element.height(this._element.height() - 35);
					return 50;
				});
				xTask._element = _window.select('.vwindow_body');
				xTask.Start();
				//	Display the dock if it is currently empty.
				if ( _dock.select('li').count == 0 ) { _dock.style('display', 'block'); }
				//	Create a spot in the dock for this item.
				var xDockSpot = document.createElement('li');
				_dock.element().appendChild(xDockSpot);
				xDockSpot = $(xDockSpot);
				//	Slide the window into its position in the dock.
				_window.Slide(xDockSpot.left() - _window.left() + ', ' + (xDockSpot.top() - _window.top()) + ', decelerate', function(pElement){
					//	Store the window's last x-y coordinates before being attached to the dock element.
					_store._dockCoordinates = { top: _window.top(), left: _window.left() };
					//	Attach this to the list item element once it's done moving into place.
					_store._dockPosition.element().appendChild(_window.element());
					_window.left(0);
					_window.top(0);
				});
				//	Save the list element associated with this window.
				_store._dockPosition = xDockSpot;
				//	Set an onclick event for the window's titlebar to restore it to normal.
				_window.select('.vwindow_title').onclick(function(){
					//	Restore the window to its previous state.
					_window.select('.vwindow_body').height(_store._beforeDock.height);
					_window.select('.vwindow_body').width(_store._beforeDock.width);
					//	Immediately re-attach the window element to the document to prevent some
					//	funky visual artifacts during the slide action.
					_window.top(_store._dockCoordinates.top);
					_window.left(_store._dockCoordinates.left);
					document.body.appendChild(_window.element());
					_window.Slide((_store._beforeDock.left - _window.left()) + ', ' + (_store._beforeDock.top - _window.top()) + ', decelerate', function(pElement){
						//	Fix the position back to original.
						_window.left(_store._beforeDock.left);
						_window.top(_store._beforeDock.top);
						_activate(_publicFunctions);
						//	Remove the dock position associated with this window.
						_store._dockPosition.remove();
						//	Hide the dock if it is empty.
						if ( _dock.select('li').count == 0 ) { _dock.style('display', 'none'); }
					});
					//	Remove the onclick event from the window's titlebar.
					_window.select('.vwindow_title').onclick(function(){});
				});
			});
			//	Init dragging.
			_window.select('.nodrag').onmousedown(function(pEvent){if ( pEvent !== undefined ) { pEvent.stopPropagation()}});
			_draggableF(true);
						
			var _publicFunctions = {

				isModal: function ()
				{
					return false;
				},
				
				uid: function ()
				{
					return _uid;
				},
				
				ndir: function ()
				{
					return _ndir;
				},

				get: function (pTag)
				{
					return _store[pTag];
				},

				set: function (pTag, pData)
				{
					_store[pTag] = pData;
					return this;
				},

				title: function (pTitle)
				{
					if ( typeof pTitle == 'undefined' ) { return _window.select('.vwindow_title').innerHtml(); }
					_window.select('.vwindow_title').innerHtml(pTitle);
					return this;
				},

				baseHref: function (pURL)
				{
					//	This function / private variable is used when rewriting links in window content.
					if ( typeof pURL == 'undefined' ) { return _baseHref; }
					_baseHref = _normalizeURL(pURL);
					return this;
				},
				
				show: function ()
				{
					_activate(this);
					return this;
				},
				
				buttons: function (pButtons, pCallback)
				{
					_btnFunction = pCallback;
					_window.select('.vwindow_buttons').innerHtml(
						'<ul><li><a href="javascript:">' + pButtons.join('</a></li><li><a href="javascript:">') + '</a></li></ul>'
					);
					_window.select('.vwindow_buttons ul li a').onclick(function(){
						this.style.outline = 0;
						_btnFunction(this.textContent);
					});
					return this;
				},
				
				nav: {
					next: _navBtnSetup('next'),
					prev: _navBtnSetup('prev')
				},

				sidebar: {
					load: function (pItems)
					{
						var xHtml = '';
						var xFunctions = new Array();
						var i = 0;
						for ( var xItem in pItems )
						{
							xHtml += '<li class="nodrag">' + xItem + '</li>';
							xFunctions[i++] = pItems[xItem];
						}
						_window.select('.vwindow_sidebar').innerHtml('<ul>' + xHtml + '</ul>');
						_window.select('.vwindow_sidebar ul li').onmousedown(function(pEvent){if ( pEvent !== undefined ) { pEvent.stopPropagation()}});
						var xItems = _window.select('.vwindow_sidebar ul li');
						for ( i = 0; i < xFunctions.length; i++ )
						{
							xItems.element(i)._clickFunction = xFunctions[i];
						}
						xItems.onclick(function(){
							this._clickFunction(_publicFunctions.title() + '_' + ($(this).innerHtml()));
						});
					},

					show: function ()
					{
						_window.select('.vwindow_sidebar').style('display', 'inline-block');
					},

					close: function ()
					{
						_window.select('.vwindow_sidebar').style('display', 'none');
					}
				},
				
				getSelectedFile: function ()
				{
					if ( typeof _store.selectedFile !== 'undefined' ) { return _store.selectedFile; }
					return false;
				},
				
				content: function (pContent)
				{
					if ( typeof pContent === 'undefined' ) { return _window.select('.vwindow_content_wrapper'); }
					_htmlContent(pContent);
					return this;
				},

				contentFromURL: function (pURL, pFilter, pCallback)
				{
					//	Create a task to load pURL in the background,
					//	let the calling code filter it with pFilter if given,
					//	update the baseHref of the current window, and load
					//	the content into the window.
					NPL.Channel.Open(pURL, function(pChannel){
						var xContent = (pFilter === undefined || pFilter === false) ? pChannel.xhr.responseText : pFilter(pChannel.xhr.responseText);
						_baseHref = _normalizeURL(pURL);
						_htmlContent(xContent);
						if ( pCallback !== undefined && pCallback !== false ) { pCallback(_publicFunctions); }
					});
				},

				contentFilter: function (pFunction)
				{
					//	Set or retrieve a function called any time new content is loaded into this window.
					if ( typeof pFunction === 'undefined' ) { return _contentFilter; }
					_contentFilter = pFunction;
					return this;
				},
				
				select: function (p)
				{
					return typeof p == 'undefined' ? _window : _window.select(p);
				},
				
				z: function (p)
				{
					if ( typeof p == 'undefined' ) return _window.style('z-index');
					_window.style('z-index', p);
					return this;
				},
				
				dockable: function (p)
				{
					if ( typeof p === undefined ) { return _dockable; }
					if ( p != _dockable )
					{
						if ( p )
						{
							_dockable = true;
						}
						else
						{
							_dockable = false;
							_window.select('.vwindow_dockbtn').style('display', 'none');
						}
					}
					return this;
				},
				
				resizable: function (p)
				{
					if ( typeof p === undefined ) { return _resizable; }
					if ( p != _resizable )
					{
						_resizable = p ? true : false;
					}
					return this;
				},

				draggable: function (p)
				{
					_draggableF(p);
					return this;
				},

				nudge: function (pX, pY)
				{
					_window.Slide(pX + ', ' + pY + ', decelerate');
					_nudgeHistory.push(-pY);
					_nudgeHistory.push(-pX);
					return this;
				},

				denudge: function ()
				{
					_window.Slide(_nudgeHistory.pop() + ', ' + _nudgeHistory.pop() + ', decelerate');
					return this;
				},

				loadSubmenu: function (pMenuItems, pCallback)
				{
					//	Calling loadSubmenu() through a window allows Velvet to associate a set of
					//	submenu items with the window. Any time the window is brought into the
					//	foreground, the submenu items will be restored along with it.
					if ( typeof pMenuItems != 'undefined' )
					{
						_store['submenu'] = pMenuItems;
						_store['submenuCallback'] = pCallback;
						NPL.Velvet.Menubar.reloadSubmenu(this.title(), pMenuItems, pCallback);
					}
					else
					{
						NPL.Velvet.Menubar.reloadSubmenu(this.title(), _store['submenu'], _store['submenuCallback']);
					}
					return this;
				},

				close: function ()
				{
					_deactivate(this);
					_activate();
					_window.Fade('begin: ' + _window.opacity() + '; end: 0; rate: 1.7', function(){
						_window.remove();
					});
				},
				
				delayclose: function ()
				{
					var xCloseTask = NPL.Tasks.NewTask(function(){
						this._window.close();
						return 0;
					});
					xCloseTask._window = this;
					xCloseTask.Start(_window.select('.vwindow_content_wrapper').element().textContent.length * 40);
				}
				
			};
			
			return _publicFunctions;
		},
		



		/**********************************************************************
		*																	  *
		*	Modal Window object.											  *
		*																	  *
		**********************************************************************/


		NewModalWindow: function (pHTML)
		{
			//	Modal windows inherit most of the same traits as regular windows,
			//	but they automatically position themselves in the center of the
			//	screen and they require user interaction before the user can click
			//	on anything else in the screen.
			var _window = NPL.Velvet.NewWindow(pHTML);
			//	Modify the class of the window using a nifty hack.
			//	_window.select('') will return the internal window. :-)
			_window.select('').classname(_window.select('').classname() + ' vmodal');
			//	Make the window not dockable.
			_window.dockable(false);
			//	Make the window not draggable.
			_window.draggable(false);
			//	Create the overlay.
			var _overlay = document.createElement('div');
			document.body.appendChild(_overlay);
			_overlay = $(_overlay);
			_overlay
				.opacity(0)
				.style("{\
					position: absolute;											\
					z-index: 98;												\
					background-color: #00183e;									\
					display: none;												\
				\}")
				.top(0)
				.left(0)
				.height(NPL.Window.Height())
				.width(NPL.Window.Width());
			
			return {

				isModal: function()
				{
					return true;
				},
				
				uid: _window.uid,
				get: _window.get,
				set: _window.set,
				title: _window.title,

				show: function()
				{
					_overlay
						.style('display', 'block')
						.Fade('begin: 0; end: .9; rate: 1.7');
					_activate(this);
					_window.z(99);
					return this;
				},

				dockable: function (pDockable)
				{
					return typeof pDockable === 'undefined' ? false : this;
				},

				resizable: _window.resizable,
				buttons: _window.buttons,
				nav: _window.nav,
				content: _window.content,
				select: _window.select,
				z: _window.z,

				close: function ()
				{
					_overlay.Fade('begin: ' + _overlay.opacity() + '; end: 0; rate: 1.7', function(){
						_overlay.remove();
					});
					_window.close();
					return this;
				},

				delayclose: _window.delayclose
				
			}
		},



		
		/**********************************************************************
		*																	  *
		*	FindWindow functions.											  *
		*																	  *
		**********************************************************************/


		FindWindow: function (pWindowTitle)
		{
			pWindowTitle = pWindowTitle.toLowerCase();
			var i = _activewindows.length;
			while ( --i >= 0 )
			{
				if ( _activewindows[i].title().toLowerCase() == pWindowTitle ) { return _activewindows[i]; }
			}
			return false;
		},

		FindWindowHref: function (pWindowHref)
		{
			pWindowHref = pWindowHref.toLowerCase();
			var i = _activewindows.length;
			while ( --i >= 0 )
			{
				if ( _activewindows[i].baseHref() == pWindowHref ) { return _activewindows[i]; }
			}
			return false;
		}
		
	};
})();
