/**
 * jquery.c42.helpers.js
 * 
 * Copyright (c) 2011 Code 42 Software, Inc.
 * 
 * A group of helper methods to use with the app, most useful when building widgets, but can be used for 
 * working with views...
 * 
 * Only put simple jQuery functions/methods in this file. It should have no dependencies.
 *
 * @author Vann Ek, vann@code42.com
 * @author Marshall Scorcio, marshall@code42.com
 */

(function($) {

	/**
	 * Calls the given function when the enter key is pressed in a text field.
	 */
	var ENTER_KEYCODE = 13;
	$.fn.enter = function(func) {
		return this.keydown(function(event) {
			if (event.which === ENTER_KEYCODE) {
				return func.call(this, event);
			}
		});
	};
	
	$.getSecureUrl = function(url) {
		url = $.trim(url);
		url = url.replace(/^http:/, 'https:');
		// This regex matches ([protocol://]hostname:)(port)
		url = url.replace(/^((?:[a-zA-Z]+:\/\/)?[^\/:]+:)([0-9]+)/, function(match, beforePort, port) {
				return beforePort + port.replace(/80$/, '85');
		});
		return url;
	};
	
	/**
	 * Return the width of a scrollbar in the current browser
	 */
	(function() {
		var width = null;
		$.scrollbarWidth = function() {
			if (width !== null) {
				return width;
			}
			var outerDiv = $('<div></div>')
				.appendTo('body')
				.css({
					width: '50px',
					height: '50px',
					overflow: 'hidden',
					position: 'absolute',
					top: '-200px',
					left: '-200px'
				});
			var innerDiv = $('<div>')
				.appendTo(outerDiv)
				.css({
					height: '100px'
				});
			var origWidth = innerDiv.innerWidth();
			outerDiv.css('overflow', 'scroll');
			innerDiv.css('width', '100%');
			var scrollWidth = innerDiv.innerWidth();
			width = (origWidth - scrollWidth);
			return width;
		};
	})();	
	
	/**
	 * Returns the location hostname excluding the subdomain. i.e. devw1.crashplanpro.com:14185 returns crashplanpro.com
	 */
	$.domain = function() {
		var domain = window.location.hostname;
		var split = domain.split('.');
		if (split.length > 2) {
			domain = domain.replace(split[0]+'\.', '');
		}
		if (domain === 'localhost') {
			return null;
		}
		return domain;
	};
	
	/*
		renders an element based on the subdomain, useful for testing and staging...
	*/
	$.renderIfSubdomain = function(elem,regexp) {
		
		var subdomainAry = window.location.hostname.split(".");
		if (subdomainAry.length < 3) {
			// we'll assume that the elements should be removed if not on a subdomain
			$(elem).remove();
		} else {
			var subdomain = subdomainAry[0];
			subdomain.match(regexp) ? $(elem).show() : $(elem).remove();
		}
	};
	
	$.fn.renderIfSubdomain = function(regexp) {
		this.each(function(index) {
	  		$.renderIfSubdomain($(this),regexp);
		});
	};
	
	/**
	 *	$.interpret("Someone's name is: ${name}",{name: "Joe"}) => "Someone's name is Joe"
	 *	A quick interpolation method for using jsrepeater syntax in strings, it's an
	 *	elegant solution to js concatenation.
	 */
	$.interpret = function (str, o) {
		if (!str) {
			return '';
		}
		return str.replace(/\$\{([^{}]*)}/g,
			function (a, b) {
				var r = o[b];
				return typeof r === 'string' || typeof r === 'number' ? r : a;
			}
		);
	};
	
	/**
	 * @return the maximum z index
	 */ 
	$.maxZ = function(selector) {
		selector = selector || 'body > *:not(.tipsy)';
		var maxZ = Math.max.apply(null,$.map($(selector), function(e,n){
			if($(e).css('position')=='absolute')
				return parseInt($(e).css('z-index'))||1 ;
			})
		);
		return maxZ;
	};
	
	/**
	 * @return the minimum z index
	 */ 
	$.minZ = function(selector) {
		selector = selector || 'body > *';
		var minZ = Math.min.apply(null,$.map($(selector), function(e,n){
			if($(e).css('position')=='absolute')
				return parseInt($(e).css('z-index'))||1 ;
			})
		);
		return minZ;
	};
	
	/**
	 * Handle the placeholder attribute in browsers that do not support it.
	 */
	$.fn.placeholder = function() {
		if ('placeholder' in document.createElement('input')) {
			return this;
		}
					
		function showPlaceholder(el, text) {
			$(el)
				.addClass('placeholder')
				.val(text);
		}
		
		function hidePlaceholder(el) {
			$(el)
				.removeClass('placeholder')
				.val('');
		}
		
		this.each(function() {
			var $this = $(this),
				text = $this.attr('placeholder');
			
			if (!text) {
				return;
			}
			
			$this.focus(function() {
				if ($this.val() == text) {
					hidePlaceholder(this);
				}
			});
			
			$this.blur(function() {
				if ($.trim($this.val()) == '') {
					showPlaceholder(this, text);
				}
			});
			
			if (document.activeElement != this) {
				showPlaceholder(this, text);
			}
			
			$this.closest('form')
				.bind('reset', function() {
					setTimeout(function() { showPlaceholder($this, text); }, 1);
				})
				.bind('submit', function() {
					if ($this.val() == text) {
						$this.val('');
					}
				});
		});
		
		return this;
	};
	
	/**
	 *	Repositions an element to a select position on the dom
	 *	$('#foo').reposition('position', $('#bar'));
	 *
	 *	@param [position] can be:
	 *
	 *	- reset : resets elem absolutely to the top left
	 *	- topLeft : alias to reset
	 *	- topRight : top right of viewport
	 *	- topCenter : top center of viewport
	 *	- center : center of viewport
	 *	- bottomLeft : bottom left of viewport
	 *	- bottomCenter : bottom center of viewport
	 *	- bottomRight : bottom right of viewport
	 * 
	 * @param [of] is the container of the positioning:
	 *
	 *	- defaults to window
	 *	- pass in a string as a selector
	 *
	 */
	$.fn.reposition = function(pos, of) {
		return reposition($(this), pos, of);
	};
	
	function reposition(elem, position, of) {
		elem = $(elem);
		of = $(of || window);
		
		var moveTo = {
			_trueWidth: function() {
				var paddingLeft = elem.css('padding-left').split("px")[0];
				var paddingRight = elem.css('padding-right').split("px")[0];
				return parseInt(paddingLeft) + elem.width() + parseInt(paddingRight);
			},
			
			_trueHeight: function() {
				var paddingTop = elem.css('padding-top').split("px")[0];
				var paddingBottom = elem.css('padding-bottom').split("px")[0];
				return parseInt(paddingTop) + elem.height() + parseInt(paddingBottom);
			},
			
			reset: function() {
				// var _position = of.get(0) == window ? 'absolute' : 'relative';
				return elem
					.css({
						position: 'absolute',
						left: 0,
						top: 0,
						marginLeft:0,
						marginTop:0
					})
					.addClass('reposition-reset');
			},

			topLeft: function() {
				return this.addClass('reposition-topleft').reset();
			},

			topRight: function() {
				this.reset();
				var pushLeft = (of.width()-this._trueWidth())+"px";
				return elem
					.css({
						left: pushLeft
					})
					.addClass('reposition-topright');
			},

			topCenter: function() {
				this.reset();
				var pushRight = (of.width()/2)+"px";
				var pullLeft = "-"+(this._trueWidth()/2)+"px";
				return elem
					.css({
						left: pushRight,
						marginLeft: pullLeft,
						top:0,
						marginTop:0
					})
					.addClass('reposition-topcenter');
			},

			center: function() {
				this.reset();
				var pullLeft = "-"+(this._trueWidth()/2)+"px";
				var pullUp = "-"+(this._trueHeight()/2)+"px";
				return elem
					.css({
						left: '50%',
						marginLeft: pullLeft,
						top: '50%',
						marginTop: pullUp
					})
					.addClass('reposition-center');
			},

			bottomLeft: function() {
				this.reset();
				var pushTop = (of.height()-this._trueHeight())+"px";
				return elem
					.css({
						top: pushTop
					})
					.addClass('reposition-bottomleft');
			},

			bottomRight: function() {
				this.reset();
				var pushLeft = (of.width()-this._trueWidth())+"px";
				var pushTop = (of.height()-this._trueHeight())+"px";
				return elem
					.css({
						left: pushLeft,
						top: pushTop
					})
					.addClass('reposition-bottomright');
			},

			bottomCenter: function() {
				this.reset();
				var pushTop = (of.height()-this._trueHeight())+"px";
				var pushRight = (of.width()/2)+"px";
				var pullLeft = "-"+(this._trueWidth()/2)+"px";
				return elem
					.css({
						top: pushTop,
						left: pushRight,
						marginLeft: pullLeft
					})
					.addClass('reposition-bottomcenter');
			}
		};

		if (of.get(0) != window) {
			elem.prependTo(of);
		}
		moveTo[position]();
		return elem;
	}
	
	/*
	 * Stacks an element on top of all other elements;
	 */
	$.fn.stackTop = function() {
		return $(this).css('z-index', $.maxZ() + 1);
	};
	
	$.fn.stackDown = function() {
		return $(this).css('z-index', $.minZ() - 1);
	};

	/**
	 * Recursively loop through the attributes of an object and delete the null and undefined.
	 * This is needed to remove null values from REST responses so we can "extend" with default values.
	 */
	$.removeNullValues = function(obj, recurse) {
		if (!obj) {
			return; // Nothing to do
		}
		for (var attr in obj) {
			var value = obj[attr];
			if (value == null) {
				// value is null or undefined (undefined == null when double-equals are used)
				delete obj[attr];
			} else if (recurse && typeof(value) == 'object') {
				// Notice the recursion here
				$.removeNullValues(value);
			}
		}
	};
		
})(jQuery);

