var Core = new function($) {
	var z = this;
	//client info gets passed with every request
	z.date = new Date();
	z.clientInfo = {
		_init: "on",//i show that the page is loaded
		tz:z.date.getTimezoneOffset()/60
	};
	z.defaultRoute = '_';//set default default route
	z.path = "";//i need to be setup
	z.controller = "";//i need to be setup
	z.ajaxPath = z.path + z.controller;
	$.ajaxSetup( {
		type: "GET",
		cache: false,
		dataType: "script"
	});

	z.setup = function(path,controller,defaultRoute){
		z.path = path;
		z.controller = controller;
		z.ajaxPath = z.path + z.controller;
		if(defaultRoute.length > 0){
			z.defaultRoute = defaultRoute;
		}
		$.ajaxSetup({
			url:z.ajaxPath
		});
		$(document).ready(function(){
			var resetEvent = new function(){
				z.checkHash();
			}
			$.ajaxHistory.initialize();
		});
	};



	//the non history ajax request
	z.doRequest = function() {
		var params = jQuery.extend({},{action:"update"}, arguments[0] || {});
		$.ajax({
			data:$.param(params)+"&"+$.param(z.clientInfo)
		});
	};
	
	z.showLoading = function() {
		if ($("#page_loading").length<1)
		{
			z.makeLoading();
		}
		//alert('loading');
		$("#page_loading").css({display:"block",position:'absolute',top:0,left:0,background:'white'});
	};

	z.makeLoading = function(){
		$('body').append('<div id="page_loading"><img src="img/loader.gif" alt="" /> Loading ...</div>');
	};

	// Hide the page loading display
	z.hideLoading = function() {
		if ($("#page_loading"))
		{
			$("#page_loading").css({display:"none"});
		}
	};

	//HISTORY
	z.ajaxHistory = function(params) {
		Core.showLoading();
		params = $.extend({},{},params);
		$.ajaxHistory.trigger(true,params);
		scroll(0,0);
	};

	z.sendForm = function(id){
		if(id && $("#"+id)){
			var req = $("#"+id).formSerialize();
			$.ajax({
				data:req+"&"+$.param(z.clientInfo)
			});
		}
	};

	z.formUpload = function(id){
		var opts = {url:z.ajaxPath,data:z.clientInfo,dataType:'script',type:'POST'};
		if(id && $("#"+id)){
			$("#"+id).ajaxSubmit(opts);
			//$("#"+id).submit();
		}
	};

	z.retrieveHistory = function(hash) {
		z.showLoading();
		$.ajax({
			data:$.param(z.clientInfo)+"&"+hash
		});
	};

	z.checkHash = function(){
		if(location.hash.length > 1){
			//if the has is default then no need to run another request
			hash = location.hash;
			if(hash.substring(0,1) == '#'){
				hash = hash.substring(1);
			}
			if(hash != z.defaultRoute){
				//pass bookmarked/typed hash through to ajax and update the page
				z.showLoading();
				//hack tpo make it so update in the url string get converted to bin for page refresh
				hash = hash.replace('update=','bin=');
				$.ajax({
					data:$.param(z.clientInfo)+"&"+hash
				});
			}//else i bookmarked the default route and there is no reason to update the page
		}
	};

	//REGEX VALIDATION FUNCTIONS

	z.emailValid = function(value){
		// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
		return false || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
	};

	z.isDigits = function (str){
		return str.match(/^\d+$/);
	};
	
	z.isWhitespace = function(str){
		return str.match(/^\s+$/);
	};

	z.isUrl = function(str){
		return str.match(/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/);
	};

	//OTHER STUFF =P

	z.newWindow = function (url,properties) {
		var load = window.open(url,'','scrollbars=no,menubar=no,height=600,width=800,resizable=yes,toolbar=no,location=no,status=no');
	};

	z.input_default_vals = new Array();
	z.input_default_style = new Array();
	z.input_last_focus = '';
	z.input_last_blur = '';
	z.input_x_digits = function (id,num_digits,complete_focus,complete_verify_function,non_digit_error_function,max_digit_exceded_function,blur_function,focus_function){
		var y = this;
		removeBorder = function (id){
			setTimeout(function (){
				this.x = $('#'+id);
				//alert('remove timeout hit:'+id+':style:'+style);
				if(z.input_default_style[id] != undefined){
					this.x.attr('style',z.input_default_style[id]);
				}else{
					this.x.removeAttr('style');
				}
			},800);
		}

		if(!num_digits){
			num_digits = 2;
		}
		if(el = $('#'+id)){
			z.input_default_vals[id] = el.val();
			z.input_default_style[id] = el.attr('style');
			el.focus(function (){
				z.input_last_focus = $(this).attr('id');
				z.input_last_blur = '';
				if($(this).val() == z.input_default_vals[$(this).attr('id')]){
					$(this).val("");
				}
				if(focus_function){
					if(typeof(focus_function) == 'function'){
						new focus_function(this);
					}
				}
			});
			el.blur(function (){
				z.input_last_blur = $(this).attr('id');
				z.input_last_focus = '';
				if($(this).val() == ""){
					$(this).val(z.input_default_vals[$(this).attr('id')]);
				}
				if(blur_function){
					if(typeof(blur_function) == 'function'){
						new blur_function(this);
					}
				}
			});
			el.keypress(function(e){
				if(e.which != 8 && e.which > 0){//allow back spaces to go free
					c = String.fromCharCode(e.which);
					this.error = false;
					this.non_digit_error = false;
					this.return_chr = true;
					if(!z.isDigits(c)){
						this.error = true;
						this.non_digit_error = true;
					}

					if($(this).val().length == num_digits){//CASE max digits recieved, refocus, press key
						if(complete_focus){
							if(el2 = $('#'+complete_focus)){
								el2.focus();
								this.error = true;
							}else{
								this.error = true;
							}
						}else{
							this.error = true;
						}
						if(max_digit_exceded_function){
							if(typeof(max_digit_exceded_function) == 'function'){
								new max_digit_exceded_function(this,num_digits);
							}
						}
						if(complete_verify_function){
							if(typeof(complete_verify_function) == 'function'){
								new complete_verify_function(this);
							}
						}
					}else if($(this).val().length+1 == num_digits && !this.error){
						if(complete_focus){
							if(el2 = $('#'+complete_focus)){
								el2.focus();
							}
						}
						if(complete_verify_function){
							if(typeof(complete_verify_function) == 'function'){
								new complete_verify_function(this);
							}
						}
					}
					if(this.error){
						$(this).css('border','1px solid red');
						new removeBorder($(this).attr('id'));
						if(this.non_digit_error && non_digit_error_function){
							if(typeof(non_digit_error_function) == 'function'){
								new non_digit_error_function(this);
							}
						}
						return false;
					}
					if(!this.return_chr){
						return false;
					}
				}
				return true;
			});
		}
	};

	z.showThenHide = function(elementId, timeout){
		if(timeout == null){
			timeout = 1000;
		}
		if(elementId && $("#" + elementId)){
			$("#" + elementId).show("fast");
			setTimeout(function(){
				$("#" + elementId).hide("fast");
			}, timeout);
		}
	};
	
	z.disableElement = function(elementId) {
		if ($("#" + elementId))
		{
			$("#" + elementId).attr("disabled", "true");
		}
	};
	
	z.enableElement = function(elementId) {
		if ($("#" + elementId))
		{
			$("#" + elementId).removeAttr("disabled");
		}
	};

	z.checkDate = function (year,month,day){
		source_date = new Date(year, month - 1 ,day);
		this.error = '';
		if(year != source_date.getFullYear())
		{
			this.error = 'year(' + year + " != " + source_date.getFullYear() + ")";
		}
		
		if((month - 1) != source_date.getMonth())
		{
			this.error = this.error+';month';
		}
		
		if(day != source_date.getDate())
		{
			this.error = this.error+';day';
		}

		return this.error;
	};

	z.getAge = function(year,month,day){
		source_date = new Date();
		year_diff = source_date.getFullYear()-year;
		month_diff = source_date.getMonth()-month;
		day_diff = source_date.getDate()-day;
		if(year_diff>0){
			if(month_diff < 0){
				year_diff = year_diff-1;
			}else if(month_diff == 0 && day_diff < 0){
				year_diff = year_diff-1;
			}
			return year_diff;
		}
		return false;
	};
	//based on function on:
	//http://www.universalwebservices.net/web-programming-resources/javascript/change-input-element-type-using-javascript
	//all event handlers are coppied
	z.changeInputType = function (oldObject, oType,blur_or_focus) {
		var newObject = document.createElement('input');
		newObject.type = oType;
		if(oldObject.size) newObject.size = oldObject.size;
		if(oldObject.value) newObject.value = oldObject.value;
		if(oldObject.name) newObject.name = oldObject.name;
		if(oldObject.id) newObject.id = oldObject.id;
		if(oldObject.className) newObject.className = oldObject.className;
		//added style support
		if(oldObject.style){
			$(newObject).attr('style',$(oldObject).attr('style'));
		}
		
		var events_old = $.data($(oldObject)[0],'events');
		oldObject.parentNode.replaceChild(newObject,oldObject);
		//if you need to fire events before the event handlers are engaged on the new element
		//when the element is swapped it looses browser focus
		if(blur_or_focus){
			if(blur_or_focus == 'focus'){
				$(newObject).focus();
			}else{
				$(newObject).blur();
			}
		}
		//this is the way jquery copies events in jquery core
		for (var type in events_old)
			for (var handler in events_old[type])
				$.event.add(newObject, type, events_old[type][handler], events_old[type][handler].data);
		return newObject;
	};

	//this function can sleep in a loop without chewing appart the cpu
	//quite clever =) it looks like it could have namespace issues but it works for my needs
	z._timeout_calls = 0;
	z.sleep_loop = function(checker,callback,error_callback){
		setTimeout(function(){
			breaker = checker(Core._timeout_calls);
			if(breaker && breaker != 'break'){
				callback();
				Core._timeout_calls = 0;
				return;
			}else if(breaker == 'break'){
				if(typeof(error_callback) == 'function'){
					error_callback();
				}
				Core._timeout_calls = 0;
				return;
			}else{
				Core._timeout_calls += 1;
				Core.sleep_loop(checker,callback,error_callback);
			}
		},1);
	};
}(jQuery);
/*
this javascript has been tested and proven to work successfully in these browsers
Firefox3,IE6,Konqueror,Opera9.5,google chrome (win),safari(win)
*/

