////////////////////////////////////////////////////////////////
// Helper Functions
////////////////////////////////////////////////////////////////

/**
 * Instead of: x = ( aFunc() == null ) ? aFunc() : y;
 *        Use: x = $.ifNull( aFunc(), y );
 */
jQuery.ifNull = function(val, defaultVal)
{
	if( val == null )
	{
		return defaultVal;
	}
	return val;
}


/**
 * Instead of: x = ( aFunc() == null ) ? aFunc() : y * 100;
 *        Use: x = $.ifNull( aFunc(), function(){ y * 100 } );
 * 
 * This method should be faster than the regular old $.ifNull, since this one lazily evaluates the default value.
 * $.ifNull will evaluate the default value whether it is used or not.
 * 
 */
jQuery.ifNullFunc = function(val, defaultValFunc)
{
	if( val == null )
	{
		return defaultValFunc();
	}
	return val;
}


/**
 * $.namespace('scSite.this.may.not.exist.yet');
 * typeof scSite.this.may.not.exist.yet == 'object'
 * 
 * $.namespace('you.can.also, pass.more.than, one.variable');
 */
jQuery.namespace = function(space, root)
{
	if( root == null )
	{
		root = window;
	}
	
	var vlist = space.split(',');
	for( var j = 0; j < vlist.length; j++ )
	{
		var theVar = root;
		var vp = $.trim(vlist[j]).split('.');

		for( var i = 0; i < vp.length; i++ )
		{
			if( theVar[vp[i]] == null )
			{
				theVar = theVar[vp[i]] = {};
			}
			else
			{
				theVar = theVar[vp[i]];
			}
		}
	}

}


/**
 * Returns true if obj is a jQuery object
 * http://benalman.com/projects/jquery-misc-plugins/#isjquery
 */
jQuery.isjQuery = function( obj ){
  return obj && obj.hasOwnProperty && obj instanceof jQuery;
};


// Works like YAHOO's version
jQuery.isObject = function( o )
{
	return o && (typeof o === "object" || jQuery.isFunction( o )) || false;	
}



/**
 * If el is a jQuery object, then the first element is returned, otherwise it is returned as-is.
 */
jQuery.toEle = function(el)
{
	return el.get ? el.get(0) : el;
}


/**
 * Can be used on html DOM elements or jQuery objects.
 */
jQuery.elEmpty = function(o)
{
	return o == null || o.length == 0;
}

/**
 * The oppisite of jQyery.elEmpty.
 * Can be used on html DOM elements or jQuery objects.
 */
jQuery.elFull = function(o)
{
	return o != null && o.length != 0;
}



jQuery.hasOwnProperty = function(o, p)
{
	return o && o.hasOwnProperty(p);
}


/*
 * Detects if the "relativeTarget" of an event is part of a list of elements or a descendant of one.
 *
 * $('.parent').mouseout($.ifRelTargetInside
 * (
 *		true,                 // In this case, true means that '.parentThingy' will be included in the check
 *		'.childThingy',
 *		 function(){ Mouse is over parentThingy or childThingy },
 *		 function{}{ "Mouse is outside of '.parentThingy' and childThingy" }
 * );
 */
jQuery.ifRelTargetInside = function(ev)
{
	var elms = [];
	var funcs = [];

	for( var i = 0; i < arguments.length; i++ )
	{
		if( typeof arguments[i] != 'function' )
		{
			elms.push(arguments[i]);
		}
		else
		{
			funcs.push(arguments[i]);
		}
	}

	var insideFunc = $.ifNull(funcs[0], jQuery.noop);
	var outsideFunc = $.ifNull(funcs[1], jQuery.noop);

	return function(ev)
	{
		var $t = $(ev.relatedTarget);

		for( var i = 0; i < elms.length; i++ )
		{
			var el = elms[i];
			if( el === true )
			{
				el = this;
			}

			if( $t.index(el) >= 0 || $t.parents().index(el) >= 0 )
			{
				return insideFunc(ev);
			}
		}

		if( funcs[1] )
		{
			return outsideFunc(ev);
		}
	};
};



/**
 * Formats a number with commas and optionally a dollar sign.
 */
jQuery.dollarFormat = function(val, withSign)
{
	if( arguments.length < 2 )
	{
		withSign = false;
	}
	
	val = (val+"").replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
	if( val[0] == '$' )
	{
		if( ! withSign )
		{
			val = val.substr(1);
		}
	}
	else if( withSign )
	{
		val = "$"+ val;
	}
	return val;
}

/**
 * Formats a number with commas 
 */
