var RACT = {};
		RACT.CommunityUI = {};
		RACT.Util = {};

if (navigator.userAgent.lastIndexOf("MSIE")!=-1) { throw "Non standards complient browser"; }

/* **************************************** */
/* ***** Util ******* */
/* **************************************** */

		RACT.Util.Vector3 = Class.create({
			x: 0,
			y: 0,
			z: 0,

			initialize: function(x,y,z) {
				this.x = x;
				this.y = y;
				this.z = z;
			}
		});

/* **************************************** */
/* ***** Layout  ******* */
/* **************************************** */

		RACT.CommunityUI.LayoutData = Class.create({
			_definition : null,
			_xPos: null,
			_yPos: null,
			_zPos: null,
			_scale: null,
			_xPercent: null,
			_index: null,

			initialize: function(definition, xPos, yPos, zPos, scale, index) {
				this._definition = definition;
				this._xPos = xPos;
				this._yPos = yPos;
				this._zPos = zPos;
				this._scale = scale;
				this._index = index;
			}
		});

		RACT.CommunityUI.BaseLayout = Class.create({
			_parent : null,
			_ID : "NULL",
			Geometry : {},

			initialize: function(parent) {
				this._parent = parent;
				this.setupView();
			},

			setupView: function() {},
			getOpacityFor: function(cell) {},
			getXPosFor: function(cell) {},

			loadLayout: function() {},
			unloadView: function() {},

			// check for overlap between 2 2d vectors
			overlapTwo2DVectors: function(a1,a2,b1,b2) {
				if (a1 <= b1 && a2 >= b1) { return true; }
				if (a1 <= b2 && a2 >= b2) { return true; }
				if (a1 >= b1 && a2 <= b2) { return true; }
				return false;
			}
		});

/* **************************************** */
/* ***** Detail Layout ******* */
/* **************************************** */
		RACT.CommunityUI.DetailLayout = Class.create(RACT.CommunityUI.BaseLayout, {
			_ID : "DETAIL"
		});

