/* JSLint Global Declerations */
/* These declerations are global tracking variables, they should eventually be moved to classes */
/*global prevPagingInfo, jsonData, pagingModuleRained, NavHeight, cache, moduleSize, mainDivMargin, NavAccordion */

/* These declerations are for text paging, and may not be necessary anymore */
/*global currentPageIndex */

/* Global Declerations for Mootools */
/*global Fx, Class, $$, $, Event, Request, Element */

/* Global Classes for the Method.com site */
/*global Location, LocationObj, ModulesManager, ModulesManagerObj, OverlayObj, Matrix, PageObj */

/* Global helper functions for the Method.com site */
/*global createCSSRule, initFooter, initNav, sizeMain, slideIn, console, extractCopy, fadeIn, fadeOut, getCache, pages, setCache, slideOut, transitionColl */

/* Globals */
var _gaq = _gaq || []; //google analytics asynchronous queue
var prevPagingInfo; 
var jsonData 			= { "Modules": [] };
var pagingModuleRained 	= false;
var NavHeight 			= new Fx.Tween('Left', 'height'); //--- We can now attach tweens to elements, we should do that instead of this.
var currentPageIndex 	= 0;
//var num_pages 			= 1;
var cache 				= {};
var moduleSize 			= {"width" : 193, "height": 171 }; 
var mainDivMargin 		= 218;

//--- Hide the Main Div
createCSSRule("#Main .module", { "visibility" : "hidden" });

if (!window.console) {
	window.console = { log: function () {}, error: function () {}, info: function () {}};
}

//--- Module repopulation is the act of transitioning the modules that exist now out, and a new set of modules in.
var RepopulationObj = new Class({
									"request" : null,
									"onRepopulateComplete" : null,
									"initialize": function() {
										this.attachEvents();
									},
									"setLocationHandler" : function(ev) {
										
										window.Location.setLocation( this.rel || this.get('rel-location') || this.getProperty("href") );
										
										if( typeof _gaq !== 'undefined')
											_gaq.push(['_trackPageview', this.rel || this.get('rel-location') || this.getProperty("href")]);
									  
										new Event(ev).stop();
									},
									"attachEvents" : function() { //--- This is a function because it is called again later.
										var that = this;
																				
										//--- Do your initialization thing.
										$$("a.internal, li.internal a, .sub-menu .menu-item a")
											.addEvent("click", this.setLocationHandler);

										
										$$("a.body").forEach(function(item, index) {
											item.getAncestor("module")
												.set('rel-location', item.rel)
												.addEvent("click", that.setLocationHandler)
												.addEvent("mouseover", onHoverModuleOver )
												.addEvent("mouseout", onHoverModuleOut);
										});
										
										$$(".jobs")
											.addEvent("mouseover", onHoverModuleOver )
											.addEvent("mouseout", onHoverModuleOut);
												
										//--- Close accordion on moues over. Not in attachEvents to prevent multi-assignings.
										$("Main")
											.addEvent("mouseover", this.onMainMouseOver);
											
									},
									"onMainMouseOver" : function(ev) {
											
											// help IE6 clear the nav when mouseover anything other than #Left div
											NavAccordion.display(-1);
									},
									"RepopulateModules" : function(target) {
										var self = this;
										var originalTarget = target;
										
										target = target.TrimEndChar('/'); 
										
										//--- When requesting the home modules, we need to specify /home/index instead of blank.
										if(target === "") { target = "/home/all"; }										
										
										//--- If Data isn't present, append it. We'll always need it anyway.
										// not anymore! HA!
										//if(target.indexOf("/data") !== 0) { target = "/data" + target; }
										
										//--- Get new Modules
										this.request = new Request({
											url 		: target+'/xhr/',
											method 		: "get",
											evalScripts	: true,
											onComplete 	: function(response) {
												var temp = new Element("DIV");
												temp.set( 'html', response);
												var main = temp.getFirst();
												main.getChildren().setStyles({ "opacity" : 0, "visibility" : "hidden" });
												//--- Normally transition the modules out.
												var modules = extractCopy($("Main").getChildren());
												transitionColl(
													modules, 	//--- All the modules except the copy
													slideOut, 								//--- Slide them out
													function () {
														//--- I should remove any events here.
													
														//console.info("removeEvent");
													
														$("Main")
															.removeEvent("mouseover", self.onMainMouseOver)
															.dispose();
													
														main.injectAfter($("Left"));
												
														if(self.onRepopulateComplete) {
															self.onRepopulateComplete(originalTarget);	
														}
													
														//--- Why doesn't refresh modules do this?
														self.attachEvents();
												});
											}
										});
										//--- Execute
										this.request.send();
									}
								
								});

var ModulesManagerObj = new Class({
									"resizeTimerID" : null,
									"repopulationObj" : null,
									"initialize" : function(repopulationObj) {
										var mainDiv = $("Main");
										var loc = window.Location.getLocation();
										
										//--- The modules Manager uses the Repopulation Module to perform actions. 
										this.repopulationObj = repopulationObj;

										

										if(mainDiv === null || window.Location.isSpecial() ) { //--- There is no div containing the modules, so do nothing.
											//--- I forget why this happens, but it is necessary.
											
											if(mainDiv) {
												mainDiv.show();
												if(!Browser.Engine.webkit){
													mainDiv.getChildren().show();
												} else {
													mainDiv.getChildren().each(function(el){
														el.show();
													});
												}
												return;
											}
											
										
											if(window.Location.isJobs()) {
												this.attachEvents();
												this.RefreshModules();
											}
											return;
										} 	
										
										if( window.Location.isHome() ) {											
											//--- Refresh the Modules collection, and slide-in the new modules.
											this.RefreshModules(slideIn);
										} else if(window.Location.isOverlay(loc)) {
											var overlayModules = window.Location.GetModuleSet( loc ); //"/thoughts/posts/"; // window.Location.getModuleSet();

											//--- If we are on the basepage, just show the modules, since they are always downloaded by default at the start.
											if(overlayModules === "/") {
												this.RefreshModules();
											} else {
												//--- Otherwise, keep the default modules hidden, and show the modules that accompany this overlay.
												this.repopulationObj.RepopulateModules( overlayModules );
											}
										}
									
                    this.attachEvents();                   
									},
									"attachEvents" : function() {
										var parent = this;
										
										//--- Wire up events	
										window.addEvent("resize",  function() {
											if(parent.resizeTimerID) {
												clearTimeout(parent.resizeTimerID);
												parent.resizeTimerID = null;
											}
											
											var cachedDim 	= {"y" : getCache("prevWinHeight"), "x" : getCache("prevWinWidth") 	};
											var currDim 	= {"y" : window.getHeight(), 		"x" : window.getWidth() 		};
											
											//--- Cache the window sizes if they aren't already stored.
											if(!cachedDim.x || !cachedDim.y) {
												setCache("prevWinHeight", currDim.y);
												setCache("prevWinWidth", currDim.x);
											}
											
											//--- If nothings changed since the last resize event, don't change the modules.
											if(cachedDim.y === currDim.y && cachedDim.x === currDim.x) {
												return; 
											}
											
											//--- Lets use a bind call here.
											parent.resizeTimerID = setTimeout( function() {
																							parent.RefreshModules(fadeIn);
																						}, 150);
										});
									},
									//--- Refresh Modules is a bad name, this is really--Make sure the correct modules are shown, and if not show them.
									"RefreshModules" : function(transition) {
										
										var mainDiv = $("Main");
										transition = transition || fadeIn;
										var parent = this;
										
										var matrix = new Matrix();

										//--- Get all the modules except for the paging module.
										var modules = this.GetModulesArray();
										
										//--- Recalculate pages
										pages = PageObj.BuildPages(modules, matrix);
										
										//--- If we navigated to page 5, then loaded a new set of modules that only had 2 pages, we should load page 2, not page 5.
										if(currentPageIndex >= pages.length) {
											currentPageIndex = pages.length-1;
										}

										//--- If the matrix is the same as before the refresh,don't worry about re-drawing modules.
										if(!this.PageHasChanged(pages, matrix)) { 
												return;
										}
										
										//--- Just to be sure.
										pages.HideAllModulesOnOtherPages(currentPageIndex);
										
										//--- Draw the current page
										pages.Get(currentPageIndex).Draw(fadeOut, transition, function() {
												//--- onComplete of drawPage
												pages.PagingModule.Toggle();
											});
										
										setCache("prevMatrix", matrix);
										setCache("prevPages", pages);
									},
									"GetModulesArray" : function(){
										var modulesArray = [];
										var kids = $("Main").getChildren();
											kids.forEach(function(item, index) {
												//--- Don't do the paging module or anything thats not a module.
												if(item.hasClass("module") && item.id !== "pagingModule") {
													modulesArray.push(item);
												}
											});

										return modulesArray.reverse();//--- We deal with the array of modules in reverse, so we take the last one and use it as the first. (Faster on the array to shrink from the back, vs the front)
									},
									"PageHasChanged" : function(pages, matrix) {
										
										//--- Get old matrix
										var cachedMatrix = getCache("prevMatrix");
										var cachedPages = getCache("prevPages");
										
										if(cachedMatrix === null || cachedPages === null) {
											return true;
										}
										
										//--- Has the matrix changed? If so, go ahead and re-draw
										if(cachedMatrix.rowCount !== matrix.rowCount || cachedMatrix.colCount !== matrix.colCount) {
											return true;
										}
										
										if(pages.Get(currentPageIndex).length !== cachedPages.Get(currentPageIndex).length) {
											return true;
										}
										
										//--- Check Page Length too
										if(pages.length !== cachedPages.length) {
											return true;
										}
										
										for(var c=0;c<pages.Get(currentPageIndex).length;c++) {
											if(pages.Get(currentPageIndex).Get(c).module.id !== cachedPages.Get(currentPageIndex).Get(c).module.id) {
												return true;
											}
										}	
										
										return false;
									}
								  });

