﻿// Improvement Ideas
//	Allow for docking to occur in more than just the bottom right corner

function Docker(htmlElement, animationDuration, isDockableDelegate) {
	this.animationDuration = animationDuration || 0;
	this.element = htmlElement;
	this.isDockableDelegate = isDockableDelegate;
	this.autoDockDelay = 2.5 * 1000; //seconds
	this.dockSizeX = this.dockSizeY = 50;
	this.isDocked = false;
	
	this._oldRegion = null; 
	this._dockTimeout = null;
	
	this.Overlay = new YAHOO.widget.Overlay(
		this.element,
		{
			visible: true
		}
	);

	this.Show = function() {
		this.Render();
		this.Reposition();
	}
	
	this.AnimateShow = function() {
		this.Render();
		this.AnimateReposition();
	}
	
	this.Render = function () {
		this.Overlay.render();
		
		if (this.IsDockable())
			this.DelayedDock();

		YAHOO.util.Event.addListener(this.element, 'mouseover', this.eventThrottle(this.onMouseOver, 200), this, true);
		YAHOO.util.Event.addListener(this.element, 'mouseout', this.eventThrottle(this.onMouseOut, 200), this, true);
		YAHOO.util.Event.addListener(window, 'resize', this.eventThrottle(this.onWindowResize, 200), this, true);
		YAHOO.util.Event.addListener(window, 'scroll', this.eventThrottle(this.onWindowScroll, 200), this, true);
	}

	// I recommend you don't alter this function unless you understand javascript closures, and anonymous functions very well
	this.eventThrottle = function(callbackMethod, delay) {
		return function(callbackMethod, delay, context) {
			return function(e) {
				if (callbackMethod._cbId)
					window.clearTimeout(callbackMethod._cbId);

				//	Ideally instead of passing a custom event to the function we would just pass the event (e) around
				//	Unfortunately, in IE7 at least, while e is populated at the time it's passed, once the callbackMethod
				//	is called, it has lost all it's values.  The current fix is just passing a custom event object that
				//	is similar to the javascript event object.  If you can find a way to pass the real event (e) object,
				//	that would be AWESOME!
				var target = YAHOO.util.Event.getTarget(e, true);
				var event = {
					altKey: e.altKey,
					ctrlKey: e.ctrlKey,
					shiftKey: e.shiftKey,
					metaKey: e.metaKey,
					button: e.button,
					clientX: e.clientX,
					clientY: e.clientY,
					screenX: e.screenX,
					screenY: e.screenY,
					target: target,
					srcElement: target
				};

				callbackMethod._cbId = window.setTimeout(
					function(target, mouseXY) {
						return function() {
							callbackMethod._cbId = null;
							callbackMethod.call(context, event);
						}
					} (event),
					delay
				);
			}
		} (callbackMethod, delay, this)
	}

	this.onMouseOver = function(e) {
		this.UnDock();
	}

	this.onMouseOut = function(e) {
		var region = YAHOO.util.Dom.getRegion(this.element);

		var top = region.top;
		var left = region.left;
		var bottom = region.bottom;
		var right = region.right;

		var mouseXY = YAHOO.util.Event.getXY(e);
		var mX = mouseXY[0];
		var mY = mouseXY[1];

		if (mX > left && mX < right && mY > top && mY < bottom)
			return;

		this.DelayedDock();
	}

	this.onWindowResize = function(e) {
		this.Reposition();

		if (this.IsDockable()) {
			this.DelayedDock();
		} else {
			this.UnDock();
		}
	}

	this.onWindowScroll = function(e) {
		this.Reposition();
	}

	this.Reposition = function() {
		this.setLocation(this.getNewLocation());
	}

	this.AnimateReposition = function() {
		var region = this.getRegion();
		var newLocation = this.getNewLocation();

		this.Animate({
			left: { from: newLocation[0], to: newLocation[0] },
			top: { from: newLocation[1] + region.height, to: newLocation[1] },
			height: { from: 0, to: region.height }
		});
	}
	
	this.IsDockable = function() {
		return this.isDockableDelegate 
					? this.isDockableDelegate() 
					: function() { return false; };
	}
	
	this.DelayedDock = function() {
		if (this.isDocked || !this.IsDockable()) return;

		this.CancelDock();
		
		this._dockTimeout = setTimeout(
			function(el) {
				return function() {
					el.Dock();
				}
			} (this),
			this.autoDockDelay
		);
	}
	
	this.Dock = function() {
		if (this.isDocked || !this.IsDockable()) return;
	
		var region = this._oldRegion = this.getRegion();
		
		this.Animate({
			width: { from: region.width, to: this.dockSizeX },
			height: { from: region.height, to: this.dockSizeY },
			left: { from: region.left, to: region.left + region.width - this.dockSizeX },
			top: { from: region.top, to: region.top + region.width - this.dockSizeY }
		});
		
		this.isDocked = true;
	}

	this.UnDock = function() {
		this.CancelDock();
		
		if (!this.isDocked) return;

		var newLocation = this.getNewLocation();

		this.Animate({
			width: { to: this._oldRegion.width },
			height: { to: this._oldRegion.height },
			left: { to: newLocation[0] - this._oldRegion.width + this.dockSizeX },
			top: { to: newLocation[1] - this._oldRegion.height + this.dockSizeY }
		});

		this.isDocked = false;
	}

	this.CancelDock = function() {
		window.clearTimeout(this._dockTimeout); 
	}
	
	this.Animate = function(attributes) {
		var anim = new YAHOO.util.Anim(this.element, attributes, this.animationDuration, YAHOO.util.Easing.easeOut);
		anim.animate();
	}

	this.getNewLocation = function() {
		var viewportWidth = YAHOO.util.Dom.getViewportWidth();
		var viewportHeight = YAHOO.util.Dom.getViewportHeight();
		var visibleRegion = YAHOO.util.Dom.getClientRegion();

		var region = this.getRegion();

		var width = region.width;
		var height = region.height;

		return [viewportWidth - width + visibleRegion.left,
				viewportHeight - height + visibleRegion.top];
	}

	this.getLocation = function() {
		var region = this.getRegion();
		return [region.left, region.top];
	}

	this.setLocation = function(location) {
		return this.Overlay.moveTo(location[0], location[1]);
	}

	this.getRegion = function() {
		return YAHOO.util.Dom.getRegion(this.element);
	}
}

var shoppingBag = null;
YAHOO.util.Event.onDOMReady(
	function() {
		shoppingBag = new Docker(YAHOO.util.Dom.get('statusbar'), 0.5, function() { return (YAHOO.util.Dom.getViewportWidth() < 1150) });
		if (shoppingBag) {
			shoppingBag.AnimateShow();
		}
	}
);