﻿var SCRIPTS = {
	GoogleMaps: "maps,2",
	GoogleEarth: "earth,1",
	JUI: "/js/jquery-ui.js"
}
// optimized shortcut for selecting a single element by id
var $$ = function(id)
{
	return jQuery("#" + id + ":first");
}

/*
	Wrapper function to jquery's $() function that will either return the first element in the collection or null if the collection is empty.
*/
function $F(expr)
{
	var result = jQuery(expr);
	if (result.length > 0)
		return result.eq(0);
	
	return null;
}

/*
	Wrapper function to jquery's $() function that will either return the last element in the collection or null if the collection is empty.
*/
function $L(expr)
{
	var result = jQuery(expr);
	if (result.length > 0)
		return result.eq(result.length-1);
	
	return null;
}

/*
	Wrapper function to jquery's $() function that will either return the n element in the collection or null if the collection is empty.
*/
function $N(expr, n)
{
	var result = jQuery(expr);
	if (result.length > 0)
		return result.eq(n);
	
	return null;
}

/* Custom Javascript/Moo Tools extensions */

function $clone(obj)
{
    return $extend({}, obj);
}

function $NaN(obj)
{
	if (obj == 0) 
		return false;
		
	if (!obj)
		return true;
		
	return obj + '' == "NaN";
}

window.location.getQueryHash = function()
{
	if (!window.location.search && window.location.search.length <= 1)
		return null;
		
	var parts = window.location.search.substring(1).split("&");
	var h = $H();
	parts.each(function(part){
	    var qs = part.split("=");
	    h.set(qs[0], unescape(qs[1]));
	});
	
	return h;
}

String.combine = function(arr, str)
{
    var s = "";
    arr.each(function(o){
        // if o is empty (not an actual boolean) then return
        if ((!o && o != false) || o == "")
            return;
            
        if (s != "")
            s += str;
            
        s += o.toString();
    });
    
    return s;
}

String.implement({
    format: function(/*args*/)
    {
        var s = this;
        $A(arguments).each(function(arg, i){
            s = s.replace("{" + i + "}", arg ? arg.toString() : "");
        });
        
        return s;
    }
});

Array.implement({
    collect: function(fn, bind)
    {
        var result = [];
        var self = this;
        this.each(function(){
            var v = fn.apply(bind || self, arguments);
            if (!v) return;
            if (Object.isArray(v))
                result.combine(v);
            else
                result.include(v);
        });
        
        return result;
    }
});

Hash.implement({
	// adds the value to the hash, if an existing value exists it will be turned into an array so that multiple values can be stored
	// returns: the hash object
	add: function(key, value)
	{
		var v = this.get(key);
		if (!v)
			v = value;
		else
		{
			if (!Object.isArray(v))
				v = [v];	
			v.push(value);
		}
		return this.set(key, v);
	}
	// a variation of toQueryString that does not use indexes when multiple values exist for one field
	,toQueryParams: function(base){
		var queryString = [];
		Hash.each(this, function(value, key){
			if (base) key = base + '[' + key + ']';
			var result;
			switch ($type(value)){
				case 'object': result = Hash.toQueryString(value, key); break;
				case 'array':
					result = "";
					value.each(function(val, i){
						if (result != "")
							result += "&";
						result += key + '=' + encodeURIComponent(val);
					});
				break;
				default: result = key + '=' + encodeURIComponent(value);
			}
			if (value != undefined) queryString.push(result);
		});

		return queryString.join('&');
	}

})

Function.implement({
	/*
	    Binds a method to an object while passing in the original "this" as the first argument. 
	    params:
	        bind: object to bind as "this"
	        preventDefault: optional - true if being bound to an event and preventDefault() should be called
	*/
	bindWith: function(bind, preventDefault)
	{
		var self = this;
		return function()
		{
		    if (preventDefault && arguments[0] && arguments[0].preventDefault)
		        arguments[0].preventDefault();
		        
			// add this as the first argument into the arguments passed in
			var args = [this].extend(arguments);
			
			// call the method with the original bind argument as this
			self.apply(bind, args);
		}
	}
});


/* Custom JQuery extensions */

//* static extensions
jQuery.extend({
	// returns true if the object is a jquery object instance
	isJQuery: function(obj)
	{
		return obj && typeof obj == "object" && 
			obj.jquery == $.prototype.jquery && 'length' in obj;
	}
	// loads a script through dom script tag instead of ajax (so that cache can be utilized)
	,loadScript: function(url, cb){
	
		// if safari then use AJAX method since it doesnt handle load events well
		if ($.browser == "safari")
			return $.getScript(url, cb);
		
		// TODO: determine if script is already on the page (code below) and if so is it ready or loading?	
		//var $script = $("script[src='" + url + "']");
		
		var script = document.createElement("script");
		
		script.src = url;
		script.type = "text/javascript";
		
		if (cb)
		{
			if ($.browser.msie)
			{
				script.onreadystatechange = function(){
					if (this.readyState == 'loaded' || this.readyState == 'complete')
						cb();			
				}
			}
			else
			{	
				script.onload = cb;
				script.onerror = cb;
			}
		}
			
		document.body.appendChild(script);
	}
});