var PagingModuleObj = new Class({
									//--- This section is pretty good, still need to do event cleanup but other then that, its clean.
									"pagesCollection" : null,
									"initialize" : function(pages) {
										this.pagesCollection = pages;
									},
									"Toggle": function() {
										if(this.pagesCollection.length > 1) {
											this.DrawPagingModule();	
											return;
										} 
										
										this.HidePagingModule();
									},
									"DrawPagingModule" : function(matrix) {
										matrix = matrix || new Matrix();
										
										var mod;
										var posX = matrix.rowCount -1;
										var posY = matrix.colCount -1;
										var parent = this;
										
										if((mod = $("pagingModule")) === null) {
											mod = new Element("DIV", {
												"id"	: "pagingModule",
												"class" : "module paging",
												"styles": {
													"position": "absolute",
													"top": posX * moduleSize.height,
													"left" : posY * moduleSize.width + mainDivMargin, //--- 206 is for the margin on the main container.
													"visibility" : "hidden",
													"opacity" : 0
												}
											});
											
											mod.injectInside($("Main"));
										} else {
											mod.setStyles( { 
													"top": posX * moduleSize.height,
													"left" : posY * moduleSize.width + mainDivMargin, //--- 206 is for the margin on the main container.
													"visibility": "hidden"
											});
											
											//--- Assign new numbers.
											//--- Kill all events! TODO
											mod.empty();
										}
								
										var controls = new Element("DIV", {"class" : "controls" });
											controls.injectInside(mod);
											
										var pageLink;
										for(var c=this.pagesCollection.length;c>0;c--) {
											pageLink = new Element("A", { 
												id 		: "pageSelect" + c,
												events 	: { "click"  : function() {
																	parent.GoToPage(this.get('text').toInt() - 1);
															  }
												}
											});
											if(c===1){
												pageLink.addClass("first");
											}
											
											if(c===(currentPageIndex+1)) {
												pageLink.addClass("selected");
											}
											
											pageLink
												.set('text', c)
												.injectInside(controls);
										}
										
										new Element("DIV", { 
															id : "prev",
															events : { "click" : function() { 
																	parent.GoToPage( currentPageIndex - 1 );
																} 
															},
															"class" : ((currentPageIndex === 0)?"disabled":"")
														}).injectInside(controls);
										
										new Element("DIV", { 
															"id" : "next",
															"events" : { "click" : function() { parent.GoToPage( currentPageIndex + 1 ); } },
															"class" : ((currentPageIndex === (this.pagesCollection.length - 1))?"disabled":"")
											}).injectInside(controls);
										
										fadeIn(mod, null, 500);
									},
									"HidePagingModule" : function() {
										var pagingModule = $("pagingModule");
										if(pagingModule !== null) {
											pagingModule.setStyle("visibility", "hidden");
										}
									},
									"GoToPage" : function(pageIndex) {
										if(pageIndex < 0) pageIndex = 0;
										if(pageIndex >= pages.length) pageIndex = this.pagesCollection.length - 1;
										if(currentPageIndex === pageIndex) return;
										var parent = this;
																				
										var pagingModule = $("pagingModule");
											pagingModule.getChildren()[0]
												  .getChildren()
												  .removeClass("selected");
										
											if(pageIndex > 0) {
												//--- Show Page Prev as active
												$("prev").removeClass("disabled");				
											} else {
												$("prev").addClass("disabled");
											}
								
											if(pageIndex == (this.pagesCollection.length - 1)) {		
												$("next").addClass("disabled");	
											} else {
												$("next").removeClass("disabled");	
											}

										$("pageSelect" + (1 + pageIndex) ).addClass("selected");
																		
										//-- Draw is ment for a whole new set of modules. Currently anyway.
										//--- Transition the current page off, then draw the new page.
										transitionColl(this.pagesCollection.Get(currentPageIndex).GetModules(), fadeOut, function() {
											//drawPage(pages, pageIndex-1, fadeOut, fadeIn);
											parent.pagesCollection
													.Get(pageIndex)
													.Draw(fadeOut, fadeIn);
										});	
									}
								});

var PagesCollectionObj = new Class({
									"length" : 0,
									"_pages" : [],
									"PagingModule" : null,
									"initialize" : function() {
										this._pages = [];
										this.PagingModule = new PagingModuleObj(this);
									},
									"Add" : function(pageObj) {
										this._pages.push(pageObj);
										pageObj._index = (this.length = this._pages.length) - 1;
									},
									"Get" : function(index) {
										return this._pages[index];
									},
									"HideAllModulesOnOtherPages" : function(index) {
										this._pages.forEach(function(_item, _index) {
											if(index == _index) {
												return;
											}
											
											for(var c=0;c<_item._modules.length;c++){ 
												_item._modules[c].module.hide();
											}
										});
									}
								});