//this can use used for soft search results. anything where you require a box to appear under an element
/*
target		= id of the element this box should appear under (no #)
box_width	= how large the box should be
html		= the html content of the box
align_corner	= this function can perform special adjustments based on width of margin
			"left" will place the top left corner of the box under the bottom left corner of the element
			"right" will place the top right corner of the box under the bottom right corner of the element
			* (!empty) will attempt to place the top right corner of the box under the bottom right corner of the 
					element without any attempted adjustment for margin
			'' empty   will choose 'left' unless doing so would cause the box to protrude out of the window's
					viewabble area. If that is the case, 'right' will be auto selected
tweek		= if auto adjustments have failed you may "tweek" the position by passing an object
			the object may have 4 properties with int values top,bottom,left,right the result will be determined in px
			and applied to the appropraite values to "tweek" the position of the box

example: Core.magicBox('element_id',200,'html here','',{top:1});
*/

//the Core magicBox: i can make a box anywhere
var Core_magic_select_box_global_holder = false;
Core.magicBox = function (target,box_width,html,align_corner,tweek,create_inside){
	Core.magicBox.setInstance({
		target:target,
		box_width:box_width,
		html:html,
		align_corner:align_corner,
		tweek:tweek,
		opened:false,
		create_inside:create_inside,
		create:function(){
				if(this.get().length < 1){
					if(typeof(this.create_inside) == 'object'){
						$(this.create_inside).append("<div id='"+this.target+"_magic_box' style='display:none;position:absolute;z-index:100;'></div>");
					}else{
						$("body").append("<div id='"+this.target+"_magic_box' style='display:none;position:absolute;z-index:100;'></div>");
					}
				}
				if(!this.box_width){
					this.box_width = 200;
				}
				this.get().css({width:box_width+'px',background:' transparent none repeat scroll 0% 0%'});
				//this is here so false can be passed to not replace the html
				if(typeof(html) == 'string'){
					this.get().html(html);
				}
				var pos = $('#'+this.target).offset();
				var height = $('#'+this.target).outerHeight();
				var width = $('#'+this.target).outerWidth();
				
				var top = pos.top+height;
				var left = pos.left;
		
				if(!align_corner){
					align_corner = 'left';
					//auto check to make sure im not sticking out of the window
					if(left+box_width > $(window).width()){
						align_corner = 'right'
					}
				}
		
				if(typeof(tweek) == 'object'){
					if(tweek.left){
						left += tweek.left;
					}
		
					if(tweek.top){
						top += tweek.top;
					}
		
					if(tweek.right){
						left -= tweek.right;
					}
		
					if(tweek.bottom){
						top -= tweek.bottom;
					}
				}

				if (align_corner == 'right'){
					var box_offset = (box_width>width ? box_width-width:width-box_width );
					this.get().css({top:top+'px',left:(left-box_offset)+'px'});
				}else{
					this.get().css({top:top+'px',left:left+'px'});
				}
			},
		//html manipulation functions
		get:function(){
			return $('#'+this.target+'_magic_box');
			},
		html:function(html){
				this.get().html(html);
			},
		//resize and positioning functions

		//action functions
		open:function(){
				this.opened = true;
				this.get().css({display:'block'});
			},
		close:function(){
				this.opened = false;
				this.get().css({display:'none'});
			},
		remove:function(){
				this.get().remove();
			}
		});

		return Core.magicBox.instances[target];
};