//* instance extensions
jQuery.fn.extend({
	
	preventDefault: function(eventNames)
	{
	    this.bind(eventNames || "click", function(e){
	        e.preventDefault();
	    });
	}
	,stopPropagation: function(eventNames)
	{
	    this.bind(eventNames || "click", function(e){
	        e.stopPropogation();
	    });
	}


	// compares the elements within the jquery object to another jquery object and returns true if they should be considered the same
	//TODO: remove?
	,equals: function(jqObj)
	{
		if (jqObj && this.length == jqObj.length)
		{
			for(var i=0;i<this.length;i++)
				if (this.get(i) != jqObj.get(i))
					return false;
					
			return true;
		}
		return false;
	}
	// sets a property on each item in the collection
	//TODO: remove?
	,prop: function(name, value)
	{
	    if ($defined(value))
		    this.each(function(){
			    this[name] = value;
		    });
	    else if (this.length > 0)
	        return this.get(0)[name];
	        
	}
	
	// sets the attribute with the value given and returns the original value
	,replaceAttr: function(name, value)
	{
		var r = this.attr(name);
		this.attr(name, value);
		return r;
	}
	
	// invokes a method on each dom element
	//TODO: remove?
	,invoke: function(name)
	{
		var args = $A(arguments).slice(1);
		this.each(function(){
			if ((fn = this[name])) fn.apply(this, args);
		});
		return this;
	}
	
	// extends each dom element within the collection with the supplied object/class
	//TODO: remove?
	,expando: function(obj)
	{
		this.each(function(){
			$extend(this, obj);
		});	
		return this;
	}
	//* CUSTOM EFFECTS
	,supaFlyIn: function(force, cb)
	{
		if (this.is(":hidden") || force)
		{
			//this.children(">").stop().hide().fadeIn(1000);
			this.animate({
			    height:"show", 
			    opacity: "show"
			 },{
			    queue: true,
			    complete: cb,
			    easing: "swing"
			});
		}
		    
		return this;
	}
	,supaFlyOut: function(force, cb)
	{
		if (this.is(":visible") || force)
		{
			this.animate({
			    height:"hide", 
			    opacity:"hide"
			 },{
			    queue: true, 
			    duration:400,
			    complete: cb,
			    easing: "swing"
			});
		}
		    
		return this;
	}
	//TODO: remove?
	,supFlyOutIn: function()
	{
        this.supaFlyOut(function(){
            $(this).supaFlyIn();       
        });
	    
	    return this;
	}
	,supaFlyToggle: function(opacity)
	{
		this.is(":hidden") ? this.supaFlyIn(opacity) : this.supaFlyOut();	
		return this;
	}
	,collectValues: function(o)
	{
		h = $H(o || {});
		var fields = this.find("input:enabled, select:enabled, textarea:enabled");
		fields.each(function(){
			var k = this.name || this.id;
			if (k)
				h.add(k, $(this).val());
		});
		
		this.formValues = h;
		return this;
	}	
	// returns the first instance of the behavior that can be found within the collection
	//	if options are specified (use just {} if default options should be used) then all items in the collection will have the behavior set on them (if they dont already)
	,behavior: function(name, options)
	{
		if (this.length > 0)
		{
			if (options)
			{
				var behaviorClass = Behaviors[name];
				this.each(function(){
					if (!this.behaviors || !this.behaviors[name])
						Behaviors.instances.push(new behaviorClass(this, $clone(options)));
				});
				
				return this.get(0).behaviors[name];
			}
			
			for(var i=0;i<this.length;i++)
			{
				var el = this.get(i);
				if (el.behaviors && el.behaviors[name])
					return el.behaviors[name];
			}
		}
	    return null;
	}
	/*,attachBehavior: function(name, options)
	{
		var behaviorClass = Behaviors[name];
		return this.each(function(){
			var opts = $clone(options);
			if (!$(this).behavior(name))
				Behaviors.instances.push(new behaviorClass(this, opts));
		});
	}*/
	// make each element act like an anchor tag by having its text underlined when hovered over (useful for IE where :hover doesnt work on every element)
	,linkHover: function()
	{
	    return this.hover(
            function(){ $(this).css("text-decoration", "underline")},
            function(){ $(this).css("text-decoration", "")}
        );
	}
	,hoverClass: function(cssClass)
	{
		return this.hover(
            function(){ $(this).addClass(cssClass)},
            function(){ $(this).removeClass(cssClass)}
        );

	}
});