/* **************************************** */
/* ***** Tree Layout ******* */
/* **************************************** */

		RACT.CommunityUI.TreeLayout = Class.create(RACT.CommunityUI.BaseLayout, {
			_ID : "TREE",
			Scaling : new RACT.Util.Vector3(.6,1,.1),  // Vector3(min, max, steps);
			VisibleAtOnce : 18, // number of cells visible at any one time, all others will be faded away
			FadeTime: 18, // how long it takes to fade a cell away
			OverlapAvoidance : 5, // number of previous cells to check for overlap with, higher numbers will exponentially slow down execution

			_currentRotationIndex: 0,

			Updater: null,

			Geometry: {
				width : 410, // width of tree box
				height: 366, // height of tree box

				topPadding: 80, // distance between top of tree and top of area

				distributionVertMid: 202, // distance from top of box to distrabution mid point

				distributionTopBegin: 123, // distance from left edge to start of top region distribution area
				distributionTopEnd: 276,// distance form left edge to end of top region distribution area

				distributionBottomBegin: 67,
				distributionBottomEnd: 290
			},

			setupView: function($super) {
				$super();

				// setup scaling
				var scaleMin = this.Scaling.x;
				var scaleMax = this.Scaling.y;
				var scaleStep = this.Scaling.z;

				// define box bounds
				var xMin = 0;//$(this._parent.getThumbsFrame()).getWidth()/2 - this.DefaultOptions.treeWidth/2;
				var xMax = this.Geometry.width;//xMin+this.DefaultOptions.treeWidth;
				var yMin = 0;//this._thumbsFrame.getHeight()/2 - this.DefaultOptions.treeHeight/2;
				var yMax = this.Geometry.height;//yMin+this.DefaultOptions.treeHeight;

				// distribute all the cells randomly in the view area
				for (var ii=0; ii<this._parent._contentCells.length; ii++) {
					for (var attempts = 0; attempts<=80; attempts++) {


						// generate scale
						var scale = Math.round(Math.random()*((scaleMax-scaleMin)/scaleStep))*scaleStep+scaleMin;
						yMax = this.Geometry.height-this._parent._contentCells[ii]._height*scale;

					// unused distbribution method
					/*
						if (false) {
							// stack random points to fall orderly in a 2 by 2 grid
							if ((ii+1)%4==0 || (ii)%4==0) {
								xMin = this.Geometry.width/2;
								xMax = this.Geometry.width;
							} else {
								xMin = 0;
								xMax = this.Geometry.width/2;
							}

							if ((ii+1)%2==0) {
								yMin = (this.Geometry.height-this._parent._contentCells[ii]._height*scale)/2
								yMax = this.Geometry.height-this._parent._contentCells[ii]._height*scale;
							} else {
								yMin = 0;
								yMax = (this.Geometry.height-this._parent._contentCells[ii]._height*scale)/2;
							}
						}
					*/

						// generate random point within those bounds
						var xPos =  Math.round(Math.random()*(xMax-xMin))+xMin;
						var yPos = Math.round(Math.random()*(yMax-yMin))+yMin;

						
						
						
						
						// figure out it point falls in top or bottom region
						if (yPos < this.Geometry.distributionVertMid) {
							//top
							// check to see if point falls left of distribution start
							if (xPos < this.Geometry.distributionTopBegin) {
								if ((((this.Geometry.distributionVertMid-yPos)/this.Geometry.distributionVertMid)*this.Geometry.distributionTopBegin) > xPos) { // point falls outside distribution area, so push it along the x axis till its in
									xPos = (((this.Geometry.distributionVertMid-yPos)/this.Geometry.distributionVertMid)*this.Geometry.distributionTopBegin);
								}
							} else if(xPos+(this._parent._contentCells[ii]._width*scale) > this.Geometry.distributionTopEnd) {
								if ((((yPos)/this.Geometry.distributionVertMid)*(this.Geometry.width-this.Geometry.distributionTopEnd)) < xPos+(this._parent._contentCells[ii]._width*scale)-this.Geometry.distributionTopEnd ) { // point falls outside distribution area, so push it along the x axis till its in
									xPos = (((yPos)/this.Geometry.distributionVertMid)*(this.Geometry.width-this.Geometry.distributionTopEnd))+this.Geometry.distributionTopEnd-(this._parent._contentCells[ii]._width*scale);
								}
							}
						} else if ((yPos+(this._parent._contentCells[ii]._height*scale))> this.Geometry.distributionVertMid) {
							//bottom
							yPos += (this._parent._contentCells[ii]._height*scale);  // examine bottom of bounding area
							// check to see if point falls left of distribution start
							if (xPos < this.Geometry.distributionBottomBegin) {
								if ((((yPos-this.Geometry.distributionVertMid)/this.Geometry.distributionVertMid)*this.Geometry.distributionBottomBegin) > xPos ) { // point falls outside distribution area, so push it along the x axis till its in
									xPos = ((((yPos-this.Geometry.distributionVertMid)/this.Geometry.distributionVertMid)*this.Geometry.distributionBottomBegin));
								}
							} else if((xPos+(this._parent._contentCells[ii]._width*scale)) > this.Geometry.distributionTopEnd) {
								if ((((this.Geometry.distributionVertMid-(yPos-this.Geometry.distributionVertMid))/this.Geometry.distributionVertMid)*(this.Geometry.width-this.Geometry.distributionBottomEnd)) < xPos-this.Geometry.distributionBottomEnd+(this._parent._contentCells[ii]._width*scale)) { // point falls outside distribution area, so push it along the x axis till its in
									xPos = (((this.Geometry.distributionVertMid-(yPos-this.Geometry.distributionVertMid))/this.Geometry.distributionVertMid)*(this.Geometry.width-this.Geometry.distributionBottomEnd))+this.Geometry.distributionBottomEnd-(this._parent._contentCells[ii]._width*scale);
								}
							}
							yPos -= (this._parent._contentCells[ii]._height*scale);
						}

						xPos += (this._parent._thumbsFrame.getWidth()-this.Geometry.width)/2; // put in middle

						xPos += this._parent._contentCells[ii]._width*scale/2;
						yPos += this._parent._contentCells[ii]._height*scale/2;
						this._parent._contentCells[ii].addLayoutData(this._ID, new RACT.CommunityUI.LayoutData(this, xPos,yPos+this.Geometry.topPadding,1,scale, ii));

						if (!this.checkForOverlaps(this._parent._contentCells[ii])) {
							//this._parent._contentCells[ii]._placeholder.setStyle({'background': 'rgb(55,168,237)'});
							attempts = 80;
						}

					}
				}

			},

			// this kinda bugs out because i think it compares thumbs at thier own % value, rather than at the % of the target
			checkForOverlaps: function(target) {
				for (var ii=target._index; ii>target._index-this.OverlapAvoidance; ii--) { // only check prev 3

					var cell = this._parent._contentCells[((ii >= 0) ? ii : ii+this._parent._contentCells.length)];
					if (cell.getLayoutData(this) == undefined || cell.getLayoutData(this) == null || cell == target) { continue; }

					//var offset = this.getScreenOffsetByPercent(this.getXPercent(target)); // px accross the screen
					var targ = target.getLayoutData(this);
					var subj = cell.getLayoutData(this);

					if (this.overlapTwo2DVectors(targ._xPos, targ._xPos+target._width*targ._scale,
																			 subj._xPos, subj._xPos+cell._width*subj._scale)) {
						if (this.overlapTwo2DVectors(targ._yPos, targ._yPos+target._height*targ._scale,
																				 subj._yPos, subj._yPos+cell._height*subj._scale)){
							return true;
						}
					}
				}
				return false;
			},



			getOpacityFor: function(cell) {
				var index = (this._currentRotationIndex-cell.getLayoutData(this)._index);
				if (index < 0) {
					index += this._parent._contentCells.length-1;
				}
				return ((index >=this.VisibleAtOnce) ? 0 : (this.VisibleAtOnce-index)/this.VisibleAtOnce);
			},

			getXPosFor: function(cell) {
				return cell.getLayoutData(this)._xPos;
			},


			startRotate: function(from) {
				if (this._parent._currentLayout != this) return;

				// from might be broken!!! currently not used, may be best to remove alltogether
				this._currentRotationIndex = from || this._currentRotationIndex;

				for (var ii=0; ii<this._parent._contentCells.length; ii++) {
					this._parent._contentCells[ii]._div.setOpacity(this.getOpacityFor(this._parent._contentCells[ii]));
				}

				var targIndex = this._currentRotationIndex;
				for (var ii=0; ii<this.VisibleAtOnce; ii++) {
					//this._parent._contentCells[targIndex]._placeholder.setOpacity(ii/this.VisibleAtOnce);
					this._parent._contentCells[targIndex].setMorph(
						this._parent._contentCells[targIndex]._div, {
							style: 'opacity: 0;',  duration: this.getOpacityFor(this._parent._contentCells[targIndex])*this.FadeTime}
					);

					targIndex--;
					if (targIndex < 0) {
						targIndex = this._parent._contentCells.length-1;
					}
				}

				this.setUpdater( new PeriodicalExecuter(this.doNextRotationStep.bind(this),this.FadeTime/this.VisibleAtOnce) );
			},

			doNextRotationStep: function() {

				this._parent._contentCells[this._currentRotationIndex].setMorph(
					this._parent._contentCells[this._currentRotationIndex]._div, {
						style: 'opacity: 1;', duration:0 } //.5
				);

				(function() {
					this._parent._contentCells[this._currentRotationIndex].setMorph(
						this._parent._contentCells[this._currentRotationIndex]._div, {
							style: 'opacity: 0;',  duration:this.FadeTime });
						this._currentRotationIndex++;
						this._parent._sheetLayout.rotateDataBy(-1);
						if (this._currentRotationIndex >= this._parent._contentCells.length) {
							this._currentRotationIndex = 0;
						}
				}).bind(this).delay(.1);//.5
			},

			// periodic updater //
			setUpdater: function(ref) {
				this.clearUpdater();
				this.Updater = ref;
			},

			clearUpdater: function() {
				if (this.Updater != null) {
					this.Updater.stop();
				}
			},

			unloadView: function() {
				this.clearUpdater();
			}

		});


