/*
	GEM Products Inc "Ticker Control" javascript
	
	- This script is dependent on the prototype.js lib
	- This script is dependent on the scriptaculous.js lib (effect)
	
	
	Versioning Information
	--------------------------------------------------
	1.0: Original release of the script
		- Funtionalities not posted for release
			- Draggable options/functionality are still in development and should not be turned on in production environment
		- Tested and released functionalities:
			- Up, Down, Left or Right scrolling
			- Content sizes both larger and smaller than visible area are supported
			- Scroll speed (in pixels)
			- Frames per second (fps)
			- Start/Stop icon images (the image should be a javascript 'Image' object, not just a URL)
	--------------------------------------------------
	1.0.1
		- Added an "isScrolling()" method to GEM.Controls.Ticker that can be used to test if the ticker is current scrolling or not.

*/


if (!GEM)				var GEM = { };
if (!GEM.Controls) 			GEM.Controls = { };


if (typeof(Rectangle) == "undefined") {
	Rectangle = Class.create({ 
		x: 0, 
		y: 0, 
		width: 0, 
		height: 0,
		initialize: function(left, top, w, h) {
			this.x = left;
			this.y = top;
			this.width = w;
			this.height = h;
		},
		getRight: function() {
			return this.x + this.width;
		},
		setRight: function(absValue) {
			this.width = absValue - this.x;
		},
		getBottom: function() {
			return this.y + this.height;	
		},
		setBottom: function(absValue) {
			this.height = absValue - this.y;
		}
	});
}
if (typeof(Size) == "undefined") {
	Size = Class.create({
		width: 0,
		height: 0,
		initialize: function(w, h) {
			this.width = w;
			this.height = h;
		}
		
	});
}

if (typeof(Point) == "undefined") {
	Point = Class.create({
		x: 0,
		y: 0,
		initialize: function(x, y) {
			this.x = x;
			this.y = y;
		},
		offsetWithSize: function(size) {
			this.x += size.width;
			this.y += size.height;
		}
	});
}

GEM.Controls.DragInfo = Class.create({ 
	vector: 0.0,
	direction: null,
	directionVector: 0.0,
	lastTimestamp: null,
	
	initialize: function() {
		
	}
});

GEM.Controls.TickerControlState = {
	Visible: 0,
	Hidden: 1,
	Showing: 2,
	Hiding: 3
};


GEM.Controls.TickerDirection = {
	Up: "Up",
	Down: "Down",
	Left: "Left",
	Right: "Right",
	IsVerticalDirection: function(direction) {
		return (direction == GEM.Controls.TickerDirection.Up || direction == GEM.Controls.TickerDirection.Down);	
	},
	IsHorizontalDirection: function(direction) {
		return (direction == GEM.Controls.TickerDirection.Left || direction == GEM.Controls.TickerDirection.Right);	
	}
};

GEM.Controls.TickerControlPlacement = {
	TopRight: 0,
	BottomRight: 1
};

