(function($) {
	// Globals
	global_ext_sub_nav_timeout = ''
	
	// Custom function extensions for jquery
	$.fn.extend({
		
		// center - center object on screen
		center: function() {
			return this.each(function() {
				var top = ($(window).height() - $(this).outerHeight()) / 2;
				var left = ($(window).width() - $(this).outerWidth()) / 2;
				$(this).css({position:'fixed', margin: 0, top: (top > 0 ? top : 0)+'px', left: (left > 0 ? left : 0)+'px'});
			});
		},
		
		// fadeInSafe - fadeIn or show depending if native opacity is supported
		fadeInSafe: function(duration,callback) {
			if ($.support.opacity) {
				return this.fadeIn(duration,callback);
			} else {
				if (callback && typeof(callback) === "function") callback();
				return this.show();
			}
		},
		
		// fadeOutSafe - fadeOut or hide depending if native opacity is supported
		fadeOutSafe: function(duration,callback) {
			if ($.support.opacity) {
				return this.fadeOut(duration,callback);
			} else {
				if (callback && typeof(callback) === "function") callback();
				return this.hide();
			}
		},
		
		// animateSafe - Avoid opacity issues using animateSafe (somewhat intelligent)
		animateSafe: function(args, duration, callback) {
			
			// Default duration
			if (typeof duration == 'function') {
				callback = duration;
				duration = 600; // 600 is the equivalent to 'slow', 200 being 'fast'
			} else if (typeof duration != 'number') {
				duration = 600;
			}
			
			// If opacity is supported just execute normally
			if ($.support.opacity) {
				return this.animate(args, duration, callback);
				
			// Opacity not natively supported
			} else {
				
				// If opacity is one of the arguments
				if (typeof args.opacity != 'undefined') {
					// Placeholder var callback use
					var selector = this;
					// If target opacity is 0
					if (args.opacity == 0) {
						// If there is more than just opacity being animated (using custom size function)
						if (Object.size(args) > 1) {
							// Sync hiding element till AFTER elements are animated
							setTimeout(function() { selector.hide() }, duration); // Attempt Sync with rest of animation
							
						// Just hide it if only argument
						} else { selector.hide(); }
					} else if (args.opacity == 1) {
						// If target opacity is 1 than show immediately show it
						selector.show();
					} else {
						// Any opacity value thats between 0 and 1 we just show
						selector.show();
					}
					delete args.opacity; // Remove opacity argument from list
					return this.animate(args, duration, callback); // Animate the rest normally
				} else {
					return this.animate(args, duration, callback); // No opacity so just continue on
				}
			}
		},
		
		// createMask - create a mask to insert before an object
		createMask: function(options) {
		
			var defaults = {
				'id': '',
				'class': '',
				'z-index': 'auto',
				'parent' : '',
				'css': ''
			}
			
			// Merge options in with defaults
			$.extend(defaults, options);
			
			if(defaults['class'] != '') defaults['class'] = ' '+defaults['class'];
			
			// Create new mask element
			var new_mask = $('<span class="dynamic_mask'+defaults['class']+'" id="'+defaults['id']+'"></span>');
			
			// Positioning parent passed in or not
			if(defaults['parent'] != '') {
				// Add it before the current element
				$(defaults['parent']).before(new_mask);
			} else {
				this.before(new_mask);
			}
			
			if(defaults['css'] != '') new_mask.css(defaults['css']); 
			
			this.css('z-index', defaults['z-index']);
			
			// Add custom class with the same z-index as selector
			new_mask.css('z-index', parseInt(this.css('z-index')) - 1);
			
			// Save mask to data of selector
			return this.data('mask', new_mask);
		},
		
		// Simple cascading fading function
		// Usage: $('div.sections, div.content').cascade({duration: 500, delay: 1000});
		// Option for executing in absolute order ( isArray : true ):
		// Usage: $(['div.sections', 'div.content']).cascade({duration: 500, delay: 1000, isArray : true});
		cascade: function (args) {
			// Arguments
			var duration = typeof(args.duration) == 'undefined' ? 600 : args.duration;
			var initialDelay = typeof(args.initialDelay) == 'undefined' ? 0 : args.initialDelay;
			var delay = typeof(args.delay) == 'undefined' ? 500 : args.delay;
			var callback = typeof(args.callback) == 'undefined' ? null : args.callback;
			var type = typeof(args.type) == 'undefined' ? 'fadeIn' : args.type;
			var isArray = typeof(args.isArray) == 'undefined' ? false : args.isArray;
			
			// Preserve this for callbacks
			var selector = this;
			
			// Fade in order of passed in selectors
			return selector.each(function (n) {
				// For executing in order
				var hack = '';
				if (isArray) { 
					hack += this;
				} else {
					hack = $(this);
				}

				if ( (n == (selector.length - 1)) && (callback != null) ) {
					if(type == 'fadeIn') $(hack).delay((delay*n) + initialDelay).fadeInSafe(duration, callback);
					if(type == 'fadeOut') $(hack).delay((delay*n) + initialDelay).fadeOutSafe(duration, callback);
				} else {
					if (type == 'fadeIn') $(hack).delay((delay*n) + initialDelay).fadeInSafe(duration);
					if (type == 'fadeOut') $(hack).delay((delay*n) + initialDelay).fadeOutSafe(duration);
				}
			});
		},
		
		// Simple crossfade function for two elements
		// Usage $('div.current').crossfade({ 'target' : $('div.new'), 'duration' : 1000);
		crossFade: function (args) {
			if(typeof(args.target) == 'undefined') {
				console.log('crossFade missing target parameter');
				return this;
			}
			var target = args.target;
			var duration = typeof(args.duration) == 'undefined' ? 1000 : args.duration;
			var delay = typeof(args.delay) == 'undefined' ? 1 : args.delay;
			var callback = typeof(args.callback) == 'undefined' ? null : args.callback;
			var reveal = typeof(args.callback) == 'undefined' ? false : args.reveal;
			
			var fadeIn_duration = duration;
			if(reveal) fadeIn_duration = 1;
			setTimeout(function(){ target.fadeInSafe(fadeIn_duration, function() {
					if (callback && typeof(callback) === "function") callback();
				});
			}, delay);
			
			return this.fadeOutSafe(duration);
		},
		
		// Help with disjointed sub-navs ( mouse-in mouse-out behavior that has to span a gap )
		// Note: ALL parameters but timeout MUST be filled
		// Usage: $('#top_nav_1').sub_nav({
		//		'target' : 'sub_nav_1',
		//		'timeout' : 2000
		//		'action_func' : function() { $('#sub_nav_1').show },
		//		'close_func' : function() { $('#sub_nav_1').hide }
		// });
		sub_nav: function(args) {
			//global_ext_sub_nav_timeout
			var target = typeof(args.target) == 'undefined' ? '' : args.target;
			var timeout = typeof(args.timeout) == 'undefined' ? 1000 : args.timeout;
			var action_func = typeof(args.action_func) == 'undefined' ? null : args.action_func;
			var close_func = typeof(args.close_func) == 'undefined' ? null : args.close_func;
			
			if (action_func && close_func) {
				// Mouse enter and leave of base object
				this.hover( function() {
					clearTimeout(global_ext_sub_nav_timeout);
					action_func();
				}, function() {
					clearTimeout(global_ext_sub_nav_timeout);
					global_ext_sub_nav_timeout = setTimeout(close_func, timeout);
				});
				// Mouse enter and leave of target object ( sub-nav )
				$(target).hover(function() {
					clearTimeout(global_ext_sub_nav_timeout);
				}, function() {
					global_ext_sub_nav_timeout = setTimeout(close_func, timeout);
				});
			}
		},
		
		// preload - preload images
		// Usage: $(['image1.jpg', '/subfolder/image3.jpg']).preload(path_to_image_folder)
		preload: function(path_to_image_folder) {
			this.each(function() {
				document.createElement('img').src = path_to_image_folder+this;
			});
		},
		
		// Shuffle - shuffle the children of a selected element
		// Usage: $(ul#main_list).shuffle()
		shuffle: function(f) {
			var filter = typeof(f) == 'undefined' ? '' : f;
			var selector = this;
			var len = this.children().length;
			
			this.children(filter).each(function() {
				var rand = Math.floor(Math.random() * len);
				selector.children(':eq('+rand+')').after($(this).clone(true));
				$(this).remove();
			});
		}
	});
	
})(jQuery);