/* **************************************** */
/* ***** Sheet Layout ******* */
/* **************************************** */

		RACT.CommunityUI.SheetLayout = Class.create(RACT.CommunityUI.BaseLayout, {
			_ID : "SHEET",
			Depth : new RACT.Util.Vector3(.6,1,.1),  // Vector3(min, max, steps);
			OverlapAvoidance : 2, // number of previous cells to check for overlap with, higher numbers exponentially slow down execution

			_planes : [], // divs that will hold the thumbs
			_planeOffset: [],
			_planeLookup: {},

			_forePlane: null, // plane infront of all others to hold highlighed and focused cellspacing
			_forePlaneDepth: 0, // depth the plane is simulating
			_forePlaneCell: null, // cell the plane is holding

			_currentScreenOffset : .5,
			_layoutIsReady : false,

			_activeCellRef: null,

			Geometry: {
				sheetScreenWidthMultiplyer: 2// how many screen widths to spread the images over
			},

			setupView: function($super) {
				$super();

				for (var ii=this.Depth.x; ii<this.Depth.y; ii+=this.Depth.z) {
					var plane = new Element("div", {"class":"plane"});
					this._planes.push(plane);
					this._planeOffset.push(ii);
					this._parent._thumbsFrame.insert(plane);

				}
				this._forePlane = new Element("div", {"class":"plane"});
				this._forePlane.observe("mouseout", this.IEMouseoutFix.bind(this));
				this._parent._thumbsFrame.insert(this._forePlane);

				var xMin = 0;
				var xMax = null; //this._parent._thumbsFrame.getWidth()*this.Geometry.sheetScreenWidthMultiplyer;
				var yMin = 100;
				var yMax = 400;

				var depthMin = this.Depth.x;
				var depthMax = this.Depth.y;
				var depthStep = this.Depth.z;

				for (var ii=0; ii<this._parent._contentCells.length; ii++) {

					for (var attempts = 0; attempts<=80; attempts++) { // atempts to stop overlaps, currently a safety to stop infinate loop

						var rand = Math.random();//Math.round(Math.random()*2)/2;
						var yPos = Math.round(rand*(yMax-yMin))+yMin;
						var depth = Math.round(Math.random()*((depthMax-depthMin)/depthStep))*depthStep+depthMin;

						xMax = this._parent._thumbsFrame.getWidth()+(this._parent._thumbsFrame.getWidth()*(this.Geometry.sheetScreenWidthMultiplyer-1)*depth);
						var xPos =  Math.round((ii/this._parent._contentCells.length)*(xMax-xMin))+xMin; //(ii/this._parent._contentCells.length)+xMin;//

						this._parent._contentCells[ii].addLayoutData(this._ID, new RACT.CommunityUI.LayoutData(this, xPos,yPos,depth,depth));
						this._parent._contentCells[ii].getLayoutData(this)._xPercent = ii/this._parent._contentCells.length;
						if (!this.checkForOverlaps(this._parent._contentCells[ii])) {
							//this._parent._contentCells[ii]._placeholder.setStyle({'background': 'green'});
							attempts = 80;
						}
						//this._parent._contentCells[ii]._div.update(ii); // debugging count
					}
				}

				this.rotateDataBy(Math.floor(this._parent.DefaultOptions.numberOfImages/2)); // make the cells currently in view be in the 'middle' of the sheet
			},


			highlightCell: function(e) {
				
				if (this._parent._currentLayout != this || e.target._parentCell == null || !this._layoutIsReady) return;
				var cell = e.target._parentCell;
				
				if (!cell._isActive) return;

				this._activeCellRef = cell;

				

				//move cell to fore plane
				this._forePlaneDepth = cell.getLayoutData(this)._zPos;

				this._forePlane.setStyle({'left': this.getPlaneXPositionAtOffset(this._forePlaneDepth, this.getScreenOffsetByPercent(this._currentScreenOffset) )+'px'}); //
				this._forePlane.insert(cell._placeholder.remove());

				//cell._hitArea.observe("mouseout", function() { alert('test'); });

				cell._div.addClassName('hover');

				scale = 1.6;
				cell.setMorph(cell._div, {
						style: ''
							+ 'opacity: '+1+';'
							+ 'margin-top: -'+scale*cell._height/2+'px;'
							+ 'margin-left: -'+scale*cell._width/2+'px;'
							+ 'width:' +scale*cell._width+ 'px;'
							+ 'height:' +scale*cell._height+ 'px;'
						, duration:.2}
					);
			},

			clearCellHighlight: function(e) {
				//alert('clear cell highlight '+e.target._parentCell);
				if (this._parent._currentLayout != this || e.target._parentCell == null) return;
				this._activeCellRef = null;
				var cell = e.target._parentCell;
				cell._div.removeClassName('hover');
				this.removeCellFromForePlane(cell);
				this._parent.morphCellToLayout(e.target._parentCell, this, .2, false);
			},
			
			IEMouseoutFix: function(e) {
				if (this._activeCellRef == null) { return; }
				this.clearCellHighlight({target:this._activeCellRef._hitArea});
			},
			
			removeCellFromForePlane: function(cell) {
				//remove from fore plane
				this.planeLookup(cell.getLayoutData(this)._zPos).insert(cell._placeholder.remove());
			},

			loadLayout: function() {
				// put the cells into the planes
				for (var ii=0; ii<this._parent._contentCells.length; ii++) {
					var cell = this._parent._contentCells[ii];
					this.planeLookup(cell.getLayoutData(this)._zPos).insert(cell._placeholder.remove());
					cell._placeholder.setStyle({'left':
						this._parent._currentLayout.getXPosFor(cell)
						-this.getPlaneXPositionAtOffset( cell.getLayoutData(this)._zPos,  this.getScreenOffsetByPercent(this._currentScreenOffset))
						+'px'
					});
					//alert(this.getPlaneXPositionAtOffset( cell.getLayoutData(this)._zPos, this.getScreenOffsetByPercent(this._currentScreenOffset)));
				}
				this._layoutIsReady = false;
				(function(self) { self._layoutIsReady = true; }).delay(.5, this); // bit hacky, trying to squash a bug by delaying interation until loading morphs have finished
			},

			planeLookup: function(depth) {
				return this._planes[Math.round(((depth - this.Depth.x)/this.Depth.z))];
			},

			getOpacityFor: function(cell) {
				return 1;
				//return cell.getLayoutData(this)._zPos;
			},

			getXPosFor: function(cell) {
				return cell.getLayoutData(this)._xPos;
				return this.getXPositionAtOffset(cell, this.getScreenOffsetByPercent(this._currentScreenOffset));
			},

			updateOffsetPercent: function(percent) {
				this._currentScreenOffset = percent;

				var val;
				for (var ii=0; ii<this._planes.length; ii++) {
					val = this.getScreenOffsetByPercent(percent)*this._planeOffset[ii];
					this._planes[ii].setStyle({'left': val+'px'});
				}

				if (this._parent._currentLayout != this) return;
				val = this.getScreenOffsetByPercent(percent)*this._forePlaneDepth;
				this._forePlane.setStyle({'left': val+'px'});
				
			},

			// given an offset, calculate the xPos of the plane
			getPlaneXPositionAtOffset: function(planeDepth, offset) {
				var xPos = offset*planeDepth;
				return xPos;
			},


			// keeps defined layout positions and rotate which cell uses this position. move each cell up the order by [amout]
			// might be best to reverse this function to improve performance, currently always used with - values
			rotateDataBy: function(amount) {
				if (amount<0) { amount+=this._parent._contentCells.length; }
				for (var count=0; count<amount; count++) {
					var first = this._parent._contentCells[0].getLayoutData(this);
					for (var ii=0; ii<this._parent._contentCells.length-1; ii++) {
						this._parent._contentCells[ii].setLayoutData(this, this._parent._contentCells[ii+1].getLayoutData(this));
					}
					this._parent._contentCells[this._parent._contentCells.length-1].setLayoutData(this, first);
				}
			},


			// % cell falls across the layout expressed as a float between 0 and 1
			getXPercent: function(cell) {
				return cell.getLayoutData(this)._xPercent;
			},


			// given an offset, calculate the xPos of the cell
			getXPositionAtOffset: function(cell, offset) {
				var data =  cell.getLayoutData(this);
				var xPos = data._xPos+offset*data._zPos;
				return xPos;
			},

			getScreenOffsetByPercent : function(percent) {
				return -(percent) // % of mouse accross screen
						* (this._parent._thumbsFrame.getWidth()*(this.Geometry.sheetScreenWidthMultiplyer-1));
			},

			// this kinda bugs out because i think it compares thumbs at thier own % value, rather than at the % of the target
			checkForOverlaps: function(target) {
				for (var ii=target._index; ii>target._index-this.OverlapAvoidance; ii--) { // only check prev 3

					var cell = this._parent._contentCells[((ii >= 0) ? ii : ii+this._parent._contentCells.length)];
					if (cell.getLayoutData(this) == undefined || cell.getLayoutData(this) == null || cell == target) { continue; }

					var offset = this.getScreenOffsetByPercent(this.getXPercent(target)); // px accross the screen
					var targX = this.getXPositionAtOffset(target,offset);
					var subjX = this.getXPositionAtOffset(cell,offset);

					if (this.overlapTwo2DVectors(targX,targX+target._width*target.getLayoutData(this)._scale,
												 subjX, subjX+cell._width*cell.getLayoutData(this)._scale)){
						if (this.overlapTwo2DVectors(target.getLayoutData(this)._yPos,target.getLayoutData(this)._yPos+target._height*target.getLayoutData(this)._scale,
													 cell.getLayoutData(this)._yPos, cell.getLayoutData(this)._yPos+cell._height*cell.getLayoutData(this)._scale)){
							return true;
						}
					}
				}
				return false;
			},

			unloadView: function() {
				// put the cells into the planes
				for (var ii=0; ii<this._parent._contentCells.length; ii++) {
					var cell = this._parent._contentCells[ii];
					this._parent._thumbsFrame.insert(cell._placeholder.remove());
					cell._placeholder.setStyle({'left': this.getXPosFor(cell)+this.getPlaneXPositionAtOffset( cell.getLayoutData(this)._zPos,  this.getScreenOffsetByPercent(this._currentScreenOffset))+'px'});
				}
			}

		});