jQuery.CommaFormatted = function(amount)
{
	var delimiter = ","; // replace comma if desired
	
	var a = new String(amount).split('.',2)
	var d = a[1];
	var i = parseInt(a[0]);
	if(isNaN(i))
	{
		return '';
	}
	var minus = '';
	if(i < 0)
	{ 
		minus = '-';
	}
	i = Math.abs(i);
	var n = new String(i);
	var a = [];
	while(n.length > 3)
	{
		var nn = n.substr(n.length-3);
		a.unshift(nn);
		n = n.substr(0,n.length-3);
	}
	if(n.length > 0)
	{
		a.unshift(n);
	}
	n = a.join(delimiter);
	if(d.length < 1)
	{ 
		amount = n;
	}
	else
	{
		amount = n + '.' + d;
	}
	amount = minus + amount;
	return amount;
}



/* Accepts a string to see if it a variable exists by that name. If it exsists then THE RESULT IS CACHED.
 * e.g.  if( scSite.isDefined('scSite.google.ga.pageTracker') ) 
 *			scSite.google.ga.pageTracker._link('blah');
 */
jQuery.isDefined = (function()
{
	// Defined Variables
	var df = {};

	return function(v)
	{
		if( df[v] )
		{
			return true;
		}

		var vp = v.split('.');
		var theVar = window;

		for( var i = 0; i < vp.length; i++ )
		{
			if( theVar[vp[i]] != null )
			{
				theVar = theVar[vp[i]];
			}
			else
			{
				return false;
			}
		}

		df[v] = true;
		return true;
	}
})();



////////////////////////////////////////////////////////////////
// Unique Id
////////////////////////////////////////////////////////////////

jQuery.scuid = (function()
{
	var uidCount = 0;
	
	return function(prefix)
	{
		prefix = $.ifNull(prefix, 'uid');
		uidCount++;
		return prefix + uidCount;
	}
})();



/*
 * Strips special characters from a string to form make an id which may be used on an element.
 */
jQuery.textToId = function(sourceTxt)
{
	return sourceTxt.substr(0, 64).replace(/ /g, '_').replace(/\&(amp;)?/, 'and').replace(/[^0-9a-zA-Z_]/g, '').replace(/_+/g, '_').substr(0, 64)
}

/*
 * Gives a unique Id to elements based on their innerHTML
 */
jQuery.fn.makeIds = function()
{
	this.each(function(i, el)
	{
		if( el && typeof el.id === 'string' && el.id.length > 0 )
		{
			// The element already has an Id, nothing to do.
			return;
		}
				
		var newId = jQuery.textToId(this.innerHTML);
		if( document.getElementById(newId) )
		{
			newId += $.scuid('__$');
		}
		el.id = newId;
	});
	return this;
}




////////////////////////////////////////////////////////////////
// Timeout Repeat (For Fixes)
////////////////////////////////////////////////////////////////

/**
 * Useful for checking for glitches and fixing them.
 *
 * @param func function the function to perform which returns true when successfully run, stopping the loop.
 * @param time int the time between calls. Also can be an array with the first element being the time to wait for the first call only.
 * @param iterations int the number of times to call the function.
 */
jQuery.timeoutRepeat = function(func, time, iterations)
{
	var count = 0;
	
	var time2;
	
	if( jQuery.isArray(time) )
	{
		time2 = time[1];
		time = time[0];
	}
	else
	{
		time2 = time;
	}
	
	function theFunction()
	{
		if( func() == true)
		{
			return;
		}
		if( count < iterations )
		{
			count++;
			setTimeout(func, time2);
		}
	}
	
	setTimeout(func, time);
};


/**
 * var x = 10
 * function incrX() { x++; }
 * $.notTooSoon('somethingUnique', 100, incrX);
 * $.notTooSoon('somethingUnique', 100, incrX);
 * $.notTooSoon('somethingUnique', 100, incrX);
 * x == 11 // True after 100ms
 * x != 13
 */
jQuery.notTooSoon = (function()
{
	var timers = {};
	
	return function(id, duration, func)
	{
		clearTimeout(timers[id]);
		
		timers[id] = setTimeout(function()
		{
			delete timers[id];
			func();
		}, duration);
	}
})();


/**
 * var x = 10
 * var spacedFunc = $.notTooOften(100, function(){ x++; });
 * spacedFunc();
 * spacedFunc();
 * spacedFunc();
 * x == 11 // True after 100ms
 * x != 13
 */
jQuery.notTooOften = function(duration, func, context)
{
	var timer;
	
	if( ! context )
	{
		context = window;
	}
	
	return function()
	{
		clearTimeout(timer);
		timer = setTimeout(function(){ func.apply(context, arguments) }, duration);
	}
};


/**
 * var x = 0;
 * var canOnlyBeRunOnce = $.runOnce(function(){ x++ });
 * canOnlyBeRunOnce();
 * canOnlyBeRunOnce();
 * canOnlyBeRunOnce();
 * x == 1;
 */
jQuery.runOnce = function(func)
{
	return (function()
	{
		var hasntRun = true;
		return function()
		{
			if( hasntRun )
			{
				hasntRun = false;
				func();
			}
		}
	})()
};