/* custom function to fix IE's cleartype issue with fadeIn() */
(function($) {
	$.fn.customFadeIn = function(speed, callback) {
		$(this).fadeIn(speed, function() {
			if(jQuery.browser.msie)
				$(this).get(0).style.removeAttribute('filter');
			if(callback != undefined)
				callback();
		});
	};
	$.fn.customFadeOut = function(speed, callback) {
		$(this).fadeOut(speed, function() {
			if(jQuery.browser.msie)
				$(this).get(0).style.removeAttribute('filter');
			if(callback != undefined)
				callback();
		});
	};
})(jQuery);


/* PrototypeJS Extensions */

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Object.extend(Object, {

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  isElement: function(object) {
    return object && object.nodeType == 1;
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

// AJAX Extensions:


//closure:
(function(){

autoMappings = {};

window.Behaviors = {
	instances: []
	,autoMappings: {}
	/*
	    Registers an class as a behavior
	      params:
	        name: name of the behavior
	        expr: expression used to automatically bind elements to the behavior (or null/false for no auto binding)
	        behavior: either an explicitly defined behavior class or class definition/options that will be used as a behavior class
	*/
	,register: function(name, expr, behavior)
	{ 
	    if (this[name])
	        throw new Error("Class \"" + name + "\" has already been registered.");
	      
	    // if a class was passed in then set the reference
	    if (behavior.$family && behavior.$family.name == "class")
	        this[name] = behavior;
	    
	    // else create a new class using the object as its definition
	    else
	    {
	        if (!behavior.behaviorName)
	            behavior.behaviorName = name;
	            
	        // extend from the base behavior class if no extensions has already been declared
	        if (!behavior.Extends)
	            behavior.Extends = Behaviors.Base;
	                	        
		    this[name] = new Class(behavior);
		}
		
		// if an expression was passed in then set it up to auto-attach behaviors when the page is loaded
		if (expr)
		{
		    this[name].autoInstances = [];
		    this.autoMappings[name] = expr;
		}
	}
	// select all behaviors that match the given name
	,select: function(sName)
	{
		var result = [];
		this.instances.each(function(o){
			if (o && o.behaviorName == sName)
				result.push(o);
		});
		return result;
	}
	// select the first behavior instance that matches the given name
	,first: function(sName)
	{
		for(var i = 0;i<this.instances.length;i++)
		{
			var o = this.instances[i];
			if (o && o.behaviorName == sName)
				return o;
		}
		return null;
	}
	// applies auto behaviors to 
	,apply: function(container)
	{
		container = $(container || document);
		
		for (var p in this.autoMappings)
	    {
	        var klass = this[p];
	        if (klass)
	            container.find(this.autoMappings[p]).each(function(){
	            	// if the behavior is already applied to the current element then skip it
	            	if (this.behaviors && this.behaviors[klass.behaviorName])
	            		return;
	            		
	                klass.autoInstances.push(new klass(this));
	            });
	    }
	}
	,registerScript: function(url, cb)
	{
		if (!Behaviors.scripts)
			Behaviors.scripts = $H();
			
		url = url.toLowerCase();
		var info = Behaviors.scripts.get(url);
		if (info)
		{
			if (cb)
			{
				if (info.loaded)
					cb();			
				else
					info.callbacks.push(cb);
			}
			return true;
		}
	
		info = {
			loaded: false,
			callbacks:[]
		}
		
		if (cb)
			info.callbacks.push(cb);
			
		Behaviors.scripts.set(url, info );
		
		var _execCallbacks = function(){
			info.loaded = true;
			info.callbacks.each(function(m){
				m();
			});
		}
		
		switch(url)
		{
			// special google maps/earth loading
			case SCRIPTS.GoogleMaps:
			case SCRIPTS.GoogleEarth:
				params = url.split(",");
				google.load(params[0], params[1], {"callback" : _execCallbacks});
				break;
				
			default: 
				$.loadScript(url, _execCallbacks);
				break;
		}		
		return false;
	}
	,registerScripts: function(urls, cb)
	{
		if (!urls || urls.length == 0 || Behaviors.areScriptsLoaded(urls))
			cb();
		else
		{		
			urls.each(function(url){
				Behaviors.registerScript(url, function(){
					if (Behaviors.areScriptsLoaded(urls))
						cb();
				});
			});
		}
	}
	,areScriptsLoaded: function(urls)
	{
		if (!Behaviors.scripts)
			return false;
			
		for(var i = 0;i<urls.length;i++)
		{
			var info = Behaviors.scripts.get(urls[i]);
			if (!info || !info.loaded)
				return false;		
		}
		
		return true;
	}
};

Behaviors.Base = new Class({
	Implements: Events
	
	,initialized: false
	,reqScripts: []
	,initialize: function(elem, options)
	{
	    if (elem)
	    {
	        // if elem is actually an element
	        if (Object.isElement(elem))
	            this.el = elem;
	            
	        // else if elem is string then assume it is an id
	        else if (Object.isString(elem))
	            elem = $(elem);
	            
	        // else if elem is a jquery object then grab the first element inside of it
	        if (jQuery.isJQuery(elem))
	        {
	            this.el = elem.get(0);
	            this.$el = elem;
	        }
	            
	        // else if options is not specified as a 2nd parameter then assume they are the first param (elem)
	        else if (!options)
	            options = elem;
    		
		    if (options)
		    {
		        this.options = options;
		        if (options.initialized)
		        	this.addEvent("initialized", options.initialized);
		        
		        if (!this.el)
			        this.el = options.element || options.id ? $$(options.id).get(0) : options.expr ? $(options.expr).get(0) : null;
		    }
		    
		    if (this.el)
		    {
		    	if (!this.$el)
					this.$el = $(this.el);
				
				if (this.behaviorName)
				{
				    if (!this.el.behaviors)
				        this.el.behaviors = {};
    				    
				    this.el.behaviors[this.behaviorName] = this;
				}
				
				// set isRail to indicate if the behavior is within the rail zone
				this.isRail = this.$el.parents("#page-rail").length == 1;
		    }
		}
		
		Behaviors.registerScripts(this.reqScripts, function(){
			this.isCompatible = this.isCompatible();
			this.isValid = this.isValid();
			
			if (this.isCompatible && this.isValid)
			{
				this.init();
				this.initLayout();
				this.initEvents();
				this.isInitialized = true;
				this.fireEvent("initialized");
			}
		
		}.bind(this));
				
		// add to the instances collection
		Behaviors.instances.push(this);
	}
	
	//* construction methods
	,isCompatible: $lambda(true)
	,isValid: function(){return this.el && true;}
	,init: $empty 				// used to initialize properties, state, etc 
	//,initHtml: $empty			// used to initialize HTML elements ///DEPRECATED: TODO: Remove all references and use initLayout
	,initLayout: $empty			// used to initialize layout (create/modify HTML elements, apply styles, etc)
	,initEvents: $empty		    // used to attach event hooks
	// true if all scripts are ready
	// jquery shortcut for this.$el.find()
	,find: function(expr){ return this.$el ? this.$el.find(expr) : null; }
});

// attach auto-behaviors on page load
$(function(){
    //* attach auto-behaviors
    Behaviors.apply();
	if ( $('.wp-siblingPageNav').length ) {
		$('#page-desc div').css("padding-top","0");
	}

    $(".buttons input[type='submit']").replaceWith('<a href="" id="send" title="Send">Send</a><a href="" id="cancel" title="Cancel">Cancel</a>');
	$("#send").click(function(){ document.forms[0].submit(); return false; });
	$("#cancel").click(function(){
		if (document.referrer.length > 0) location.href = document.referrer;
		else document.forms[0].reset();
		return false;
	});
	//* College Alumni Shading
	$("#college-connections-list li ul li:nth-child(odd)").addClass("striped");
	$("#college-connections-list li ul li ul > li").removeClass("striped");

	//* Char Limits:
	 $("input[type='text'][size]").each(function(){	 	
	 	
		// the size!=20 is a hack for IE.
		if (this.size != 20)
			$(this).attr("maxlength", this.size);
	});
	
	function trimValue($el){
		var val = $el.val();
	    var size = parseInt($el.attr("size"), 10);
	    if (val && val.length >= size)
	        $el.val(val.substring(0,size));
	}
	$("#privacy-policy").click(function(){
		window.open($(this).attr("href"),"privacy");
		return false;
	});
	$("textarea[size]")
		.keypress(function(){
			// use a timeout so that the keypress actually outputs the value. This way we can trim the text no matter what was typed (or pasted)
			trimValue.delay(10, this, $(this));
		}).change(function(){
			trimValue($(this));
		});
		$(".img-module img").each(function (i) {
			var imgWidth = $(this).width() + 20;
			var imgHeight = $(this).height() + 2;
			$(this).parent().parent().parent().parent().parent().parent().parent().parent().css("width", imgWidth);
			$(this).parent().css("height", imgHeight);
		});
});

})();