(function($){
	var num = function(el, props) {
		var r = 0;
		$.each(props.split(/\s+/) || [], function(i,v){
			r += parseInt($(el).css(v)) || 0;
		});
		return r;
	}
	
	var innerHSpace = function(el) { return num(el, "paddingLeft paddingRight"); }
	var innerVSpace = function(el) { return num(el, "paddingTop paddingBottom"); }
	var outerHSpace = function(el) { return innerHSpace(el) + num(el, "marginLeft marginRight borderLeftWidth borderRightWidth"); }
	var outerVSpace = function(el) { return innerHSpace(el) + num(el, "marginTop marginBottom borderTopWidth borderBottomWidth"); }
	
	var getPageSize = function() {
		var xScroll, yScroll;
		
		if (window.innerHeight && window.scrollMaxY) {	
			xScroll = window.innerWidth + window.scrollMaxX;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}

		var windowWidth, windowHeight;

		if (self.innerHeight) {	// all except Explorer
			if(document.documentElement.clientWidth){
				windowWidth = document.documentElement.clientWidth; 
			} else {
				windowWidth = self.innerWidth;
			}
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		}	

		// for small pages with total height less then height of the viewport
		if(yScroll < windowHeight){
			pageHeight = windowHeight;
		} else { 
			pageHeight = yScroll;
		}


		// for small pages with total width less then width of the viewport
		if(xScroll < windowWidth){	
			pageWidth = xScroll;		
		} else {
			pageWidth = windowWidth;
		}
		
		//
		var largestWidth;
		var largestHeight;
		var smallestWidth;
		var smallestHeight;
		//
		if ( pageWidth >= windowWidth )
		{	largestWidth = pageWidth; smallestWidth = windowWidth;	}
		else
		{	largestWidth = windowWidth; smallestWidth = pageWidth;	}
		//
		if ( pageHeight >= windowHeight )
		{	largestHeight = pageHeight; smallestHeight = windowHeight;	}
		else
		{	largestHeight = windowHeight; smallestHeight = pageHeight;	}
		
		// Return
		var arrayPageSize = {'pageWidth':pageWidth,'pageHeight':pageHeight,'windowWidth':windowWidth,'windowHeight':windowHeight,'largestWidth':largestWidth,'largestHeight':largestHeight};
		return arrayPageSize;
	}
	
	$.fn.zoomPan = function(options){
		var isMethodCall = (typeof options == "string"),
			args = Array.prototype.slice.call(arguments, 1);
			
		if (isMethodCall) {
			var instance = $(this[0]).data("zoomPan");
			if (instance && $.isFunction(instance[options])) {
				instance[options].apply(instance, args);
			} else {
				return undefined;
			}
		}
		
		return this.each(function() {
			var instance = $(this).data("zoomPan");
			if (isMethodCall && instance && $.isFunction(instance[options])) {
				instance[options].apply(instance, args);
			} else {
				$(this).data("zoomPan", new $.zoomPan(this, options));
			}
		});
	};
	
	var ZoomPan = $.zoomPan = function(element, options) {
		this.element = $(element);
		this.options = $.extend(true, {},$.zoomPan.defaults, options);
		this.init();
	};
	
	$.extend($.zoomPan.prototype, {
		init: function() {
			var self = this;
			
			this.image = $(".image", this.element);
			if (this.image.length < 1) {
				return;
			}
			this.imageWidth = $("img", this.image).width();
			this.imageHeight = $("img", this.image).height();
			
			this.maxWidth = this.imageWidth * 1;
			this.maxHeight = this.imageHeight * 1;
			
			this.track = $(".toolZoom_track", this.element);
			this.trackHeight = this.track.innerHeight();
			
			this.drag = $(".toolZoom_track .toolZoom_drag", this.element);
			this.dragHeight = this.drag.outerHeight();
			this.dragHeight = 13;
			
			this.dragMaxPos = this.trackHeight - this.dragHeight;
			this.dragPos = Math.round(this.dragMaxPos/2);
			this.ratio = this.dragMaxPos / 100;
			
			this.image.pan();
			
			$(window).wresize(function(){
				self.resize.call(self);
			});
			
			//this.initZoom();
			self.resize.call(self);
			self.centerImage.call(self);
		},
		
		resize: function() {
			
			var self = this;
			var pageSize = getPageSize();
			var w, h, sx, sy;
			
			w = Math.min(pageSize.pageWidth, pageSize.windowWidth);
			h = Math.min(pageSize.pageHeight, pageSize.windowHeight);
			
			if ( w < 800 ) {
				w = 800;
			}
			if ( h < 400) {
				h = 400;
			}
			
			var imageOffset = self.image.offset();
			var offset = self.element.offset();
			
			var x,y;
			x = imageOffset.left - offset.left;
			y = imageOffset.top - offset.top;
			
			var minLeft = w - $("img", this.image).width();
			var minTop = h - $("img", this.image).height();
			
			$(self.element).css({
				width:w+'px',
				height:h+'px'
			});
			
			if ( x < minLeft ) { x = minLeft; }
			if ( x > 0 ) { x = 0; }
			if ( y < minTop ) { y = minTop; }
			if ( y > 0) { y = 0; }
			
			this.image.css({
				left:x+'px',
				top:y+'px'
			});
			
			sx = w / this.imageWidth;
			sy = h / this.imageHeight;
			
			if (sx < sy) {
				this.minWidth = Math.floor(sx * this.imageWidth);
				this.minHeight = Math.floor(sx * this.imageHeight);
			} else {
				this.minWidth = Math.floor(sy * this.imageWidth);
				this.minHeight = Math.floor(sy * this.imageHeight);
			}
			
			this.initZoom();
		},
		
		initZoom: function() {
			var self= this;
			var track = this.track;
			var drag = this.drag;
			var dragHeight = this.dragHeight;
			var trackHeight = this.trackHeight;
			var maxHeight = this.dragMaxPos;
			var ratio = this.ratio;
			
			drag.css({
				top: (maxHeight - this.dragPos)+"px"
			});
			
			drag.bind("mousedown.zoompan", function(e) {
				var dragOffset = drag.offset();
				var trackOffset = track.offset();
				var offsetTop = e.pageY - dragOffset.top;
				$("html")
					.unbind("mousemove.zoompan")
					.unbind("mouseup.zoompan")
					.bind("mousemove.zoompan", function(e) {
						var x,y,scale,w,h;
						
						y = e.pageY - trackOffset.top - offsetTop
						
						if (y < 0) {
							y = 0;
						}
						if (y > maxHeight) {
							y = maxHeight;
						}
						
						self.updateDragPos.call(self, y);
						
						return false;
					})
					.bind("mouseup.zoompan", function(){
						$("html")
							.unbind("mousemove.zoompan")
							.unbind("mouseup.zoompan");
					});
					
				return false;
			});
			
			$(".toolZoom_zoomIn", this.element).click(function(){
				self.zoomIn.call(self);
				this.blur();
				return false;
			});
			
			$(".toolZoom_zoomOut", this.element).click(function(){
				self.zoomOut.call(self);
				this.blur();
				return false;
			});
			
			$(["top", "bottom", "left", "right"]).each(function(i,v) {
				$(".toolPan_" + v, this.element).click(function(){
					self.pan.call(self, v);
					this.blur();
					return false;
				});
			});
			
			track.mousedown(function(e){
				var dragOffset = drag.offset();
				var trackOffset = track.offset();
				var y = e.pageY - trackOffset.top;
				y -= dragHeight/2;
				y = Math.round(y);
				if ( y < 0 ){ y = 0; }
				if ( y > maxHeight ){ y = maxHeight; }
				
				self.updateDragPos.call(self, y, true);
				
				
				return false;
			});
			
			scale = 200 - ((self.dragMaxPos - self.dragPos) / self.ratio);
			self.scaleImage.call(self, scale);
		},
		
		updateDragPos: function(y, anim) {
			var self = this;
			var scale;
			this.drag.stop();
			if (anim) {
				this.drag.animate({top:y+'px'}, {
					duration: 400,
					step: function() {
						var t = parseInt($(this).css("top"));
						self.dragPos = self.dragMaxPos - y;
						scale = 200 - (t / self.ratio);
						self.scaleImage.call(self, scale);
						
					}
				});
			} else {
				this.dragPos = this.dragMaxPos - y;
				this.drag.css({
					top:y+'px'
				});
				scale = 200 - (y / self.ratio);
				self.scaleImage.call(self, scale);
			}
		},
		
		zoomIn: function() {
			var self= this;
			var track = this.track;
			var drag = this.drag;
			var maxHeight = this.dragMaxPos;
			
			var dragOffset = drag.offset();
			var trackOffset = track.offset();
			
			var y = dragOffset.top - trackOffset.top;
			
			y -= Math.round(maxHeight / 8);
			if ( y < 0 ){ y = 0; }
			self.updateDragPos.call(self, y, true);
		},
		
		zoomOut: function() {
			var self= this;
			var track = this.track;
			var drag = this.drag;
			var maxHeight = this.dragMaxPos;
			
			var dragOffset = drag.offset();
			var trackOffset = track.offset();
			
			var y = dragOffset.top - trackOffset.top;
			
			y += Math.round(maxHeight / 8);
			if ( y > maxHeight ){ y = maxHeight; }
			self.updateDragPos.call(self, y, true);
		},
		
		pan: function(dir) {
			var instance = this.image.data("pan");
			if (instance) {
				instance.pan.call(instance, dir);
			}
		},
		
		scaleImage: function(scale) {
			var img, l, t, x, y, w, h;
			
			scale = scale / 100;
			
			img = $("img" ,this.image);
			/*
			w = scale * this.imageWidth;
			h = scale * this.imageHeight;
			*/
			w = this.minWidth + ((this.maxWidth - this.minWidth) * (scale - 1));
			h = this.minHeight + ((this.maxHeight - this.minHeight) * (scale - 1));
			//h = scale * (this.maxHeight - this.minHeight) ;
			
			offset = this.image.offset();
			parentOffset = this.image.parent().offset();
			parentWidth = this.image.parent().innerWidth();
			parentHeight = this.image.parent().innerHeight();
			
			offset.left -= parentOffset.left;
			offset.top -= parentOffset.top;
			
			cx = parentWidth / 2;
			cy = parentHeight / 2;
			cx -= offset.left;
			cy -= offset.top;
			
			scx = cx / img.width();
			scy = cy / img.height();
			
			ncx = scx * w;
			ncy = scy * h;
			
			l = (parentWidth/2) - ncx;
			t = (parentHeight/2) - ncy;
			
			if ( l < (parentWidth - w)) {
				l = parentWidth - w;
			}
			if ( t < (parentHeight - h)) {
				t = parentHeight - h;
			}
			if (l > 0) {
				l = 0;
			}
			if (t > 0) {
				t = 0;
			}
			
			img.attr({
				width:w,
				height:h
			});
			this.image.css({
				left: l+"px",
				top: t+"px"
			});
		},
		
		centerImage: function() {
			var img, l, t, parentWidth, w;
			
			img = $("img" ,this.image);
			
			parentWidth = this.image.parent().innerWidth();
			
			l = Math.round((parentWidth - img.width()) / 2);
			
			w = img.width();
			if ( l < (parentWidth - w)) {
				l = parentWidth - w;
			}
			if (l > 0) {
				l = 0;
			}
			
			this.image.css({
				left: l+"px"
			});
		}
	});
	
	
	$.extend($.zoomPan, {
		defaults: {
		}
	});
	
	$.widget("ui.pan", $.extend({}, $.ui.mouse, {
		_init: function() {
			var self = this;
			this.parent = this.element.parent();
			this.image = $("img", this.element);
			if ($.browser.mozilla) {
				this.cursor_def = "-moz-grab";
				this.cursor_grabbing = "-moz-grabbing";
			} else {
				this.cursor_def = "url('images/openhand.cur'), url('images/openhand.gif'), move";
				this.cursor_grabbing = "url('images/closedhand.cur'), url('images/closedhand.gif'), move";
			}
			this.element.css("cursor",this.cursor_def);
			
			this.element.bind("mousewheel", function(event, delta) {
				var instance = $(this).parents(".zoomPan").data("zoomPan");
				if (instance) {
					if (delta > 0) {
						instance.zoomIn.call(instance);
					} else {
						instance.zoomOut.call(instance);
					}
				}
			});
			
			this._mouseInit();
			
		},
		
		_mouseDown: function(e) {
			this.element.css("cursor",this.cursor_grabbing);
			return $.ui.mouse._mouseDown.call(this,e);
		},
		_mouseUp: function(e) {
			this.element.css("cursor",this.cursor_def);
			return $.ui.mouse._mouseUp.call(this,e);
		},
		
		_mouseStart: function(e) {
			return true;
		},
		_mouseCapture: function(e) {
			//console.debug(this);
			this.offset = this.element.offset();
			this.parentOffset = this.parent.offset();
			
			this.offset.left -= this.parentOffset.left;
			this.offset.top -= this.parentOffset.top;
			
			this.panWidth = this.parent.innerWidth() - $(this.image).width();
			this.panHeight = this.parent.innerHeight() - $(this.image).height();
			
			this.startPos = {
				left: e.pageX - this.parentOffset.left - this.offset.left,
				top: e.pageY - this.parentOffset.top - this.offset.top
			}
			return true;
		},
		_mouseDrag: function(e) {
			//console.debug(this);
			this.curPos = {
				left: e.pageX - this.parentOffset.left - this.offset.left,
				top: e.pageY - this.parentOffset.top - this.offset.top
			}
			var x, y;
			x = this.curPos.left - this.startPos.left + this.offset.left;
			y = this.curPos.top - this.startPos.top + this.offset.top;
			
			if ( x < this.panWidth ) { x = this.panWidth; }
			if ( y < this.panHeight ) { y = this.panHeight; }
			if (x > 0) { x = 0; }
			if (y > 0) { y = 0; }
			
			this.element.css({
				left: x+"px",
				top: y+"px"
			});
			
			return false;
		},
		_mouseStop: function(e) {
			//this.element.removeClass("image-drag");
			return false;
		},
		pan: function(dir) {
			this.offset = this.element.offset();
			this.parentOffset = this.parent.offset();
			
			this.offset.left -= this.parentOffset.left;
			this.offset.top -= this.parentOffset.top;
			
			this.panWidth = this.parent.innerWidth() - $(this.image).width();
			this.panHeight = this.parent.innerHeight() - $(this.image).height();
			
			x = this.offset.left;
			y = this.offset.top;
			if (dir == 'right') {
				x -= 200;
			} else if (dir == 'left') {
				x += 200;
			} else if (dir == 'top') {
				y += 200;
			} else if (dir == 'bottom') {
				y -= 200;
			}
			//y = this.offset.left + 100;
			
			/*
			
			this.curPos = {
				left: e.pageX - this.parentOffset.left - this.offset.left,
				top: e.pageY - this.parentOffset.top - this.offset.top
			}
			var x, y;
			x = this.curPos.left - this.startPos.left + this.offset.left;
			y = this.curPos.top - this.startPos.top + this.offset.top;
			*/
			
			if ( x < this.panWidth ) { x = this.panWidth; }
			if ( y < this.panHeight ) { y = this.panHeight; }
			if (x > 0) { x = 0; }
			if (y > 0) { y = 0; }
			
			this.element
				.stop()
				.animate({left:x+"px", top:y+"px"}, {queue:false,duration:500});
			
		}
	}));
	
	
	$.extend($.ui.pan, {
		defaults: {
			delay: 0,
			distance: 1
		}
	});
	
	$(function() {
		$(".zoomPan").zoomPan();
	});
})(jQuery);