GEM.Controls.Ticker = Class.create({ 
	scrollIncrement: 1,
	fps: 30,	
	scrollElement: null,
	contentInstances: [],
	scrollTimer: null,
	scrollDirection: GEM.Controls.TickerDirection.Up,
	originalContent: null,
	originalStyle: null,
	debugCallbackFunction: null,
	controlPlacement: GEM.Controls.TickerControlPlacement.BottomRight,
	showsControls: true,
	iconImageStart: null,
	iconImageStop: null,
	canDrag: false,
	
	_controlOptions: {
		backgroundColor: "#003767",
		backgroundOpacity: 0.25,
		height: 15,
		itemWidth: 30,
		fontColor: "white",
		fontFamily: "Arial",
		fontSize: "8pt",
		
		animationDuration: 0.5,
		autoHide: true,
		autoHideTimer: null,
		state: GEM.Controls.TickerControlState.Visible,
		autoHideDelay: 0.5,
		controlHideEffect: null,
		controlShowEffect: null,
		
		isDragging: false,
		dragPosition: new Point(0,0),
		dragVector: 0.0,
		dragVectorDirection: 0.0,
		dragDirection: null,
		dragDecelerateTimer: null,
		dragStopTimestamp: null,
		decelerateFrameCount: 0,
		decelerateStartSpeed: 0
	},
	_uiElements: {
		controlContainer: null,
		controlBackground: null,
		controlController: null,
		controlStartOrStop: null,
		
		contentOffset: new Point(0, 0),
		contentSize: new Size(0, 0),
		lastScrollTime: null
	},
	
	/*
	/// POSSIBLE PARAMETERS LIST (IN ORDER)
		1) THE HTML DOM ELEMENT TO BE USED AS THE TICKER CONTAINER
		2) INITIAL TICKER PROPERTY VALUES
		3) A CALLBACK FUNCTION USED FOR DEBUG LOG MESSAGES
	*/
	initialize: function(element) {
		
		if (!arguments[0]) {
			throw("Must pass an element to the first parameter of GEM.Controls.Ticker constructor");
		}
		
		this.scrollElement = $(element);
		
		if (arguments[1]) {
			//this.scrollDirection = arguments[1];
			Object.extend(this, arguments[1]);
		}
		if (arguments[2]) {
			this.debugCallbackFunction = arguments[2];	
		}
		
		this._log("Init Ticker instance..");
		
		this.originalContent = this.scrollElement.innerHTML;
		this.originalStyle = Object.extend({ }, this.scrollElement.style);
		
		this.reset();
	},
	/* RESET'S THE STATE OF THE TICKER TO INTIAL STARTING VALUES, AND RE-CREATES ALL THE NECESSARY HTML ELEMENTS USED BY THE TICKER */
	reset: function() {
		
		/// reset/clear any current instances...
		this.contentInstances = [];
		
		var layoutInfo = this.scrollElement.getLayout(true);
		var scrollElementSize = { width: layoutInfo.get("width"), 
							 	  height: layoutInfo.get("height"), 								  
								  innerWidth: layoutInfo.get("width") - layoutInfo.get("padding-left") - layoutInfo.get("padding-right"), 
								  innerHeight: layoutInfo.get("height") - layoutInfo.get("padding-top") - layoutInfo.get("padding-bottom"), 
								  innerLeft: layoutInfo.get("padding-left"), 
								  innerTop: layoutInfo.get("padding-top") };
	
		/// clear the contents of the scrolling element
		this.scrollElement.innerHTML = "";
		this.scrollElement.style.padding = "0px";
	
		/// make sure the style properties of the elements are setup correctly
		this.scrollElement.style.overflow = "hidden";
		this.scrollElement.style.position = "relative";		/// MUST SET THIS TO 'RELATIVE' (RATHER THAN DEFAULT OR 'STATIC' BECAUSE THE CHILD ELEMENTS WILL BE SET TO 'ABSOLUTE')
		this.scrollElement.ticker = this;
		
		/// in the context the element's mouse events 'this' refers to the element
		this.scrollElement.onmouseover = (function(e) { this.ticker.clearAutoHideTimer(); this.ticker.showControls(); });
		this.scrollElement.onmouseout = (function(e) { this.ticker.clearAutoHideTimer(); this.ticker.autoHideStartTimer(); });
		this.scrollElement.onmousemove = (function(e) { if (this.ticker._controlOptions.state == GEM.Controls.TickerControlState.Hidden) this.ticker.showControls(); this.ticker._drag(e); } );
		this.scrollElement.onmousedown = (function(e) { this.ticker.startDragging(e); });
		this.scrollElement.onmouseup = (function(e) { this.ticker.stopDragging(e); });
		
		/// create a 'template' child element that will be used to copy/create all the instances needed to use for scrolling
		var childTemplate = Builder.node("div");	/// !!! DO NOT SET THE WIDTH OR HEIGHT PROPERTY OF THE NEW DIV. THIS CAUSES INCORRECT MEASUREMENT CALCULATIONS LATER ON. INSTEAD, SIMPLY LEAVE UNSPECIFIED
		childTemplate.innerHTML = this.originalContent;
		childTemplate.style.position = "absolute";
		childTemplate.style.left = "0px";//scrollElementSize.innerLeft.toString()+"px;"
		childTemplate.style.top = "0px";//scrollElementSize.innerTop.toString()+"px;"
		childTemplate.style.display = "block";
		//childTemplate.style.width = scrollElementSize.width.toString()+"px;"
		//childTemplate.style.height = scrollElementSize.height.toString()+"px;"
		
		//childTemplate.style.border = "1px dashed blue";
		
		if (this._isVerticalScroll()) {
			childTemplate.style.paddingLeft = this.originalStyle.paddingLeft;
			childTemplate.style.paddingRight = this.originalStyle.paddingRight;
			/*
			childTemplate.style.paddingTop = this.originalStyle.paddingTop;
			childTemplate.style.paddingBottom = this.originalStyle.paddingBottom;
			*/
		}
		else {
			childTemplate.style.paddingLeft = this.originalStyle.paddingLeft;
			childTemplate.style.paddingRight = this.originalStyle.paddingRight;
			childTemplate.style.paddingTop = this.originalStyle.paddingTop;
			childTemplate.style.paddingBottom = this.originalStyle.paddingBottom;
		}
		//childTemplate.style.paddingBottom = this.originalStyle.paddingBottom;
		
		var numberOfInstancesNeeded = 2;
		//// add the template to DOM, get the height, then remove from DOM.
		this.scrollElement.appendChild(childTemplate);
		var childHeight = childTemplate.getHeight();
		var childWidth = childTemplate.getWidth();
		
		this._uiElements.contentSize = new Size(childWidth, childHeight);
		this._uiElements.contentOffset = new Point(0, 0);
		
		if (this._isVerticalScroll()) {
			numberOfInstancesNeeded = 1 + Math.ceil(scrollElementSize.height / childHeight);
		}
		else {
			numberOfInstancesNeeded = 1 + Math.ceil(scrollElementSize.width / childWidth);
		}
		this.scrollElement.removeChild(childTemplate);
		
		this._log("\
Resetting ticker. Scroll Direction: "+this.scrollDirection+"\n\
Calculated needing "+numberOfInstancesNeeded+" instances. Container Size: "+scrollElementSize.width+"/"+scrollElementSize.height+".	\n\
Template Size: w="+scrollElementSize.innerWidth+", h="+scrollElementSize.innerHeight+"	\n\
Template Position: x="+scrollElementSize.innerLeft+", y="+scrollElementSize.innerTop+" \n\
Size Info: "+scrollElementSize.toString()
+"\n-----------------------");
							
		var debugString = "Setting initial position of child elements:\n";
		var tmpEl, startingTop, startingLeft;
		for (var i = 0; i < numberOfInstancesNeeded; ++i) {
			tmpEl = childTemplate.clone(true);	/// make a copy of the template. Pass in true as 1st parameter indicating that we also want all child elements cloned!!
			tmpEl.id = "contentInstance_"+i.toString();
			//tmpEl.innerHTML = i.toString()+": "+tmpEl.innerHTML;
			
			/// must add the elements to the HTML DOM document before we can get any measurements or dimensions based on the element....
			this.contentInstances.push(tmpEl);
			this.scrollElement.appendChild(tmpEl);	
			
			if (this._isVerticalScroll()) {
				startingTop = (this.scrollDirection == GEM.Controls.TickerDirection.Up) ? (childHeight * i) : -(childHeight * i);
				tmpEl.style.top = startingTop.toString()+"px";
				debugString += i.toString()+": "+startingTop.toString()+"\t";
			}
			else {
				startingLeft = (this.scrollDirection == GEM.Controls.TickerDirection.Left) ? (childWidth * i) : -(childWidth * i);
				tmpEl.style.left = startingLeft.toString()+"px";
				debugString += i.toString()+": "+startingLeft.toString()+"\t";
			}
		}	
		
		this._log(debugString);
		
		if (this.showsControls) {
			
		
			var indicatorWidth = scrollElementSize.width;
			
			/// create and place the elements to control the ticker
			this._uiElements.controlContainer = Builder.node("div", { name: "controlContainer" } );
			this._uiElements.controlContainer.style.position = "absolute";
			this._uiElements.controlContainer.style.left = "0px";
			this._uiElements.controlContainer.style.top = (scrollElementSize.height - this._controlOptions.height)+"px";
			this._uiElements.controlContainer.style.width = indicatorWidth.toString()+"px";
			this._uiElements.controlContainer.style.height = this._controlOptions.height.toString()+"px";
			this._uiElements.controlContainer.style.padding = "0px";
			this._uiElements.controlContainer.style.margin = "0px";
			/*
			this._uiElements.controlContainer.style.
			this._uiElements.controlContainer.style.
			*/
			
			this._uiElements.controlBackground = Builder.node("div", { name: "controlBackground", style: "position:absolute; height: "+this._controlOptions.height.toString()+"px; top: 0px; width: "+indicatorWidth.toString()+"px; background-color: "+this._controlOptions.backgroundColor+"; " });
			this._uiElements.controlBackground.setOpacity(this._controlOptions.backgroundOpacity);
			this._uiElements.controlController = Builder.node("table", { name: "controlController", cellpadding: 0, cellspacing: 0, border: 0, style: "position:absolute; height: "+this._controlOptions.height.toString()+"px; top: 0px; width: "+indicatorWidth.toString()+"px; " } );

			var tbody = Builder.node("tbody");
			var spacerCell = Builder.node("td", { height: this._controlOptions.height } );
			spacerCell.innerHTML = "&nbsp;";
			var controlCell = Builder.node("td", { height: this._controlOptions.height, width: this._controlOptions.itemWidth, align: "center", valign: "bottom" } );
			controlCell.ticker = this;
			controlCell.onclick = (function() { this.ticker.toggleScrolling(); });
			controlCell.innerHTML = "Start";
			controlCell.style.color = "white";
			controlCell.style.lineHeight = "normal";
			controlCell.style.fontFamily = this._controlOptions.fontFamily;
			controlCell.style.fontSize = this._controlOptions.fontSize
			controlCell.style.color = this._controlOptions.fontColor;
			controlCell.style.cursor = "pointer";
			
			this._uiElements.controlStartOrStop = controlCell;
			var tRow = Builder.node("tr");	
							  
			tRow.appendChild(spacerCell);
			tRow.appendChild(controlCell);	
			tbody.appendChild(tRow);
			
			this._uiElements.controlController.appendChild(tbody);
			this._uiElements.controlContainer.appendChild(this._uiElements.controlBackground);
			this._uiElements.controlContainer.appendChild(this._uiElements.controlController);
			
			this.scrollElement.appendChild(this._uiElements.controlContainer);
			
			this._controlOptions.state = GEM.Controls.TickerControlState.Visible;
			var ticker = this;
			setTimeout((function(){ ticker.hideControls(); }), 2000);
			
			//this._log(this._uiElements.controlController.innerHTML);
		}
	},
	/* BEGINS THE TICKER'S SCROLLING ANIMATION */
	startScrolling: function() {
		if (this.scrollTimer) 
			return;
			
		this._log("Start Scrolling");
		this._scrollIncrement();
		
		if (this.iconImageStop && this.iconImageStop.src) {
			this._uiElements.controlStartOrStop.innerHTML = "";
			this._uiElements.controlStartOrStop.appendChild(Builder.node("img", { border: 0, src: this.iconImageStop.src } ));
		}
		else {
			this._uiElements.controlStartOrStop.innerHTML = "Stop";
		}
	},
	/* STOPS THE TICKERS SCROLLING ANIMATION */
	stopScrolling: function() {
		if (!this.scrollTimer)
			return;
			
		this._log("Stop Scrolling");
		
		clearTimeout(this.scrollTimer);
		this.scrollTimer = null;
		this._uiElements.lastScrollTime = null;
		
		if (this.iconImageStart && this.iconImageStart.src) {
			this._uiElements.controlStartOrStop.innerHTML = "";
			this._uiElements.controlStartOrStop.appendChild(Builder.node("img", { border: 0, src: this.iconImageStart.src } ));
		}
		else {
			this._uiElements.controlStartOrStop.innerHTML = "Start";
		}
	},
	toggleScrolling: function() {
		if (this.scrollTimer) 
			this.stopScrolling();
		else
			this.startScrolling();
	},
	isScrolling: function() {
		return (this.scrollTimer != null);
	},
	/* REVERTS THE CONTENTS OF THE TICKER'S HTML DOM ELEMENT TO THE INITIAL STARTING CONTENTS */
	revertToOriginal: function() {
		this.scrollElement.innerHTML = this.originalContents;
		this.scrollElement.style = this.originalStyle;
	},
	/* HIDE THE TICKER'S USER INTERFACE CONTROLS */
	hideControls: function() {
		if (this._controlOptions.state == GEM.Controls.TickerControlState.Hidden || this._controlOptions.state == GEM.Controls.TickerControlState.Hiding || this._controlOptions.state == GEM.Controls.TickerControlState.Showing)
			return;
			
		//this._log("Hiding Controls");
		var ticker = this;
		this._controlOptions.controlHideEffect = new Effect.Opacity(this._uiElements.controlContainer, { from: 1.0, to: 0.0, duration: this._controlOptions.animationDuration, afterFinish: (function() { ticker._controlOptions.state = GEM.Controls.TickerControlState.Hidden; ticker._controlOptions.controlHideEffect = null; }) });
	},
	/* SHOWS THE TICKERS USER INTERFACE CONTROLS */
	showControls: function() {
		if (this._controlOptions.state == GEM.Controls.TickerControlState.Visible || this._controlOptions.state == GEM.Controls.TickerControlState.Hiding || this._controlOptions.state == GEM.Controls.TickerControlState.Showing)
			return;
			
		//this._log("Showing Controls");
		this.clearAutoHideTimer();
		var ticker = this;
		this._controlOptions.controlShowEffect = new Effect.Opacity(this._uiElements.controlContainer, { from: 0.0, to: 1.0, duration: this._controlOptions.animationDuration, afterFinish: (function() { ticker._controlOptions.state = GEM.Controls.TickerControlState.Visible; ticker._controlOptions.controlShowEffect = null; }) });
	},
	autoHideStartTimer: function() {
		if (!this._controlOptions.autoHide || this.indicatorState == GEM.Controls.TickerControlState.Hidden || this.indicatorState == GEM.Controls.TickerControlState.Hiding || this.indicatorState == GEM.Controls.TickerControlState.Showing) 
			return;
			
		var ticker = this;
		this._controlOptions.autoHideTimer = setTimeout(function(){ ticker.hideControls(); }, ticker._controlOptions.autoHideDelay * 1000);
	},
	clearAutoHideTimer: function() {
		clearTimeout(this._controlOptions.autoHideTimer);
		this._controlOptions.autoHideTimer = null;
	},
	startDragging: function(e) {
		if (!this.canDrag)
			return;
			
		if (!e) e = window.event;
		
		this._log("Starting dragging...");
		
		this._clearDragDecelerate();
		this._controlOptions.dragPosition.x = e.clientX;
		this._controlOptions.dragPosition.y = e.clientY;
		
		this._controlOptions.isDragging = true;
	},
	stopDragging: function(e) {
		if (!this.canDrag)
			return;
			
		this._log("Stopping dragging...");
		
		this._controlOptions.isDragging = false;
		this._controlOptions.dragStopTimestamp = new Date();
		this._controlOptions.decelerateStartSpeed = this._controlOptions.dragVectorDirection;
		
		this._dragDecelerate();
	},
	_drag: function(e) {
		if (!this.canDrag || !this._controlOptions.isDragging)
			return;
			
		var offset = new Size(e.clientX - this._controlOptions.dragPosition.x, e.clientY - this._controlOptions.dragPosition.y);
		
		if (this._isVerticalScroll()) {
			this._controlOptions.dragVectorDirection = offset.height;
			this._controlOptions.dragDirection = (offset.height > 0) ? GEM.Controls.TickerDirection.Down : GEM.Controls.TickerDirection.Up;
		}
		else {
			this._controlOptions.dragVectorDirection = offset.width;
			this._controlOptions.dragDirection = (offset.width > 0) ? GEM.Controls.TickerDirection.Right : GEM.Controls.TickerDirection.Left;
		}
		this._shiftContents(this._controlOptions.dragDirection, this._controlOptions.dragVectorDirection);
		
		this._log("DRAG: Mouse coords: ("+e.clientX.toString()+", "+e.clientY.toString()+") Previous offset: "+offset.width.toString()+", "+offset.height.toString()+". Drag direction: "+this._controlOptions.dragDirection+". Speed: "+this._controlOptions.dragVectorDirection);
		
		this._controlOptions.dragPosition.x = e.clientX;
		this._controlOptions.dragPosition.y = e.clientY;
		
		/// find the distance travelled...
		this._controlOptions.dragVector = Math.sqrt(Math.pow(offset.width, 2) + Math.pow(offset.height, 2));
		

	},
	_dragDecelerate: function() {
		/// keep moving the contents while decelerating the movement until speed drops to 0...
		
		++this._controlOptions.decelerateFrameCount;
		
		
		
		var timeSinceDragStopped = new Date().getTime() - this._controlOptions.dragStopTimestamp.getTime();
		var maxDecelTime = 1.0;	/// max amount of time it should take to decelerate to 0...
		var maxFrames = maxDecelTime / this.fps;
		
		
		/// get the speed decrement
		var decrement = Math.abs(Math.floor(this._controlOptions.decelerateStartSpeed / maxFrames));
		
		var stopMovement = false;
		
		if (this._controlOptions.dragDirection == GEM.Controls.TickerDirection.Up || this._controlOptions.dragDirection == GEM.Controls.TickerDirection.Left) {
			this._controlOptions.dragVectorDirection += decrement;
			if (this._controlOptions.dragVectorDirection > -1.0) {
				stopMovement = true;	
			}
		}
		else {
			this._controlOptions.dragVectorDirection -= decrement;
			if (this._controlOptions.dragVectorDirection < 1.0) {
				stopMovement = true;	
			}
		}
		
		
		/// see if movement should stop
		if (stopMovement) {
			this._controlOptions.dragVectorDirection = 0.0;
			this._controlOptions.dragDirection = null;
			this._log("End drag deceleration");
			this._clearDragDecelerate();
		}
		/// perform deceleration operations.
		else {
			var frameInterval = this._getFrameInterval();
			this._shiftContents(this._controlOptions.dragDirection, this._controlOptions.dragVectorDirection);
			var ticker = this;
			this._controlOptions.dragDecelerateTimer = setTimeout((function() { ticker._dragDecelerate(); }), frameInterval);	
		}
		
	},
	_clearDragDecelerate: function() {
		if (this._controlOptions.dragDecelerateTimer) {
			clearTimeout(this._controlOptions.dragDecelerateTimer);
			this._controlOptions.dragDecelerateTimer = null;
		}
		this._controlOptions.dragVectorDirection = 0.0;
		this._controlOptions.dragDirection = null;
	},
	_scrollIncrement: function() {
		//var offsetSize = new Size();
		
		/// FOR SLOWER BROWSERS OR MOBILE DEVICES THAT CAN'T KEEP UP WITH THE DESIRED FRAMES PER SECOND...
		/// CALCULATE THE # OF FRAMES THAT SHOULD HAVE PASSED SINCE THE LAST SCROLL FRAME, AND THEN SCROLL THE CONTENTS BY # OF FRAMES TIMES THE SCROLL INCREMENT...
		var now = new Date();
		var frameTime = 1 / this.fps;
		var timeElapsed = (this._uiElements.lastScrollTime == null) ? frameTime * 1000 : now.getTime() - this._uiElements.lastScrollTime.getTime();
		var framesPassed =  Math.floor((timeElapsed / 1000) / frameTime);
		if (framesPassed <= 0) framesPassed = 1;
		var distance = this.scrollIncrement * framesPassed;
		this._uiElements.lastScrollTime = now;
		
		//this._log("Calculated frame distance "+distance.toString()+". FPS: "+this.fps.toString()+". Frames passed: "+framesPassed.toString()+". Time elapsed (ms): "+timeElapsed);
		
		//offsetSize.width = (this.scrollDirection == GEM.Controls.TickerDirection.Left) ? -this.scrollIncrement : ((this.scrollDirection == GEM.Controls.TickerDirection.Right) ? this.scrollIncrement : 0);
		//offsetSize.height = (this.scrollDirection == GEM.Controls.TickerDirection.Up) ? -this.scrollIncrement : ((this.scrollDirection == GEM.Controls.TickerDirection.Down) ? this.scrollIncrement : 0);
		
		//this._shiftContentsWithSize(offsetSize);
		
		this._shiftContents(this.scrollDirection, (this.scrollDirection == GEM.Controls.TickerDirection.Up || this.scrollDirection == GEM.Controls.TickerDirection.Left) ? -distance : distance);
		//this._shiftContents(this.scrollDirection, (this.scrollDirection == GEM.Controls.TickerDirection.Up || this.scrollDirection == GEM.Controls.TickerDirection.Left) ? -this.scrollIncrement : this.scrollIncrement);
		
		var ticker = this;
		this.scrollTimer = setTimeout(function(){ ticker._scrollIncrement(); }, this._getFrameInterval());
	},
	_shiftContentsWithSize: function(offsetSize) {
		
		
		var visibleHeight = this.scrollElement.getHeight();
		var visibleWidth = this.scrollElement.getWidth();
		var childElement, newPosition, childPosition, childSize;
		
		childSize = new Size(this.contentInstances[0].getWidth(), this.contentInstances[0].getHeight());
		
		this._uiElements.contentOffset.offsetWithSize(offsetSize);
		
		/// 'loop' the content offset so that the value of the offset never exceeds the combined size of all content instances...
		if (Math.abs(this._uiElements.contentOffset.y) > (childSize.height * this.contentInstances.length)) {
			this._uiElements.contentOffset.y = (this._uiElements.contentOffset.y % childSize.height);	/// set new offset to be equal to the remainder of current offset / total content height of all instances...
		}
		if (Math.abs(this._uiElements.contentOffset.x) > (childSize.width * this.contentInstances.length)) {
			this._uiElements.contentOffset.x = (this._uiElements.contentOffset.x % childSize.width);	/// set new offset to be equal to the remainder of current offset / total content height of all instances...
		}
		
		for (var i = 0; i < this.contentInstances.length; ++i) {
			childElement = this.contentInstances[i];
			if (this._isVerticalScroll()) {
				childPosition = new Point(this._uiElements.contentOffset.x, this._uiElements.contentOffset.y + (childSize.height * i));
			}
			else {
				childPosition = new Point(this._uiElements.contentOffset.x + (childSize.width * i), this._uiElements.contentOffset.y);
				
				if (this.scrollDirection == GEM.Controls.TickerDirection.Left && childPosition.x < -childSize.width) {
					childPosition.x = childSize.width * (this.contentInstances.length - 1);	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.x.toString()+") to left with left value "+newPosition);
				}
				else if (this.scrollDirection == GEM.Controls.TickerDirection.Right && childPosition.x > visibleWidth) {
					childPosition.x = visibleWidth - (childSize.width * (this.contentInstances.length));	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.x.toString()+") to right with left value "+newPosition);
				}
			}
			childElement.setStyle({ left: childPosition.x.toString()+"px", top: childPosition.y.toString()+"px" });
		}
		
	},
	_shiftContents: function(direction, distance) {
		 // =-=-=-=-=-=-=-=-  ORIGINAL OPERATIONS HERE  ==-=-=-=-=-=-=-=-=-  //
		var visibleHeight = this.scrollElement.getHeight();
		var visibleWidth = this.scrollElement.getWidth();
		var childElement, newPosition, childPosition, childHeight, childWidth;
		var inc = distance;// (this.scrollDirection == GEM.Controls.TickerDirection.Up || this.scrollDirection == GEM.Controls.TickerDirection.Left) ? -this.scrollIncrement : this.scrollIncrement;
//		this._log("Scrolling: "+inc.toString());
		
		var debugString = "";
		
		
		for (var i = 0; i < this.contentInstances.length; ++i) {
			childElement = this.contentInstances[i];
			
			if (this._isVerticalScroll(direction)) {
				childPosition = parseFloat(childElement.getStyle("top"));
				childHeight = childElement.getHeight();		/// TODO: NEED TO REPLACE THIS WITH LOGIC TO GET THE ACTUAL SIZE OF THE CONTENTS RATHER THAN THE SIZE OF THE CHILD'S 'VISIBLE AREA'
				newPosition = childPosition + inc;	
				if (direction == GEM.Controls.TickerDirection.Up && newPosition < -childHeight) {
					newPosition = childHeight * (this.contentInstances.length - 1);	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.toString()+") to bottom with top value "+newPosition);
				}
				else if (direction == GEM.Controls.TickerDirection.Down && newPosition > visibleHeight) {
					newPosition = visibleHeight - (childHeight * (this.contentInstances.length));	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.toString()+") to top with top value "+newPosition);
				}
				childElement.setStyle({ top: newPosition.toString()+"px" });
				
				
			}
			else {
				childPosition = parseFloat(childElement.getStyle("left"));
				childWidth = childElement.getWidth();
				newPosition = childPosition + inc;	
				
				if (direction == GEM.Controls.TickerDirection.Left && newPosition < -childWidth) {
					newPosition = childWidth * (this.contentInstances.length - 1);	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.toString()+") to left with left value "+newPosition);
				}
				else if (direction == GEM.Controls.TickerDirection.Right && newPosition > visibleWidth) {
					newPosition = visibleWidth - (childWidth * (this.contentInstances.length));	
					this._log("resetting instance # "+i.toString()+" (current pos "+childPosition.toString()+") to right with left value "+newPosition);
				}
				childElement.setStyle({ left: newPosition.toString()+"px" });
			}
				
			
			debugString += i.toString()+": "+newPosition.toString()+"\t";
		}		
	},
	_getFrameInterval: function() { return (1 / this.fps) * 1000; },
	_log:function(message) {
		if (this.debugCallbackFunction) {
			this.debugCallbackFunction(message);	
		}
	},
	_isHorizontalScroll: function(direction) {
		if (!direction)
			direction = this.scrollDirection;
			
		return (direction == GEM.Controls.TickerDirection.Left || direction == GEM.Controls.TickerDirection.Right);
	},
	_isVerticalScroll: function(direction) {
		if (!direction)
			direction = this.scrollDirection;
			
		return (direction == GEM.Controls.TickerDirection.Up || direction == GEM.Controls.TickerDirection.Down);
	}

});