//required for accessing instances inside event handler functions
Core.magicBox.instances = {};
//never write a function in the primary magic box object that requires a call to these instance functions
Core.magicBox.getInstance = function (target){
	if(Core.magicBox.instances[target]){
		return Core.magicBox.instances[target];
	}
	return false;
};

Core.magicBox.setInstance = function (object){
	Core.magicBox.instances[object.target] = object;
};

//magicSelect, magicBox extension
//this uses document level mouse move to handle
Core.magicSelect = function (target,box_width,html,align_corner,tweek){
	Core.magicBox.setInstance({
		target:target,
		mb:Core.magicBox(target,box_width,html,align_corner,tweek),
		mouseover_open:true,
		mouse_timeout:false,
		mouse_location:false,
		mouse_event:false,
		on_open_func:false,
		on_close_func:false,
		debug:false,
		//dimensions of the mbox
		x:0,
		y:0,
		x2:0,
		y2:0,
		//dimensions of the target
		tx:0,
		ty:0,
		tx2:0,
		ty2:0,
		open:function(){
				this.mb.create();
				this.bindings();
				if(typeof(this.on_open_func) == 'function'){
					this.on_open_func();
				}
				if(this.mouseover_open){
					$('#'+this.mb.target).unbind('mouseover');
				}
				this.mb.open();
				//mbox
				this.x  = this.mb.get().position().left;
				this.y  = this.mb.get().position().top;
				this.x2 = this.x+this.mb.get().width();
				this.y2 = this.y+this.mb.get().height();
				//target
				this.tx = $('#'+this.mb.target).offset().left;
				this.ty  = $('#'+this.mb.target).offset().top;
				this.tx2 = this.tx+$('#'+this.mb.target).width();
				this.ty2 = this.ty+$('#'+this.mb.target).height();
			},
		close:function(){
				$().unbind('mousemove');
				if(this.mouseover_open){
					$('#'+this.mb.target).bind('mouseover',function(){
						Core.magicBox.getInstance($(this).attr('id')).open();
					});
				} else {
					$('#'+this.mb.target).bind('click',function (){
						Core.magicBox.getInstance($(this).attr('id')).open();
					});
				}
				this.mb.close();
				if(typeof(this.on_close_func) == 'function'){
					this.on_close_func();
				}
			},
		bindings: function(){
				if(this.mouseover_open){
					$('#'+this.mb.target).unbind('mouseenter');
				} else {
					$('#'+this.mb.target).unbind('click');
				}
				if(typeof(Core_magic_select_box_global_holder) == 'object'){
					if(Core_magic_select_box_global_holder != this){
						Core_magic_select_box_global_holder.close();
					}
				}
				Core_magic_select_box_global_holder = this;
				$().mousemove(function(e){
					Core_magic_select_box_global_holder.checkMouse(e);
				});
			},
		checkMouse:function(event){
			mx = event.pageX;
			my = event.pageY;
			if(this.debug){
				if($("#"+this.mb.target+'_debug_target').length == 0){
					$($('body')[0]).append('<div id="'+this.mb.target+'_debug_target'+'" style="position:absolute;background:green;top:'+this.y+'px;left:'+this.x+'px;width:'+(this.x2-this.x)+'px;height:'+(this.y2-this.y)+'px;"></div>');
					$($('body')[0]).append('<div id="'+this.mb.target+'_debug'+'" style="position:absolute;background:yellow;top:'+this.ty+'px;left:'+this.tx+'px;width:'+(this.tx2-this.tx)+'px;height:'+(this.ty2-this.ty)+'px;"></div>');
				}
			}
			//mouse inside target element?
			if(mx >= this.tx && my >= this.ty){
				if(mx <= this.tx2 && my <= this.ty2){
					//console.log();
					return true;
				}
			}
			//mouse inside mb element?
			if(mx >= this.x && my >= this.y){
				if(mx <= this.x2 && my <= this.y2){
					//console.log('inside mb');
					return true;
				}
			}
			this.close();
			return false;
		}
	});
	return Core.magicBox.instances[target];
};
//////////////
//not used in this lib
//these are here as utility and can be useful for manual event handling
Core.extractEvents = function(selector){
	if($(selector).length > 0){
		return $.data($(selector)[0],'events');
	}
	return new Array;
};

Core.addEvents = function(jq_object,copied_events){
	for (var type in copied_events)
		for (var handler in copied_events[type])
			$.event.add(jq_object, type, copied_events[type][handler], copied_events[type][handler].data);
};
//###################################################################################