var PageObj = new Class({
						"length" : 0,
						"_modules": [],
						"_index" : -1,
						"_copyModule" : null,
						"initialize" : function() {
							this._modules = [];
							this.length = 0;
						},
						"Add" : function(module, x, y) {
							if(module.id == "") {
								//--- We'll need this to have a unique ID fromt his point on.
								module.id = "moduleID" + (new Date().getTime()) + $random(1, 100000);	
							} 
							
							if(module.hasClass("copy")) {
								_copyModule = module;
							}
							
							this._modules.push( { "module" : module, "x" : x, "y" : y } );	
							this.length = this._modules.length;
						},
						"Get" : function(index) {
							if(index < 0 || index > this._modules.length) {
								return null;	
							}
							return this._modules[index];
						},
						"GetModules" : function() {
							var modules = [];
							for(var c=0;c<this._modules.length;c++) {
								modules.push(this._modules[c].module);
							}
							
							return modules;
						},
						"ShowCopy" : function() {
							var parent = this;
							
							if(!this._copyModule) {
								return;	
							}
							
							this._copyModule.show();
							
							//--- This seems like to many steps.
							this._modules = this._modules.erase(
										elementsToShow.filter( function(item, index) {
												return (item.module.id == parent._copyModule.id);
											}
										)
									);
						},
						"Draw" : function(transOut, transIn, callback) {
							var currInfo;
							var parent = this;
							var cachedPages = getCache("prevPages"); 
							var transOutElements = [];
							var transInElements = [];
							
							if(cachedPages && cachedPages[this._index]) {
								//--- Get collection of nodes to transition out.
								//--- The elements to transition out are the complement elements that are currently displayed, but not in the page to be drawn.
								transOutElements = PageObj.Complement( this, cachedPages[this._index] );
							}
							
							//extractAndShowCopy(this._modules);
							this.ShowCopy(); //--- I don't extract it from the modules collection though.
														
							transitionColl( extractCopy(transOutElements), transOut, function() {
								var len = parent._modules.length;
								var count = 1;
								
								//--- Need to position them all before showing any of them.
								//--- Once we start displaying one, there might be another already in its place that will get moved later as these transitions are staggered.
								parent._modules.forEach(function(item, index) {
									item.module.setStyles({
										"position": "absolute",
										"top"	: item.y * moduleSize.height + "px",
										"left" 	: item.x * moduleSize.width + mainDivMargin + "px"
									});
								});
								
								//--- Transition the Position modules in
								parent._modules.forEach(function(item, index) {
									//--- Transition each module in. On the last module,execute the callback.
									transIn(item.module, function() {
										if(count++ == len && callback) {
											callback();
										}					
									}, null, calculateDelay(index) );
									//--- Index, times 3 to make the diff between each module greater, plus 1 so that the first one counts as 1 not 0, times 30 which is the amount of milliseconds to wait before moving to the next module.
								});
					
							});
							
							currentPageIndex = this._index;
						}
					 });

//--- Static method of the Page Object
PageObj.BuildPages = function(modules, matrix) {
	var pages = new PagesCollectionObj();
	var currentPage = new PageObj();
	var curr, fitInfo, c, peekNode;
	var hangPrevent = 0;
	
	while(modules.length > 0) { //--- While there are still modules left to place
		c=modules.length-1;
		
		if(hangPrevent > 1000) {
			break;
		}
						
		while( !matrix.isFull() && c >= 0)  {
			curr = $(modules[c]);
			fitInfo = matrix.ModuleFit( curr ); 

			if(fitInfo.canFit) {
				currentPage.Add( curr, fitInfo.x, fitInfo.y );
				
				matrix.ToggleModuleBits(fitInfo); //--- Indicate in the matrix that this space is reserved / taken.
				
				modules = modules.erase(curr);					
			}
				
			c--;
			// If the next module is null (no more modules) and there is at least 2 pages display the paging component.
			matrix.TogglePagingBit( (modules.length > 0 || pages.length > 1) );
		} 
						
		hangPrevent++;
		pages.Add(currentPage); 			//--- Store page in pages collection
		currentPage = new PageObj(); 		//--- New page
		matrix.Reset();
	}
	
	return pages;
};

//--- Return all elements that do not intersect with page1 and are only on page2.
PageObj.Complement = function(page1, page2) {
	if(page1 == null || page2== null) {
		return [];
	}
	var complement = [];
	var c1, c2;
	for(var c=0,len = Math.max(page1._modules.length, page2._modules.length);c<len;c++) {
		c1 = page1.Get(c);
		c2 = page2.Get(c);
		//--- If one of the items is null, they can't be equal.
		if(c2 == null || c1 == null) {
			//--- if it was c2, then we want that included.
			if(c2) {
				complement.push( c2.module );
			}
		} else if( !(c1.module.id == c2.module.id &&
			  c1.x == c2.x &&
			  c1.y == c2.y) ) {
			complement.push( c2.module );
		}
	}
	return complement;
};
	

var OverlayObj = new Class({
							"onHideOverlayComplete" : function(){}, //--- Fired when you hide the overlay
							"onShowOverlayComplete" : function(){},
						   	"initialize" : function() {
								this.ResizeOverlay();
										
								window.addEvent("resize", this.ResizeOverlay.bind(this));
							},
							"onShowOverlay" : function(target) {
								//console.info("onShowOverlay");
								
								//--- Hide Flashere section.
								var flash = $$(".FLASHHERE");
								
								//--- Attach Tween to outerContainer
								$$("#outerContainer").set('tween', {
									duration:500,
									property: "height",
									transition : Fx.Transitions["Expo"]["easeOut"],
									onStart: function(el) { el.setStyle("overflow", "hidden"); },
									onComplete: function(el){ el.setStyle("overflow", "visible"); }
								});
								
								if(window.Book && flash.length === 0) {
									Book.Make();
								}
								
								window.execLateScript();
								flash.show();
								
								//--- Can we do something here?
								this.attachEvents();
								
								this.onShowOverlayComplete(target);
							},
							"isOverlayOpen" : function() {
								return $defined($("OverlayContent"));
							},
							"ResizeOverlay" : function() {
								
								var overlayDiv = $("Overlay");
								if(overlayDiv) {
									//--- Set new width and height
									
									var size = window.getSize();
									var newHeight = size.y; // Math.max(size.y, size.scrollSize.y); 
									var newWidth = size.x - parseInt(overlayDiv.getStyle("margin-left"), 10) ;
									overlayDiv.setStyles( {"width" : newWidth + "px", "height" : newHeight + "px"} );
								}
							}, 
							"ShowOverlay" : function(actionItem) {
								var parent = this;
								
								var target = actionItem.TrimEndChar('/');
								var overlayTransitionSpeed = 400;
								
								new Request({
									url: target,
									evalScripts	: true,
									data: {xhr: true},
									method:'get',
									onComplete: function(response) {
													
													var overlayContainer = new Element("DIV", { 
														"id" : "overlayContainer",
														"styles" : {
															"visiblity" : "hidden",
															"opacity" : 0
														}});

													overlayContainer
														.set( 'html', response )
														.injectInside(document.body);
													
													$("OverlayContent").setStyle("display", "block");
													
													$("Overlay")
														.setStyle('height', getHeight()+17)
														.setStyle('width', getWidth()-200);

													parent.ResizeOverlay();
													
													//--- Hide Flashere section.
													/*
													var flashHere = $$(".FLASHHERE")[0];
													var flash = (flashHere) ? flashHere.setStyle("display", "none") : null;
													*/
													var flash = $$(".FLASHHERE");
													
													fadeIn(overlayContainer, parent.onShowOverlay.bind(parent, actionItem), overlayTransitionSpeed);
													flash.hide();
													
													$$('a.track, a.internal')
													.filter(function(e){ return $(e).getParents('#overlayContainer').length > 0; })
                    		  .addEvents({
														'click': function(){
															if( typeof _gaq !== 'undefined')
																_gaq.push(['_trackPageview', this.rel]);
														}
													});
												}
									}).send();
							},
							"HideOverlay" : function(ev, newRel) {
								var overlay = $("Overlay");
								var overlayContent = $("OverlayContent");
								var parent = this;
								
								if(!overlay || !overlayContent) {
									return false;
								}
								
								fadeOut(overlay);
								
								fadeOut(overlayContent, function() {
									//var el;
									//--- Remove the overlay from the document. Opportunity to cache here.
									
									$$("#overlayContainer", "#Overlay", "#OverlayContent").dispose();
									
									/*
									if((el = $("overlayContainer")) != null) {
										el.dispose();
									} else if(document.getElementById("Overlay")) { 
										$("Overlay").dispose();
										$("OverlayContent").dispose();
									}
									*/
									
									window.Location.setLocation( newRel || window.Location.GetModuleSet( window.location.hash.substr(1) ) );
										
									if(parent.onHideOverlayComplete) {
										parent.onHideOverlayComplete();	
									}									
									
								}, 800);
								
								if(ev) {								
									new Event(ev).stop();
								}
								
								return true;
							}, 
							"attachEvents" : function() {
								//console.info("overlay attach events");
								
								var that = this;
								
									//--- add setLocation(rel) to post tags
								$$(".tags a.internal", "li.internal a", "a.section")
									.addEvent("click", function(ev) {
										//--- Used to be this, now use HideOverlay
										//window.Location.setLocation(this.rel);
										
										that.HideOverlay(null, this.rel);

										new Event(ev).stop();							
									});
								
								$$("#overlayClose", "#Overlay")
									.addEvent("click", this.HideOverlay.bindWithEvent(this));
								
								var el;
								/*
								if((el = $("overlayClose")) != null) {
									el.addEvent("click", this.HideOverlay.bindWithEvent(this));
								}
								
								if((el = $("Overlay")) != null) {
									el.addEvent("click", this.HideOverlay.bindWithEvent(this));
								}
								*/
								if((el = $("overlayContainer")) != null) {
									// help IE6 clear the nav when mouseover anything other than #Left div
									el.addEvent("mouseover", function(ev) {
										NavAccordion.display(-1);
									});
									
								}
							}
						   });

