/*-------------------------------------------------------------------- 
Scripts for positioning overlaid components: menus, tooltips, overlays, loading indicators, etc.
Version: 1.0, 01.18.2008

By: Maggie Costello Wachs (maggie@filamentgroup.com)
	http://www.filamentgroup.com
		
Copyright (c) 2007 Filament Group
Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)

Dependencies:
	jQuery library
	utilities.js *** included below
	
Requirements:
	- the object is absolutely positioned with CSS
	
Parameters (defaults noted with * where applicable):
	@referrer = the link (or other element) used to show the overlaid object 
	@settings = can override four of the Position defaults:
		- posX/Y: where the top left corner of the object should be positioned in relation to its referrer.
				X: left*, center, right
				Y: top, center, bottom*
		- offsetX/Y: the number of pixels to be offset from the x or y position.  Can be a positive or negative number.
		- alignH/V: where the entire menu should appear in relation to its referrer.
				Horizontal: left*, right
				Vertical: up, down*
		- detectH/V: detect the viewport horizontally / vertically
		- linkToFront: copy the menu link and place it on top of the menu (visual effect)
--------------------------------------------------------------------*/

jQuery.fn.positionTo = function(settings) { 
	var el = $(this);
	var settings = jQuery.extend({
		targetObject: 'window',
		offsetX: 0, // offset is always calculated from the top left corner of the target object
		offsetY: 0, 
		alignH: 'left',
		alignV: 'top', 
		detectH: true, // check for horizontal collision detection?
		detectV: true // and vertical?
	}, settings);
	
	var to = $(settings.targetObject);
	
	// set location relative to the target object	
	var helper = $('<div class="positionHelper"></div>');	
	helper.appendTo('body').append(el).css({ 
		position:'absolute', 
		left: to.offset().left + settings.offsetX, 
		top: to.offset().top + settings.offsetY, 
		width: 0, 
		height: 0 });
		
	// align the widget
	if (settings.alignV == 'top') {
		el.css({ top: 0, bottom: 'auto' });
		if (settings.detectV && !fitVertical(el)) {
			el.css({ bottom: 0, top: 'auto' });
		}
	} 
	else {
		el.css({ bottom: 0, top: 'auto' });
		if (settings.detectV && !fitVertical(el)) {
			el.css({ top: 0, bottom: 'auto' });
		}
	};
	
	// and horizontally
	if (settings.alignH == 'left') {
		el.css({ left: 0, right: 'auto' });
		if (settings.detectH && !fitHorizontal(el)) {
			el.css({ right: 0, left: 'auto' });
		}
	} 
	else {
		el.css({ right: 0, left: 'auto' });
		if (settings.detectH && !fitHorizontal(el)) {
			el.css({ left: 0, right: 'auto' });
		}
	};

	return this;
};


/*-------------------------------------------------------------------- 
Utility functions
Version: 1.0, 01.18.2008

By: Maggie Costello Wachs (maggie@filamentgroup.com)
	http://www.filamentgroup.com
		
Copyright (c) 2007 Filament Group
Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)

Dependencies:
	jQuery library
--------------------------------------------------------------------*/

function sortBigToSmall(a, b) { return b - a; };

function getScrollTop(){
	return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
};

function getScrollLeft(){
	return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
};

function getWindowHeight(){
	var de = document.documentElement;
	return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
};

function getWindowWidth(){
	var de = document.documentElement;
	return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
};

// getTotalWidth/Height finds the width/height of an element including padding
jQuery.fn.getTotalWidth = function(){
	return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth'));
};

jQuery.fn.getTotalHeight = function(){
	return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom'));
};

// assign random ids to a single element or a group of child elements
jQuery.fn.setRandomId = function(settings){
	var settings = jQuery.extend({
		children: null, // selectors for child elements (acts only on the caller element by default)
		attribute: null // attribute, if not id (default)
	}, settings);
	
	var thisAttr = settings.attribute || 'id';
		
	var setId = function(el){
		var newId = 'id_' + Math.floor(Math.random()*9999);
		el.attr(thisAttr, newId);
	};
	
	if (settings.children) {
		$(this).find(settings.children).each(setId($(this)));
	}
	else { setId($(this)); }
	
	return $(this);
};

$.fn.slideIn = function(){
	return $(this).css('opacity', 0).show(400, function(){$(this).fadeTo(1, 200);});
};

$.fn.slideOut = function(){
	return $(this).fadeTo(0, 200, function(){$(this).hide(400).css('opacity', 1);});
};

// Test to see if this element will fit in the viewport
/* Parameters:
	@el = element to position, required
	@leftOffset / @topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none')
*/
function fitHorizontal(el, leftOffset){
	var leftVal = parseInt(leftOffset) || $(el).offset().left;
	return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0);
};

function fitVertical(el, topOffset){
	var topVal = parseInt(topOffset) || $(el).offset().top;
	return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0);
};