/* **************************************** */
/* ***** ContentCell ******* */
/* **************************************** */


		RACT.CommunityUI.ContentCell = Class.create({
			_parent : null,

			_div : null,
			_placeholder : null,
			_hitArea : null,
			_chrome : null,
			_detail : null,
			_content : null,

			_isActive: false,
			_detailData : null,

			_width: 0,
			_height: 0,

			_morphRefs: null,
			_currentLayout: null,
			_layoutValues : {},

			_index : null,
			_urlID : "",

		// -------- constructor ---------
			initialize : function(parent, index) {
					this._parent = parent;
					this._index = index;
					this._layoutValues = {};
					this._morphRefs = {};
					this.addMarkup(index);
			},

		// -------- add html markup --------

			addMarkup : function(count) {
				this._detail = new Element('div', {'class':'detail'}).insert(new Element('img', {'src':this._parent._baseHref+'images/tree.png', 'class':'tree'}));
				this._detail.setStyle({'opacity':0});
				this._hitArea = new Element('a', {'class':'hitArea'});
				this._chrome = new Element('img', {'src':this._parent._baseHref+'images/frame.png', 'class':'frame'});
				this._content = new Element('div', {'class':'content'+(count%2==0?' alt': '')});//.update(count);

				this._div = new Element('div', {'class':'thumb'}).insert(this._content).insert(this._chrome).insert(this._detail).insert(this._hitArea);
				this._placeholder  = new Element('div', {'class':'placeholder'}).insert(this._div);

				this._parent.getThumbsFrame().insert(this._placeholder);

				this._width = 125;
				this._height = 100;
				this._hitArea._parentCell = this._detail._parentCell =  this;

				
			},

			setupInterations: function() {
				this._hitArea.observe("mouseover", this._parent._sheetLayout.highlightCell.bind(this._parent._sheetLayout));
				this._hitArea.observe("mouseout",  this._parent._sheetLayout.clearCellHighlight.bind(this._parent._sheetLayout));
				this._hitArea.observe("click",  this._parent.applyCellFocus.bind(this._parent));
			},

			isDetailDataLoaded : function() {
				return (this._detailData != null);
			},


			getDetailData : function(callbackFunction, scope) {
				if (this.isDetailDataLoaded()) {
					callbackFunction.bind(scope, this)();
					return true;
				}
				// ajax in content
				var self = this;
				new Ajax.Request(this._parent._AjaxLocation+'/'+this._urlID +'?ajaxContext=true', {

				  onSuccess: function(response) {
						// Handle the response content...
						self._detailData = response.responseText;
						self._detail.insert(self._detailData);
						var targetA = self._detail.select('a.fundClose')[0];
						targetA.select('img')[0]._parentCell = self;
						targetA.observe("click", self._parent.removeCellFocus.bind(self._parent));
						targetA.href="#";
						callbackFunction(self);
				  }
				});

				return false;
			},

			addLayoutData: function(id, data) {
				this._layoutValues[id] = data;
			},

			setLayoutData: function(layout, data) {
				this._layoutValues[layout._ID] = data;
			},

			getLayoutData: function(layout) {
				return this._layoutValues[layout._ID];
			},


			pushData : function(data) {
				this._content.update(data);

				var urlChunks = this._content.select('a')[0].href.split('/');
				this._urlID = urlChunks[urlChunks.length-1];
				if (this._urlID.lastIndexOf("?") != -1) {
					this._urlID = this._urlID.split("?")[0];
				}
				this._content.select('a')[0].href = "#"; //ie8
			},

			setMorph: function(target,args) {
				var targID = this.getMorphRefIDFor(target);
				this.clearMorph(targID);
				this._morphRefs[targID] = new Effect.Morph(target, args);

			},

			clearMorph: function(targID) {
				if (this._morphRefs[targID] != null && this._morphRefs[targID] != undefined) {
					this._morphRefs[targID].cancel();
				}
			},

			//work around - js is a bit buggy using objects as keys
			getMorphRefIDFor: function(target) {
				return (
					(target == this._div) ? "div" :
					(target == this._placeholder) ? "placeholder" :
					(target == this._chrome) ? "chrome" :
					(target == this._detail) ? "detail" :
					(target == this._parent._thumbsFrame) ? "thumbsFrame" :
					"undefined");
			}
		});