/* Layout logic encapsulations */
var Matrix = new Class({
	"moduleSize"		: {"width" : 193, "height": 171 },
	"_m"				: [],
	"rowCount"		: -1,
	"colCount"		: -1,
	"initialize" 	: function(rowCount, colCount) {
		if(rowCount == null && colCount == null) {
			var coors = window.getSize();
			var pageData = {};
				pageData["rows"] = Math.max(3, Math.floor(coors.y / moduleSize.height)); //--- Ditch the partial numbers with floor, then take the larger of the two numbers basically establishing a minimum of 3.
				pageData["cols"] = this.GetColsMax(); //Math.max(3, Math.floor( (coors.x - mainDivMargin) / moduleSize.width));

				setCache("previousPageDimensions", pageData);
				
			rowCount = pageData["rows"];
			colCount = pageData["cols"];
		}
		
		if(isNaN(rowCount) || isNaN(colCount)) {
			 //console.error("invalid arguments");
			return;
		}
		this._m = [];
		
		var cols = new Array(colCount);
		
		for(var c=0;c<colCount;c++) {					//--- Loop through all the positions of the row.
			cols[c] = 0;									//--- Set all its positions to 0. 
		}														//--- 0 indicates that the position has not yet been filled, which should be the case at the begining.
		
		for(var c=0;c<rowCount;c++) {
			this._m.push( $A(cols) );					//--- Add an array of 0's for every row.
		}
		
		this.rowCount = rowCount;
		this.colCount = colCount;
	},
	"GetColsMax" : function() {
		var colMax = 3;
		var coors;
		var off = false;

		//--- Compensate for copy, the minimum the page should display is the width of the copy column.
		//--- So if the copy column spans 5 columns, we should display 5 columns.
		var copy = $$(".copy")[0];
		if(copy) {
			//--- Needs to be display block, the visibility should be hidden for it already.
			if(copy.getStyle("display") == "none") {
				off = true;
				copy.setStyle("display", "block");
			}
			
			coors = copy.getSize();
			colMax = Math.max(colMax, Math.ceil(coors.x / this.moduleSize.width) );
			
			if(off) {
				copy.setStyle("display", "none");	
			}
		}
		
		//--- Now do the normal window size, and take whichever is bigger.
		coors = window.getSize();
		return Math.max(colMax, Math.floor( (coors.x - mainDivMargin) / this.moduleSize.width) );
	},
	"Reset"	: function() {
		this._m.each(function(item, index) {
			item.each(function(subItem, subIndex) {
				item[subIndex] = 0;
			});
		});
	},
	"TogglePagingBit" 	: function(val) {
		var lastRow = this._m[this._m.length-1];
			lastRow[lastRow.length-1] = (val!=null)? (val?1:0) : !lastRow[lastRow.length-1];
	},
	"ToggleModuleBits"	: function(fitInfo) {
		if(this._m[fitInfo.y] == null || this._m[fitInfo.y][fitInfo.x] == null) {
			return;
		}
		
		var colIndex;
		var rowIndex = fitInfo.y;
		for(var c=0;c<fitInfo.rowSpan;c++) {
			colIndex = fitInfo.x;
			for(var d=0;d<fitInfo.colSpan;d++) {
				this._m[rowIndex][colIndex++] = 1;
			}
			
			rowIndex++;
			
			if(this._m[rowIndex] == null) {
				return;
			}
		}
	},
	"ModuleFit"		: function(module) {
		var row, col;
		var canFit = false;	 
		var currModuleSize = this._moduleSize(module);
		var colSpan = Math.ceil( currModuleSize.x / this.moduleSize.width );
		var rowSpan = Math.ceil( currModuleSize.y / this.moduleSize.height );
		
		row_iterate:
		for(var r=0;r<this._m.length;r++) {			//--- For each row
			for(var c=0, currCell;c<this._m[r].length;c++) { 	//--- For each cell in the row.
				currCell = this._m[r][c];
				if(currCell == 0) {
					if((this._m.length - r) < rowSpan) {  	//--- If the amount of rows remaining in the matrix (length of rows minus previous rows == amount of rows left in the matrix.)
						break row_iterate;					//--- Break, as there aren't enough rows in the matrix to show this module.
					}
					
					
					for(var n=0;n<rowSpan;n++) {			//--- For each row the module spans, ensure the column has cells at the positions necessary to fit.
						//--- make sure there are enough cells
						for(var p=0;p<colSpan;p++) {		//--- For each cell the module requires, ensure that the row has an empty space.
							if(this._m[r+n][c+p] != 0) {
								continue row_iterate;
							}
							
						}
					}
					
					canFit = true;
					col = c;
					row = r;
					
					break row_iterate;
				}
			}
		}		

		return {
			"canFit" : canFit,
			"rowSpan": rowSpan,
			"colSpan": colSpan,
			"x"		 : col,
			"y"		 : row
		};
	},
	"_moduleSize" : function(el) {
		var displayType = el.getStyle("display");
		if(displayType == "block") {
			return el.getSize();
		} 
		var elSize;
		el.setStyle("display", "block");
		elSize = el.getSize();
		el.setStyle("display", displayType);
		return elSize;
	},
	"isFull"	: function() {
		return (this._m.toString().indexOf("0") == -1);
	},
	"toString" : function() {
		return this._m.toString();
	}
});