/**
 * Loads an image and sends the dimensions to the callback function
 * 
 * @param src string
 * @param callback function
 */
jQuery.imageDimensions = (function($)
{
	
	var unusedLoaders = [];
	
	function getLoader()
	{
		if( unusedLoaders.length )
		{
			return unusedLoaders.pop();
		}
		return $('<img/>');
	}
	
	return function(src, callback)
	{
		getLoader().unbind().load(function(e)
		{
			var $t = $(this);
			unusedLoaders.push($t);
			callback(this.width, this.height);
			$t.attr('src', '')
		}).attr('src', src)
	}
	
})(jQuery)



////////////////////////////////////////////////////////////////////
// sc-Compnent
////////////////////////////////////////////////////////////////////

var scSite =
{
	debugMode: false,

	//
	// Component Handler
	////////////////////////////////

	// A component is just an object with an init() function and an isInit variable to ensure it only gets initialized once.
	componentInitialized: false,
	components: {},

	// Initializes each component. If a component is added after this function is called, then its init function will be called instantly.
	init: function()
	{
		// Initialize Components
		for( k in scSite.components )
		{
			var comp = scSite.components[k];
			if ( comp && comp.init && ! comp.isInit )
			{
				comp.init();
				comp.isInit = true;
			}
		}
		scSite.componentInitialized = true;
	},

	/**
	 * Here is an example of how to add a component, reasons follow:
	scSite.example = scSite.addComponent('example', function(comp)
	{
		$.extend(comp, {
			init: function()
			{
				$('something').click(comp.doWork);
			},
			
			workCount: 0,
			doWork: function()
			{
				comp.workCount++;
				$(this).data('workCount', comp.workCount);
			}
		});
	});
	
	 * We do it this way so that we can guarantee we have a reference to the actual component {comp}.
	 * Pretend we have 2 objects like so:
	 *		var obj1 = {name: "Object 1", getName: function(){alert(this.name)}};
	 *		var obj2 = {name: "Object 2", getName: obj1.getName};
	 * 
	 * Calling obj2.getName() will alert "Object 2", not "Object 1", even though Object 1 is where the function originated.
	 * Then lets say we do this:
	 *		$('.aButton').click(obj2.getName)
	 *		
	 * Although you might expect getName to retun "Object 2" or even "Object 1", it will actually return the "name" attribute on the button.
	 * Bottomline is that you MUST be careful when using "this". I setup addComponent this way to avoid that issue.
	 * 
	 * Here is an alternative way to do it:
	scSite.example2 = scSite.addComponent('example2',
	{
		init: function()
		{
			// WRONG: although it is normally ok to use "this" in the init, just don't.
				$('something').click(this.doWork);
			// RIGHT
				$('something').click(scSite.example2.doWork);
		}
		
		workCount: 0,
		doWork: function()
		{
			// RIGHT
				scSite.example2.workCount++;
				$(this).data('workCount', scSite.example2.workCount);
		}
	});
	 * 
	 */
	addComponent: function( name, component )
	{
		if ( ! scSite.components[name] )
		{
			if (typeof component == 'function')
			{
				var tComponent = {isInit: false};
				component(tComponent);
				component = tComponent;
			}
			else
			{
				component.isInit = false;
			}
			
			scSite.components[name] = component;
			if( scSite.componentInitialized && component.init && ! component.isInit )
			{
				component.init();
				component.isInit = true;
			}
		}
		return scSite.components[name];
	}
};

//
// onload
////////////////////////////////
jQuery(function()
{
	scSite.init();
});




////////////////////////////////////////////////////////////////
// Debug
////////////////////////////////////////////////////////////////

scSite.dbugBox = scSite.addComponent('dbugBox', function(comp)
{
	$.extend(comp,
	{
		init: function()
		{
			if( scSite.debugMode === true )
			{
				comp.box = $('<div id="dbugBox" />').css({position: 'absolute', overflow:'scroll', left: 2, top: 200, height: 500, width: 250, background: '#EEE', fontSize:'.8em'}).hide();
				$('body').append(comp.box);
			}
		},
		
		box: null,
		
		addMsg: function(msg)
		{
			if( scSite.debugMode === true )
			{
				comp.box.show();
				comp.box.append($('<div />').html(msg)).scrollTop( comp.box.scrollTop() + 1000 );
			}
		}
	});
});


// If console.log is not defined, make our own:
if( ! console )
{
	var console = {};
}
if( ! console.log )
{
	console.log = scSite.dbugBox.addMsg
}




////////////////////////////////////////////////////////////////
// Other
////////////////////////////////////////////////////////////////


//
// PNGFix
////////////////////////////////
//
// since PNGFix only gets included for < IE7, we need to set it to noop for other browsers not to break when PNGfix gets called
//

function PNGfix()
{}