/* **************************************** */
/* ***** Main ******* */
/* **************************************** */

		RACT.CommunityUI.Main = Class.create({

			_contentCells : [],
			_treeLayout : null,
			_sheetLayout: null,
			_detailLayout: null,
			_currentLayout : null,
			_focusIndex: 0,
			_data : [],

			images : [],
			placeholders: [],
			treePoints : [],
			sheetPoints : [],
			morphObjects : [],
			placeholderMorphObjects : [],
			inTreeView : true,

			screenXOffset : 0,

			_thumbsFrame : null,
			mouseMon: null,

			_growDelayFinished : false,

			DefaultOptions : {
				numberOfImages: 20,
				thumbsFrameHeight: 474
			},

			_AjaxLocation : "",
			_baseHref : "",

		// ------------- Initalisation -------------
			initialize : function(baseHref) {
				
				this._baseHref = baseHref;

				this._thumbsFrame = new Element('div', {'id':'thumbsFrame'});
				//$('body').insert(this._thumbsFrame);
				$('CFUI').insert(this._thumbsFrame);
				this._thumbsFrame.observe("mouseover", this.triggerSheetLayout.bind(this));

				this.scrapeData();

				for (var ii=0; ii<this.DefaultOptions.numberOfImages; ii++) {
					this.addImage(ii);
					if (ii < this._data.length) {
						this._contentCells[ii].pushData(this._data[ii]);
						this._contentCells[ii]._isActive = true;
					}
				}

				this._treeLayout = new RACT.CommunityUI.TreeLayout(this);
				this._sheetLayout = new RACT.CommunityUI.SheetLayout(this);
				this._detailLayout = new RACT.CommunityUI.DetailLayout(this);



				this._contentCells.each(function(cell) {

					cell.setupInterations();
				});

				this.jumpToLayout(this._treeLayout);
				this._treeLayout.startRotate.bind(this._treeLayout).defer(); // this is crap

				this.startMouseMon(this);
			},
			setAjaxLocation : function(href) {
				this._AjaxLocation = href;
			},

			scrapeData : function() {
				$("fundSummary").select('li').each((function(elm) {
					this._data.push(elm.remove().innerHTML);
				}).bind(this));
			},


			addImage : function(count) {
				this._contentCells.push(new RACT.CommunityUI.ContentCell(this, count));
			},


			getThumbsFrame : function() {
				return this._thumbsFrame;
			},

			// position objects on screen using [layout] with no morphing
			jumpToLayout: function(layout) {
				if (this._currentLayout != null) {
					this._currentLayout.unloadView();
				}

				for (var ii=0; ii<this._contentCells.length; ii++) {
					var cell = this._contentCells[ii];
					var data = cell.getLayoutData(layout);
					cell._placeholder.setStyle({
							'left' : data._xPos+'px',
							'top' : data._yPos+'px'
					});
					cell._div.setStyle({
						  'opacity' : data._zPos,
							'marginTop' : -cell._height*data._scale/2+'px',
							'marginLeft' : -cell._width*data._scale/2+'px',
							'width' : data._scale*cell._width+ 'px',
							'height' : data._scale*cell._height+ 'px'
					});
					cell._currentLayout = layout;
				}
				this._currentLayout = layout;
			},


			// morph position, scale and opacity of cells using [layout]
			morphToLayout: function(layout, duration) {

				duration = duration || .6;
				duration = (duration < 0) ? 0 : duration;

				if (this._currentLayout != null) {
					this._currentLayout.unloadView();
				}
				// placeholders
				for (var ii=0; ii<this._contentCells.length; ii++) {
					this.morphCellToLayout(this._contentCells[ii], layout, duration, true);
				}
				this._currentLayout = layout;
			},

			morphCellToLayout: function(cell, layout, duration, doPosition) {

					var data = cell.getLayoutData(layout);
					if (doPosition) {
						cell.setMorph(cell._placeholder, {
							style: ''
								+ 'left:' +layout.getXPosFor(cell)+'px;'
								+ 'top:' +data._yPos+'px;'
							, duration:duration
						});
					}

					cell.setMorph(cell._div, {
						style: ''
							+ 'opacity: '+layout.getOpacityFor(cell)+';'
							+ 'margin-top: -'+data._scale*cell._height/2+'px;'
							+ 'margin-left: -'+data._scale*cell._width/2+'px;'
							+ 'width:' +data._scale*cell._width+ 'px;'
							+ 'height:' +data._scale*cell._height+ 'px;'
						, duration:duration}
					);

					cell._currentLayout = layout;
			},



/* **************** Positioning Animators **************** */

			triggerFlyOut : function() {
				if (this._currentLayout == this._treeLayout) {
					this.morphToLayout(this._sheetLayout);
					
				} else {
					this.morphToLayout(this._treeLayout);
					this._treeLayout.startRotate.bind(this._treeLayout).delay(0.8);
				}
			},

			triggerSheetLayout: function() {
				if (this._currentLayout != this._treeLayout) { return; }
				this._sheetLayout.loadLayout();
				this.morphToLayout(this._sheetLayout);
			},

			triggerTreeLayout: function() {
				this.morphToLayout(this._treeLayout);
				this._treeLayout.startRotate.bind(this._treeLayout).delay(0.8);
			},

/* **************** Sheet Panning **************** */

			updateScreenX : function(percent) {
				percent = Math.max(0, Math.min(100, percent));//clamp
				percent = percent*1.2-.1; // edge padding
				this._sheetLayout.updateOffsetPercent(percent);
			},


			morphPos : function(cell,offset) {
				var xPos = this._sheetLayout.getXPositionAtOffset(cell, offset);
				cell._placeholder.setStyle({'left' : xPos+'px'});
			},



/* **************** Cell Enlarge *************** */

			applyCellFocus: function(e) {
				e.stop();

				if (this._currentLayout == this._detailLayout) { 
					return;
				}
				
				if (this._currentLayout == this._treeLayout || e.target._parentCell == null || !e.target._parentCell._isActive) return;

				this._currentLayout = this._detailLayout;
				var cell = e.target._parentCell;

				//hide hit area
				cell._hitArea.setStyle({'display':'none'});

				// set fore plane to position 0
				this._sheetLayout._forePlane.setStyle({'left': 0+'px'});

				// adjust position of cell before morphing
				cell._placeholder.setStyle({'left' : (cell.getLayoutData(this._sheetLayout)._xPos
													+this._sheetLayout.getScreenOffsetByPercent(this._sheetLayout._currentScreenOffset)*this._sheetLayout._forePlaneDepth)+'px'});

			
				//place detail in correct position
				// NOW IN WRONG PLACE PERHAPS
				cell._detail.setStyle({
					'top': (scale*cell._height/2-200)+'px',
					'left': (scale*cell._width/2-(950/2))+'px' // .5 width of detail pain
				});

				//reveal detail
				cell.setMorph(cell._detail, {
					style: 'opacity: 1'
					, duration:.4
					}
				);

				// put cell ontop
				cell._placeholder.setStyle({'z-index': 10});

				// move cell to middle of screen
				cell.setMorph(cell._placeholder, {
					style: ''
						+ 'left:' +this._thumbsFrame.getWidth()/2+'px;'
						+ 'top:' +200+'px;'
					, duration:.4
					}
				);

				//hide content
				cell.setMorph(cell._content, {
					style: 'opacity: 0;',
					duration : .4
					}
				);

				//hideChrome
				cell.setMorph(cell._chrome, {
					style: 'opacity: 0;',
					duration : .4
					}
				);

				// make sure cell is fully opaque
				cell.setMorph(cell._div, {
					style: 'opacity: 1;',
					duration : .4
					}
				);


				cell.getDetailData(this.growDetailViewWhenReady, this);
				
				this._growDelayFinished = false;
				(function() {
					this._growDelayFinished = true;
					this.growDetailViewWhenReady(cell);
				}).bind(this).delay(.4);
			},

			growDetailViewWhenReady : function(targetCell) {
				//alert("grow detail view when ready");
				if (this._growDelayFinished && targetCell.isDetailDataLoaded()) {
					this._growDelayFinished = false;
					this.growDetailView(targetCell);
				}
			},

			growDetailView : function(cell) {
						// remove "content"
						cell._content.setStyle({'display': 'none'});
			
						// enlarge cell
						cell.setMorph(cell._div, {
							style: ''
								+ 'width:' +990+'px;'
								+ 'margin-left: -'+990/2+'px;'
							, duration:.4
							}
						);

						// fix position of detail div
						cell.setMorph(cell._detail, {
							style: ''
								+ 'left: 0px;'
							, duration:.4
							}
						);

						(function() {
							// enlarge cell
							cell.setMorph(cell._div, {
								style: ''
									+ 'height:' +Math.max(cell._detail.getHeight(), this.DefaultOptions.thumbsFrameHeight)+'px;'
									+ 'margin-top: -'+200+'px;' // top of area
								, duration:.4
								}
							);
							
							//enlarge thumbs frameElement
							cell.setMorph(this._thumbsFrame, {
								style: ''
									+ 'height:' +Math.max(cell._detail.getHeight(), this.DefaultOptions.thumbsFrameHeight)+'px;'
								, duration:.4
								}
							);

							// fix position of detail div
							cell.setMorph(cell._detail, {
								style: ''
									+ 'top: '+0+'px;'
								  + 'left: 0px;'
								, duration:.4
								}
							);

						}).bind(this).delay(.4);
				},


			removeCellFocus: function(e) {
				e.stop();
				if (this._currentLayout == this._treeLayout || e.target._parentCell == null) return;

				var cell = e.target._parentCell;
				cell._div.removeClassName('hover');


				scale = 1.1;
				
				// shrink cell
				cell.setMorph(cell._div, {
					style: ''
							+ 'margin-top: -'+scale*cell._height/2+'px;'
							+ 'margin-left: -'+scale*cell._width/2+'px;'
							+ 'width:' +scale*cell._width+ 'px;'
							+ 'height:' +scale*cell._height+ 'px;'
					, duration:.4
					}
				);
				
				//shrink thumbs frame
				cell.setMorph(this._thumbsFrame, {
					style: ''
						+ 'height:' +this.DefaultOptions.thumbsFrameHeight+'px;'
					, duration: .4
					}
				);
					
				

				// fix position of detail div
				cell.setMorph(cell._detail, {
					style: ''
						+ 'top: '+(scale*cell._height/2-200)+'px;'
						+ 'left: '+(scale*cell._width/2-400)+'px;' // .5 width of detail pain
					, duration:.4
					}
				);

				(function() {
					//hide detail
					cell.setMorph(cell._detail, {style: 'opacity: 0', duration:.4});

					//show content
					cell._content.setStyle({'display': 'block'});
					cell.setMorph(cell._content, {style: 'opacity: 1;', duration: .4});

					//showChrome
					cell.setMorph(cell._chrome, {style: 'opacity: 1;',	duration: .4});

					this.morphCellToLayout(cell, this._sheetLayout, .8, true);
					this._currentLayout = this._sheetLayout;

					// set fore plane to position
					this._sheetLayout.updateOffsetPercent(this._sheetLayout._currentScreenOffset);

					// adjust position of cell before morphing
					cell._placeholder.setStyle({'left' : ((this._thumbsFrame.getWidth()/2)
													-this._sheetLayout.getScreenOffsetByPercent(this._sheetLayout._currentScreenOffset)*this._sheetLayout._forePlaneDepth)+'px'});
					this._sheetLayout.removeCellFromForePlane(cell);

					cell._hitArea.setStyle({'display':'block'});
				}).bind(this).delay(.4);
			},

/* **************** Mouse Monitor **************** */

			startMouseMon : function(self) {
				$('body').observe('mousemove', self.mouseMove.bind(self));
			},

			stopMouseMon : function() {
				return;
				$('body').stopObserving('mousemove');
			},

			mouseMove : function(e) {
				this.updateScreenX((e.pageX-this._thumbsFrame.cumulativeOffset()[0])/this._thumbsFrame.getWidth());
				if (this._currentLayout != this._sheetLayout) return;

				if (!this.isPointOverElement(e.pageX, e.pageY, this._thumbsFrame)) {
					this.triggerTreeLayout();
				}
			},

			// does an x,y point on the screen have the element under it
			isPointOverElement : function(x,y,elm) {
				var offset = elm.cumulativeOffset();
				return (! (x < offset[0] || x > offset[0]+elm.getWidth()
						|| y < offset[1] || y > offset[1]+elm.getHeight()) );
			}
		});