var LocationObj = new Class({
								"moduleItem" : "/", //--- Last set of modules to have been loaded.
								"overlayItem" : "", //--- Last overlay to have been loaded.
								"currentActionItem" : "",
								"SWFAddressInitialized" : false,
								"onAddressChange" : function(){},
								"initialize" : function() {
									var parent = this;
									
									SWFAddress.addEventListener(SWFAddressEvent.INIT, function() {
										

										if(parent.isSpecial()) {
											simplePaging();
										}
										
										if( isHomeIndex( parent.getLocation() ) ) {
											
											parent.setLocation("/");	
										}
										
										SWFAddress.addEventListener(SWFAddressEvent.CHANGE, function() {
											var actionItem = parent.getLocation();
											
											if(parent.isSpecial() && parent.isHome(actionItem)) {//--- If we are on a special page do nothing.
												return;	
											}
											
											//--- Did we try to navigate somewhere while not on the main page? 
											if(!parent.isMain() && !parent.isHome(actionItem)) {
																								
												//--- Navigate to the main page so that we don't get, /detail/blah/index#/home/index.
												//--- We navigate to the main page, with the action item in the URL so it proceeds to its action.
												window.location.href = window.location.protocol + "//" + window.location.hostname + "/#" + actionItem;
												//syncNav(actionItem);
												//parent.currentActionItem = actionItem;
												return;
											}
											
											parent.onAddressChange(actionItem);
											
											syncNav(actionItem);
											
										});
										
										parent.SWFAddressInitialized = true;
										
									});
									
									//--- Sometimes SWFAddress doesn't fire the init event properly for webkit, so this brute forces it to fire.
									window.addEvent('load', function() {
										
										setTimeout(function() {
											
											if(parent.SWFAddressInitialized) {
												return;
											}
											
											SWFAddress.dispatchEvent(new SWFAddressEvent('init'));
											SWFAddress.dispatchEvent(new SWFAddressEvent('change'));
										}, 50);
									});
								}, 
								"isOverlay" : function(rel) {
									//alert ((/\/about|office\//).test(rel));
									return (/\/detail|office\//).test(rel);
									
									//return (rel.indexOf("/about") != -1 || rel.indexOf("office/") != -1);
								},
								"isSpecial" : function() {
									return ( $$(".specialPage")[0] != null ||  this.isJobs());
								},
								"isJobs" : function() {
									var jobs = $$(".jobs")[0];
									return (jobs != null && jobs.hasClass("x16"));
								},
								"isHome" : function(rel) {
									var loc = rel || this.getLocation();
									if(loc == "/" || loc == "/home/all" || loc == "/home/all/") {
										return true;	
									}
									return false;
								},
								"getModuleSetPath" : function(actionItem) {
									if(!$defined(actionItem)) return "";
									var module_set_path = $pick(['/content', 'office/', '/tag'].map(
													  function(item, index, array) {
														  if(actionItem.contains(item)){
															  return actionItem.split(item)[0];
														  }
														  return null;
													  }
											).clean()[0], actionItem);
									return module_set_path;
								},
								"setLocation" : function(newRel) {
									if(newRel.test(/^http/)){
										window.location = newRel;
										return;
									} 

									// if(newRel.indexOf("clients") != -1){
									// 	newRel = location.path+"/#/"+newRel;
									// }
									switch(newRel){
										case '/about/clients/':
										case '/about/clients':
										case '/about/awards/':
										case '/about/awards':
											window.location = newRel;
											return;
									}
									
									
									
									if(window.location.pathname.indexOf("presskit") > 1){
										window.location.pathname = "/#"+newRel;
									}
																		
									newRel = newRel.trim();
									
									//--- We are already there.
									if(newRel == this.getLocation()) {
										return;	
									}
									try {
										SWFAddress.setValue(newRel);
									} catch(e) {
										//--- For IE6, which doesn't like the SWFAddress.setValue call before finished loading.
										window.location.hash = newRel;
										//console.error( e );
									}
								},
								"getLocation" : function() {
									return SWFAddress.getValue();
								},
								"isMain" : function() {
									return window.location.pathname == "/";
								},
								"CompareLocations" : function(a, b) {
									if(a == b) return true; //--- Same
									if(!a||!b) return false;
									a = a.TrimEndChar('/');
									b = b.TrimEndChar('/');
									return (a == b);
								},
								"GetModuleSet" : function(rel) {
									var splitStr = (rel.indexOf("office/") == -1) ? "/detail" : "office/";
									var pair = rel.split(  splitStr );
									var retVal = pair[0];

									//--- Ugly Hack, but maintained for backwards compatibility.
									if(window.location.href.toString().indexOf("/HOW") != -1) {
										retVal = "/news/inthepress";
									}
									
									//--- Tag Logic
									//--- If the previous module set was a tags portion of the module set the URL specifies, use the previous moduleset.
									if(this.moduleItem.indexOf(retVal + "/tag/") === 0) {
										retVal = this.moduleItem;
									}
									//--- Ensure that the last character is a slash
									return retVal.TrimEndChar('/') + '/'; //(retVal.length === 0 || retVal.charAt(retVal.length - 1) !== "/") ? retVal + "/" : retVal; 
								}								
							});

/* Initializes the Navigation Accordian and attaches all its events. */
function initNav() {
	var opening = false;
	var ActiveNav = null;
	var SubNav = null;
	var nextNav;
	var NavAccordian;
	var rightBelowNav;
	var timerID;
	
	sections = $$("#menu-method > li");
	for(var i = 0; i < sections.length; i++){
		
		var submenus = $(sections[i]).getChildren('ul.sub-menu');
		if(submenus.length == 0){
			var attachmenu = document.createElement('ul');
			attachmenu.className = 'sub-menu';
			sections[i].appendChild(attachmenu);
		}
	}
	

	NavAccordion = new Accordion('#menu-method > li > a', 'ul.sub-menu', {
			show        : -1,
			duration    : 300, transition: Fx.Transitions.linear,
			onActive    : function(toggler, element){ 
				opening = true; 
				toggler.getParents('li').addClass('selected');
			},
			onBackground: function(toggler){ 
				toggler.getParents('li').removeClass('selected');
				if(nextNav) { nextNav.setStyle("margin-top", "15px"); } 
			},
			onComplete  : function(toggler, element) { 
				opening = false; 
				
			}
		}
	);
	
	$$('#menu-method > li > a').addEvent("click", function(event){
		if($(this).getProperty('href')=='#') event.preventDefault();
	}).addEvent('mouseenter', function(){
		if($(this).getProperty('href')=='#') $(this).setStyle('cursor', 'default');
	}).addEvent('mouseleave', function(){
		//if($(this).getProperty('href')=='#') $(this).setStyle('cursor', 'default');
	});
	
	$$("#menu-method > li").addEvent("mouseenter", function(event) {
			ActiveNav = $(this);
			clearTimeout(timerID);
			
			timerID = setTimeout(function() {
				if(!ActiveNav) return;
				var subNav = ActiveNav.getChildren('ul.sub-menu')[0];
				NavAccordion.display(subNav);
				nextNav = ActiveNav.getNext();
				if(nextNav){
					nextNav.setStyle("margin-top", "15px");
				}
			}, 200 + ((opening) ? 200: 0));

		}
	);
	
	$("Left").addEvent("mouseleave", function() {
		clearTimeout(timerID);
		NavAccordion.display(-1);
	});

	$("OtherNavigation").addEvent("mouseenter", function() {
		clearTimeout(timerID);
		NavAccordion.display(-1);
	});
	
	//--- For IE6, it doesn't listen to the mouseleave event very well. Sometimes it will close on mouseout of the A tags.
	$$(".menu-method-container li a").addEvent("mouseout", function(ev) { 
		clearTimeout(timerID);
		
		new Event(ev).stop(); 
	});
		
	$$(".sub-menu li a").addEvent("click", function(event) {
		try {
			
			syncNav( $(this).getProperty("href") );
			
			NavAccordion.display(-1);
			this.blur();
		} catch(e) {
		}
	});
	
	$("mcms_menu_container").setStyle("visibility", "visible");
	
	
	window.syncNav = function(loc) {
		var originalLoc = loc;
		var rel;
		var subNavIndicator = $("subNavIndicator");
		
		// remove selected class from a tag inside subNav
		$$('.sub-menu a')
			.removeClass('selected');
		
		if( (window.Location.isHome() || loc === "/jobs/index" || loc === "/contact/index") && subNavIndicator) {
			subNavIndicator.dispose();
		}		
		
		/*
		if(loc.charAt(loc.length - 1) == "/") {
			loc = loc.substr(0, loc.length - 1);
		}
		*/
		//--- Get the A Tag associated with the current section.
		
		// support for /thoughts/browse/tag/tagname so subnav updates to Browse by Tag
		// would be cool if mootools had more filterByAttribute matching rules, like
		// "value begins with value of attribute" instead of just "val of attrib begins with val"
		/*if(loc.indexOf("/tag") != -1) {
			var pair = loc.split("/tag");
			loc = pair[0];
		}
		*/
		loc = window.Location
				.getModuleSetPath(loc)
				.TrimEndChar('/');
		if(loc.test(/\/about\/clients\/list/))
			loc = loc.replace(/\/list/,'');
		
		var items = $$(".sub-menu li a[href=" + loc + "]");//.filterByAttribute("rel", "=", loc);
		
		if(items.length == 0){
			return;
		}
		newItem = items[0];
		
		//ActiveNav = newItem.getAncestor("navItems").getPrevious();
			
		if(subNavIndicator) {
			rel = subNavIndicator.getProperty("rel");
			if(rel == originalLoc) {
				return;
			}
			
			subNavIndicator.injectBefore($(getActiveNav(newItem)).getChildren('a')[0]);
		} else {
			subNavIndicator = new Element("span", {"id":"subNavIndicator"});
			subNavIndicator.injectBefore($(getActiveNav(newItem)).getChildren('a')[0]);
		}
		subNavIndicator.innerHTML = (newItem.get('text') == "") ? newItem.innerHTML : newItem.get('text');
		subNavIndicator.setProperty("href", newItem.getProperty("href"));
	};
	
	function getActiveNav(lnk) {
		var li = (lnk.tagName != "LI") ? lnk.getParents('li')[1] : lnk.getParent();
		if(!li) return null;
		
		return li;
		//return div;
	}
	
	
}

/* footer should stay in the bottom left hand corner till the window is smaller then the work at method/contact us navigation section. */
function initFooter() {
	var footer = $("footer");
	if(!footer) {
		return;
	}
	
	var otherNav = $("OtherNavigation");
	var otherNavCoords = otherNav.getCoordinates();
	
	positionFooter();
	
	window.addEvent("resize", function() {
		positionFooter();
	});
	
	function positionFooter() {
		//var footerCoords = footer.getCoordinates();
		var winHeight = window.getHeight() - 20;
		
		if(winHeight <= otherNavCoords.bottom) {  	//--- If the window is smaller then where work at method is positioned
			footer.setStyles({ 
				"bottom" : "",              	//--- DOn't do the calculation from the bottom anymore.
				"top": otherNavCoords.bottom + "px"	//--- Put the footer right at the bottom of the other navigation.
			});
		} else {
			footer.setStyles({
				"bottom" : "11px",                 	//--- Go back to inheriting the position from the bottom calculation.
				"top":	""
			});
		}
	}
}

function sizeMain() {
	resizeMain();
	
	window.addEvent("resize", resizeMain);
	
	function resizeMain() {
		if($("Main"))
			$("Main").setStyle("width", window.getSize().x	 - 230 + "px");		

	}
}

/* Global Functions */
function setCache(key, val) {
	cache[key] = val;
}

function getCache(key) {
	return cache[key] || null; 
}

function onHoverModuleOver() {
	this.addClass("hover");
}

function onHoverModuleOut() {
	this.removeClass("hover");
}

function execLateScript() {	
	//--- If no flash
	if(swfobject.getFlashPlayerVersion().major < 9) {
		//console.info('execlatescript flash less then 9');
		var content = $("flashcontent");
		if(content) {
			content.hide();

			ToggleCopyText(null, content.get('text'));
			
			var imgs = content.getElements("IMG");
			if(imgs.length > 0) {
				$(imgs[0])
					.setStyles({ position: "absolute", bottom:"0px" })
					.injectInside(content.getParent());
			}
	
			content.empty();
		}
		return;
	}
	
	$$(".lateBind").forEach(function(item, index) {
		var str = item.get('html') || item.innerHTML;
		try {
			eval(str);
		} catch(e) {
			alert("LateScriptError: " + e.message);
		}
	});
}


function extractCopy(elementsToRemoveFrom) {
	var copy = $$(".copy")[0];

	//--- Remove copy from the modules to transition.
	return elementsToRemoveFrom.erase(copy);
}

function createCSSRule(rule,attributes){
	//Create the CSS rule
	var newRule = "\n"+rule+"{\n";
	for (var attribute in attributes)
	{
		newRule += "\t" + attribute + ": " + attributes[attribute] + ";\n";
	}
	newRule += "}\n";
 
	//Inject it in the style element or create a new one if it doesn't exist
	var styleTag = $$('style[type="text/css"]')[0] || new Element("style").setProperty('type','text/css').injectInside(document.head);
	if(Browser.Engine.trident)
	{
		styleTag.styleSheet.cssText += newRule;
	}
	else
	{
		styleTag.appendText(newRule);
	}
}

function transitionColl(elements, transition, callback) {
	var len = elements.length;
	var completedCount = 0;
	
	if(elements == null || len == 0 || transition == null) {
		if(callback) {
			callback();
		}
		return; 
	}
	
	elements.forEach(function(item, index) {
		transition(item, finished, null, calculateDelay(index));
	});
	
	function finished() {
		completedCount++;
		if(completedCount == len) {
			if(callback) {
				callback();
			}
		}
	}
}

function fadeOut(el, callback, duration) {
	if(el.getStyle("visibility") == "hidden") {
		if(callback) {
			callback();
		}
		return; //--- Its already hidden.
	}	
	
	var effect = new Fx.Morph(el, {
			"duration"	: duration || 500, 
			"transition"	: Fx.Transitions.linear,
			"onComplete" 	: function(module) { 
							el.hide(); 
							if(callback) {
								callback();
							}
						  } 
		});
		
		effect.start( { "opacity": 0 } );
}

function fadeIn(el, callback, duration) {
	
	var effect = new Fx.Morph(el, {
			"duration"	: duration || 500, 
			"transition"	: Fx.Transitions.linear, 
			"onStart" 		: function(el) {
								el.show();
							},
			"onComplete" 	: function(module) { 
							if(callback) {
								callback();
							}
						  } 
		});
	
		effect.start( { "opacity": 1 } );
}

function slideIn(el, callback, duration, delay) {
	//--- I don't know why but this method doesn't work in opera, so use the fadeIn method instead. Something to do with the timeout.
	if(window.opera) {
		fadeIn(el, callback, duration);

		return;
	}
	
	var currentTop = el.getStyle("top").toInt();
	delay = delay || 0;
	
	var styles = new Fx.Morph(el, {
										"ignore" : false, 
										"duration" : 200, 
										"transition" : Fx.Transitions.linear,
										"onStart" 		: function(el) {
											el.show();
										},
										"onComplete" : function(el) { 
													if(callback) {
														callback();
													} 
												} 
									});
		
	setTimeout(function() {
				styles.start( {
					"top" : [currentTop-35, currentTop],
					"opacity" : [0, 1]
				});
				
			}, delay);
}

function slideOut(el, callback, duration, delay) { 
	var visibility = el.getStyle("visibility");
	//--- Invalid? Return.
	if(	!el || visibility == "hidden" || 
		( visibility == "inherit" &&  el.getParent().getStyle("visibility") == "hidden") //--- if we hid the parent, but not the modules themselves, no need to slide out.
	) { 
		if(callback) {
			callback();
		}
		return;
	}
	
	var currentTop = el.getStyle("top").toInt();
	var styles = new Fx.Morph(el, {
									"wait" : false, 
									"duration" : 200, 
									"transition" : Fx.Transitions.linear, 
									"onComplete" : function(el) {
														el.hide();
														if(callback) { 
															callback(); 
														} 
													}
									});
		
	setTimeout(function() {
				styles.start( {
					"top" : [currentTop, currentTop+35],
					"opacity" : [1, 0]
				});
			}, delay);
}

function calculateDelay(index) {
	return (index+1) * 30;
}

function isHomeIndex(val) {
	return (val == "/home/all" || val == "/home/all/");
}

/* 
	Some base methods to add to all the moo objects. 
*/
Element.implement({ 
	"getAncestor" : function(className) {
		var parent = this.getParent();
		while(parent != null) { if(parent.hasClass(className)) { return parent; } parent = parent.getParent(); }
		return null;
	},
	"show" : function() {
		this.setStyles( {
			"visibility" : "visible",
			"display" : "block",
			"opacity" : "1"
		} );
		return this;
	},
	"hide" : function() {
		this.setStyles( {
			"visibility" : "hidden",
			"display" : "none",
			"opacity" : 0
		} );
		return this;
	},
	"toggleText" : function(){
		var s = this.getProperty("_toggleText");
		this.setProperty("_toggleText", this.get('text'));
		this.set('text', s);
		return this;
	}
});

String.implement({
	TrimEndChar: function(c) {
		if(this.charAt(this.length -1) === c){
			return this.substr(0, this.length - 1);
		}
		return this;
	}
});


/*
 * Called from Flash to initialize Case Study and Leadership copy columns
 *
 */
function ToggleCopyText(elID, txt) {
	//console.info("toggle copy text");
	var outerDiv = $("outerContainer");
	var textDiv = $("textContainer");
	//var sizeDiv = new Fx.Tween("outerContainer", "height", {"duration":500, "transition" : Fx.Transitions["Expo"]["easeOut"]});
	var copyVersion = getCopyVersion(unescape(txt));
	var el;
	
	//--- If there is no text to display.
	if(txt == null || txt.length == 0) {
		
		$('booknav').setStyle('display', 'none');
		$('book').innerHTML = "";
		if($('readMore')) {
			$('readMore').dispose();
		}
		if(outerDiv.getStyle("height").toInt() != 0) {
			//--- Hide.
			//sizeDiv.start(148, 0);
			outerDiv.tween(148, 0);
		}
		
	} else {
		
		switch(copyVersion) {
			case 1:
				//--- Perform a simple paging breakup with this text.
				simplePaging.delay(1, null, txt);
				break;
			case 2:
				//--- ?
				readMorePaging.delay(1, null, txt);
				break;
		}
		
		if(outerDiv.getStyle("height").toInt() != 148) {
			//sizeDiv.start(0, 148);
			outerDiv.tween(0, 148);
		}
	}
}

/* 
  Default version of paging.
  Inserts text into a content div, then runs a script to break it up.
 */ 
function simplePaging(txt) {
	//console.info("simplePaging");
	
	txt = txt || "";
	
	if(!$defined($("book"))) {
		return;
	}
	
	$$("#readMore")
		.dispose();
	
	$$("#textContainer")
		.removeClass("read-more");
	
	$("book")
		.set('html', '')
		.show();
	
	$("booknav")
		.show();
	
	//var tempHeight = outerDiv.getStyle("height");
	//--- Needs to be in a setTimeout so that it breaks from this process thread otherwise the paging doens't work.
	//setTimeout(makeBook.pass([344, 115]), 1);
		
	Book.MakeAsync(unescape(txt));
}


function readMorePaging(txt) {
	//console.info("readMorePaging");
	var strCopy = unescape(txt).replace(/<span id="more-\d+"><\/span>/,'<!--more-->').split("<!--more-->");
	//console.log(strCopy);
	var introText = strCopy[0];
	var more = strCopy[1];
	var bookMade = false;
	var cont = $("outerContainer");
	var bookDiv = $("book");
	/*
	var sizeDiv = new Fx.Tween("outerContainer", "height", {
			duration:500,
			transition : Fx.Transitions["Expo"]["easeOut"],
			onStart: function(el) { el.setStyle("overflow", "hidden"); },
			onComplete: function(el){ el.setStyle("overflow", "visible"); }
		
		});
	*/
	
	var intro = $("readMore") || (new Element("DIV", {id: "readMore", 'class': "read-more-intro"}).injectInside("textContainer"));
		intro.set( 'html', introText);

	//--- Add read more toggle link
	var readMore = new Element("A", {'class': 'read-more-link', href:'#', _toggleText: 'Read Less'});

		readMore
			.set( 'html', 'Read More')
			.addEvent("click", readMoreToggle)
			.injectInside(intro);
		
	$("textContainer")
		.addClass("read-more");
	
	bookDiv
		.hide()
		.set('html', '');
	
	cont.tween(148);
		
	function readMoreToggle(ev) {
		
		var hide = (cont.getSize().y == 569);
		
		if(!bookMade) {
			bookDiv.show();
			
			Book.MakeAsync(more, 344, 390, function() {

				cleanParagraphs(function() {
					injectReturnToImageViewerLink();			
					injectRelatedLinksHeader();
				});
				
				
			});
			bookMade = true;
		} else {
			if(hide) {
				bookDiv.hide();
			} else {
				bookDiv.show();
			}
		}
		
		cont.tween( (hide) ? 148 : 569 );
		
		$$('.read-more-link').toggleText();
		
		new Event(ev).stop();
	}
	
	
	function injectReturnToImageViewerLink() {
		
		var lastCol = $("book").getElement(".pageset:nth-child(last)").getElement('.bookpage:nth-child(last)');
		var lastP = lastCol.getLast("p") || new Element("P").inject(lastCol, 'top');
		
		//--- Add the Return to Image Viewer link
		new Element("A", {'class': 'read-more-link', href:'#', _toggleText: 'Return to Image Viewer'})
				.set('text', 'Return to Image Viewer')
				.addEvent("click", readMoreToggle)
				.inject(lastP, 'after');
		
	}
	
	//--- We dont' want to start either of the columns with an empty paragraph
	//--- We also need the correct amount of paragraphs around the retrn to image viewer link section.
	function cleanParagraphs(onComplete) {
		//var lastCol = $("book").getElement(".lastCol");
		//var firstCol = $("book").getElement(".firstCol");
		var emptyP;
		var related = $("book").getElement(".lastCol ul.related");
		
		//--- Do either of these columns start with an empty
		$$($$("#book .firstCol", "#book .lastCol").getFirst("p:empty"))
			.dispose();
		
		if($defined(related)) {
			while($defined(emptyP = related.getPrevious("p:empty"))) {
				emptyP.dispose();
			}			
		}
		
		if($defined(onComplete)) {
			onComplete();
		}
	}
	
	function injectRelatedLinksHeader() {
		var related = $("book").getElement(".lastCol ul.related");
		if(!$defined(related) || $defined($("book").getElement(".related-header"))){
			return;
		}
		
		new Element("H5", {'class': 'related-header', html: 'Related Links:'})
			.inject(related, 'before');
		
	}
}


function getCopyVersion(strCopy) {
	if(strCopy==null)return 1;
	//--- If copy has more in it, its the second version of the layout.
	if(strCopy.contains("<!--more-->")) {
		return 2;
	}
	
	return 1;
}



/*
	Turns a block of text into columns using a width and height to determine the amount of columns.
*/
var Book = new Class({
	_divDim : null,
	_contentDiv: null,
	pages : [],
	current_page: 1,
	cols_per_page : 2,
	_newPage : function (page_num) {
		return new Element("div", {
			'id': 'pageset-' + page_num,
			'class': 'pageset',
			'styles': {  'display': 'none' }
		});
	},
	initialize : function() {
		this._divDim = $('divDim');
		this._contentDiv = $("content");
		
		$$('#booknav-next')
			.setProperty("href", "#")
			.addEvent("click", this.ShowNextPage.bindWithEvent(this));
			
		$$('#booknav-prev')
			.setProperty("href", "#")
			.addEvent("click", this.ShowPrevPage.bindWithEvent(this));
		
	},
	ParseText: function(text) {
		if(!this._contentDiv) return;
		
		this._contentDiv.set( 'html', text);
	},
	MakeColumns : function(width, height) {
		if (!this._contentDiv) {
			return [];
		}
		
		//if (document.getElementById && typeof document.getElementById("content").innerHTML != "undefined") {
		
		// we always have at least 1 page unless something's amok
		var colCount = 1;
		
		// get height and width from divDim on each page, sized appropriately
		var colWidth = width   || this._divDim.getStyle('width').toInt();
		var colHeight = height || this._divDim.getStyle('height').toInt();
		this.cols_per_page = ($('clientList')) ? 4 : 2; //--- Client list is so special, it gets 4 columns!
		this.current_page = 1;
		this.pages = [];
		var that = this;
		
		// do the heavy lifting; inside lib_columns.js
		var colContentArray = Columns.splitText(document.getElementById("content").innerHTML, colWidth, colHeight);
		
		// how many pages we'll need
		//--- This needs Fixin, global variables bite.
		//window.num_pages = Math.ceil(colContentArray.length / this.cols_per_page);
		
		var page = this._newPage(this.pages.length+1);
		
		// BUGFIX for trailing invalid <li/> tag
		// not used, li's do not render properly in IE6 AT ALL
		
		colContentArray.map(function(item, index) {
			item =  item.replace('<li></li>', '');
			
			var col = new Element("div", {
				'id': 'bookpage-' + (index + 1),
				'class': 'bookpage' + ((colCount % 2 == 0) ? ' evenCol' : ' oddCol')
			});
			
			if(colCount % that.cols_per_page == 0) {
				col.addClass('lastCol');
			}
			
			if(colCount == 1 || colCount == (that.cols_per_page + 1) ) {
				col.addClass('firstCol');
			}
			
			col
				.set('html', item)
				.injectInside(page);// inject col into page
			
			// inject page into pages wrapper
			if(page.getChildren().length == that.cols_per_page || index == (colContentArray.length - 1)) {
				// inject the full page into pages div and create new page
				// 
				that.pages.push(page);//.injectInside($('book'));
				page = that._newPage(that.pages.length+1);
				
			}
			
			colCount++;
			
			return item;
		});
				
		return this.pages;
	},
	Draw : function(el, pages) {
		pages = pages || this.pages;
		el = el || $("book");
		
		if(!el) {
			return;
		}
		
		el.empty();		
		
		$each(pages, function(item, index) {
			item.injectInside(el);
		});
		
		/* WTF??? */
		//--- Attach Generic events?
		//var Bookpopulation = new RepopulationObj();		
		
		$$("a.internal")
		  .filter(function(e){ return $(e).getParents('#overlayContainer').length > 0; })
			.addEvent("click", this.setLocationHandler);
		
		
		this.UpdateNavigation();
		this.ShowPage(1);
	},
	ShowPage : function(pageIndex){
		//console.info("show page ", pageIndex, this.current_page);
		var currPage;
		if((currPage = $('pageset-'+pageIndex)) == null) {
			return;
		}
		
		$$('.pageset').setStyle('display', 'none');
		currPage.setStyle('display', 'inline');
		currPage.setStyle('visibility', 'visible');
		
		this.current_page = pageIndex;
		
		
	},
	ShowNextPage : function(ev) {
		if($('pageset-'+(this.current_page+1))) {
			//console.info("Calling Show Next Page!!!");
			this.ShowPage(this.current_page+1);
			this.UpdateNavigation();
		}
		if(ev) {
			new Event(ev).stop();
		}
	},
	ShowPrevPage : function(ev) {
		if($('pageset-'+(this.current_page-1))) {
			this.ShowPage(this.current_page-1);
			this.UpdateNavigation();
		}
		if(ev) {
			new Event(ev).stop();
		}
	},
	UpdateNavigation : function() {
		var navDiv;
		var currentNavDiv;
		
		// just need current_pg / num_pages
		if((currentNavDiv = $('booknav-current')) !== null) {
			// update current_pg / numpages
			
			currentNavDiv
				.set( 'html', '<span>{currentPage}/{numberOfPages}</span>'.substitute({ currentPage: this.current_page, numberOfPages: this.pages.length}) );
				//.set( 'html', "<span>" + current_page + " / " + num_pages + "</span>");
		}
		
		// we need all pages clickable here
		if((currentNavDiv = $('booknav-current-full')) !== null) {
			// will use tmpDiv innerHTML 
			var tmpDiv = new Element('div');
			
			currentNavDiv
				.empty()
				.adopt(
					this.pages.map( function(item, index, array) {
						index = index + 1; //--- Index logic isn't 0 based.
						
						var a = new Element("a", {'id': 'pageSelect' + index, 'html': index})
								.addEvent("click", this.ShowPage.pass(index, this))
								.addEvent("click", this.UpdateNavigation.bind(this));
					
						if(this.current_page == index) { a.addClass('selected'); }
						
						if(index == 1) { a.addClass('first'); }
						
						return a;
					}, this)
						.reverse()
				);
		}
		
		if($('booknav-current') || $('booknav-current-full')) {
		
			if(this.pages.length > 0) {
				$('booknav').show();
			}
			
			// if on first page, change a#booknav-prev bg image src to inactive
			$('booknav-prev')
				.setStyles(
					(this.current_page <= 1) ? 
						{backgroundImage: 'url(/wp-content/themes/method-blog/mcms/images/prev_page_inactive.gif)', cursor: 'default'} :
						{backgroundImage: 'url(/wp-content/themes/method-blog/mcms/images/prev_page_inactive.gif)', cursor: 'pointer'}
				);
	
			$('booknav-next')
				.setStyles(
					(this.current_page == this.pages.length) ? 
						{backgroundImage: 'url(/wp-content/themes/method-blog/mcms/images/next_page_inactive.gif)', cursor: 'default'} :
						{backgroundImage: 'url(/wp-content/themes/method-blog/mcms/images/next_transparent.gif)', cursor: 'pointer'}
				);
			
			$$("#booknav-current", "#booknav-current-full", "#booknav-next", "#booknav-prev")
				.setStyle('visibility', (this.pages.length > 1)  ? 'visible' : 'hidden');
		}
	}
});

Book.Make = function(bookText, width, height) {
  // console.info("Book.Make");
	
	var newBook = new Book();
		if(bookText) {
			newBook.ParseText(bookText);
		}
		newBook.MakeColumns(width, height);
		newBook.Draw();
		
	Book.Instance = newBook;
	
	return newBook;
};

Book.MakeAsync = function(bookText, width, height, onComplete) {
	// console.info("Book.MakeAsync");
	
	var newBook = new Book();
	if(bookText){
		newBook.ParseText(bookText);
	}
	
	setTimeout(function() {
		newBook.MakeColumns(width, height);
		newBook.Draw();
		if(onComplete) {
			onComplete(newBook);
		}
	}, 1);
	
	Book.Instance = newBook;
};



/* Initialize the document */
window.addEvent('domready', function () {
	
	try {	
		//--- Nav Stuffs
		//--- Doesn't rely on any of the objects below it, an actual self contained object function.
		initNav();
		
		//--- Init footer script
		//--- Also has no dependencies
		initFooter();
		
		sizeMain();
		//--- Modules should be re-loaded on the current page.
		//initModuleRepopulation();
		var Repopulation = new RepopulationObj();
			Repopulation.onRepopulateComplete = function(target) {
				window.Location.moduleItem = target;

				//--- on Repopulate Event
				ModulesManager.RefreshModules(slideIn);
			};
		
		//--- New elements, need their events.
		var Overlay = new OverlayObj();
			Overlay.onShowOverlayComplete = function(actionItem) {
				//--- Store the location of the last opened overlay.
				Location.overlayItem = actionItem;
				
				$$("a.download_link").addEvent("click",function(event){
				  $this = $(this);
				  event.preventDefault();
				  var moduleType = $this.getParents('.module').get('class')[0].replace(/module x\d+ /, '');
					if( typeof _gaq !== 'undefined'){
        		_gaq.push(['_trackEvent', 'Downloads', moduleType, $this.title]);
        		setTimeout(function(){document.location = $this.href;}, 250);
					}
        		
				});
				
      	
			};
		
		//--- Location Control
		//initLocationControl();
		window.Location = new LocationObj();
		window.Location.onAddressChange = function(actionItem) {
			//--- If current action item is empty we are doing an initial page load. If action item is / we have nothing to load.
			//--- This is mainly so if you navigate directly to a module without a hash, then proceed to navigate around the site, it operates from the base url address with the hash not the deep link with the hash.
			if(this.currentActionItem === "" && actionItem === "/" ) {
				if(this.isOverlay(window.location.href)) {
					Overlay.onShowOverlay(actionItem);
				}
				
				this.currentActionItem = actionItem;
				return;
			}
			
			
			//--- Hide any overlays that are currently showing. It does a check to see if any are open.
			Overlay.HideOverlay();
			
			//--- Okay, now that we know we are on the main page with a proper action item, what do we do with it? If its an overlay, show the overlay.
			if(this.isOverlay(actionItem)) {
				//--- Show Overlay For action item
				Overlay.ShowOverlay( actionItem );			
			} else if(!this.CompareLocations(actionItem, this.moduleItem)) { //--- If we are navigating to a new module set. (If we go from moduleset, to overlay, and close the overlay, we don't want to reload the module set.
				//--- Module repopulation
				//--- Start from the begining
        // console.log(actionItem, this.moduleItem);
				currentPageIndex = 0;
				
				//--- Get new modules from the server and populate them here.
				Repopulation.RepopulateModules( actionItem );
			}
			
			//--- Track what we are currently doing.
			parent.currentActionItem = actionItem;
		};
		
		//--- Init Module Logic
		//initModuleControl();
		//--- Relies on window.Location, so it has to be afterwards. Can we invert that dipendency?
		var ModulesManager = new ModulesManagerObj(Repopulation);
		
		syncNav(window.location.pathname);

	} catch(e) {
		console.error( e );
		//alert(e.message);
	}
	
});
