/*!
 * jQuery axzoomer   1.2
 * Alban Xhaferllari
 * albanx@gmail.com
 * Copyright 2010, AUTHORS.txt (http://www.albanx.com)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * Please preserve this heading always
 * http://jquery.org/license
 *
 * 
 */

(function($)
{
	var methods =
	{
		init : function(options)
		{
		    var settings = 
		    {
		    	'maxZoom':4,
		    	'zoomIn':'',
		    	'zoomOut':'',
		    	'showControls':true,
		    	'opacity':0.5,
		    	'sensivity':10
		    };
    	    return this.each(function() 
    	    {
    	    	var $this=$(this);
				if(options) $.extend(settings,options);//extends default options
					
				if(this.tagName!='IMG')//apply only to tag image
				{
					$.error('Zoomer plugin applies only to img tag.');
					return false;
				}
				
				//mem old settings for destroy call
				$this.data('init-status',{
					position:$this.css('position'),
					parent:$this.parent(),
					left:$this.css('left'),
					top:$this.css('top')
				});
				
				//prevent applying zoom plugin twice
				if($this.hasClass('ax-zoom'))
					return;
				
				//add class and set absolute position to image for drag and zoom
				$this.addClass('ax-zoom')
					 .css({position:'absolute',top:0,left:0})
					 .data('settings',settings);
				
				// Detect device type, normal mouse or touchy(ipad android)
				var touchy=(/iphone|ipod|ipad|android/i).test(navigator.userAgent);
				var move=(touchy)?'touchmove.axzoomer':'mousemove.axzoomer';
				var end=(touchy)?'touchend.axzoomer':'mouseup.axzoomer';
				var down=(touchy)?'touchstart.axzoomer':'mousedown.axzoomer';

				//create the container div and append to image parent
				var container=$('<div class="ax-container" />').appendTo($this.parent())
										  .css({'position':'relative','left':0,'top':0})
										  .hover(function(){
											  if(settings.showControls)
												  controlsDiv.show('fade');
										  },function(){
											  if(settings.showControls)
												  controlsDiv.hide('fade');
										  });
				$this.data('container',container);//mem for desotroy method
				//div window for zoom view
				var windowDiv=$('<div class="ax-window" />').css({overflow:'hidden','position':'absolute','left':0,'top':0})
										  .appendTo(container)//attach to first parent
										  .append($this);//the move img to div
				
				//Div containing buttons and controls
				var controlsDiv=$('<div class="ax-controls"/>').appendTo(container).hide()
											.css({'position':'absolute','opacity':settings.opacity});
				
				//function to zoom on button down using timers
				var zoomtimer;
				
				$this.controlZoom=function(x)
				{
					$this.zoomInOut(x);
					zoomtimer=setTimeout(function(){
						$this.controlZoom(x);
					},30); 
				};
				
				//zoom in button	
				var customIn=($this.attr('zoomIn')!=undefined)?$this.attr('zoomIn'):"";
				var zoombut_in;
				if(customIn.indexOf("#")!=-1)
					zoombut_in=$(customIn);
				else if(settings.zoomIn!='')
					zoombut_in=$('<img src="'+settings.zoomIn+'" />').appendTo(controlsDiv).css('cursor','pointer');
				else
					zoombut_in=$('<input type="button" value="+"/>').appendTo(controlsDiv).css('cursor','pointer');

				zoombut_in.bind(down,function(e){
							  e.stopPropagation();
							  MOUSEX=MAIN_WIDTH/2;
							  MOUSEY=MAIN_HEIGHT/2;
							  $this.controlZoom(0.05);
						  })
						  .bind(end,function(){
							   clearTimeout(zoomtimer);
						  })
						  .bind('mouseout',function(){
							   clearTimeout(zoomtimer);
						   });				
				
				//zoom out button
				var customOut=($this.attr('zoomOut')!=undefined)?$this.attr('zoomOut'):"";
				var zoombut_out;
				if(customOut.indexOf("#")!=-1)
					zoombut_out=$(customOut);
				else if(settings.zoomOut!='')
					zoombut_out=$('<img src="'+settings.zoomOut+'" />').appendTo(controlsDiv).css('cursor','pointer');
				else
					zoombut_out=$('<input type="button" value="-"/>').appendTo(controlsDiv).css('cursor','pointer');
				zoombut_out.bind(down,function(e){
							   e.stopPropagation();
							   MOUSEX=MAIN_WIDTH/2;
							   MOUSEY=MAIN_HEIGHT/2;
							   $this.controlZoom(-0.05);
						   })
						   .bind(end,function(){
							   clearTimeout(zoomtimer);
						   })
						   .bind('mouseout',function(){
							   clearTimeout(zoomtimer);
						   });

				//image initial width and height			
				var MAIN_WIDTH=$this.width();
				var MAIN_HEIGHT=$this.height();
				//sources
				var MAIN_SRC=$this.attr('src');//normal size source
				var ZOOM_SRC=$this.attr('src-big');//hight resolution source of image
				//image current width and height
				var CURR_W=0,CURR_H=0;
				var MAIN_TOP=0,MAIN_LEFT=0;	
				
				//wait image preload for setup
				var imgLoad=new Image();
				imgLoad.onload=function(){
					MAIN_WIDTH=$this.width();
					MAIN_HEIGHT=$this.height();
					container.css({'width':MAIN_WIDTH, 'height':MAIN_HEIGHT});
					windowDiv.css({'width':MAIN_WIDTH, 'height':MAIN_HEIGHT});
					//setting left so to center the tools 
					controlsDiv.css({'height':35,'top':MAIN_HEIGHT-30,'left':(MAIN_WIDTH-controlsDiv.width())/2});
					CURR_W=MAIN_WIDTH;//curr dims img
					CURR_H=MAIN_HEIGHT;
				};
				imgLoad.src=this.src;
			
				//function to get MOUSEX, MOUSEY postiion relative to window
				//or to get touchx,y medium coordinates on more than one touche
				var TOUCHNUM=0;
				function medium_coors(e,rel2obj)
				{
					var fingers=1;
					if(rel2obj && (MAIN_TOP==0 || MAIN_LEFT==0))//calculate once for perfomance
					{
						MAIN_TOP=$(rel2obj).offset().top;
						MAIN_LEFT=$(rel2obj).offset().left;
					}
					
					if(touchy)//i consider only two touches for zooming
					{
						e=e.originalEvent;
						fingers=e.touches.length;
						var xsum=0,ysum=0;
						//calculate centroid for touches
						for(var i=0;i<fingers;i++)
						{
							xsum+=(e.touches[i].pageX-MAIN_LEFT);
							ysum+=(e.touches[i].pageY-MAIN_TOP);
						}
					
						return [xsum/fingers,ysum/fingers,fingers];
					}
					else
					{
						return [e.pageX-MAIN_LEFT,e.pageY-MAIN_TOP,fingers];
					}
				}
				/******************************************************/
				
				var ZOOM=false;
				var START_X=0;//start X move position
				var START_Y=0;//start Y move position
				
				var MOUSEX=0;//mouse X relative to window DIV
				var MOUSEY=0;//mouse Y relative to window DIV
				
				var CURR_Y=0;//curr pos of img relative to window DIV
				var CURR_X=0;//obiusvly is 0 0 at start
				
				$this.ENABLE_DRAG=false;
				//bind events to window div, not to image tag, we leave image tag free
				windowDiv
				.bind(down,function(e){
					e.preventDefault();
					//get starting position relative to windowDiv always
					CURR_X=$this.get(0).offsetLeft;
					CURR_Y=$this.get(0).offsetTop;
					CURR_W=$this.width();
					CURR_H=$this.height();
					
					//update and get image position, dimensions
					var coors=medium_coors(e,this);
					START_X=coors[0];
					START_Y=coors[1];
					TOUCHNUM=coors[2];
					if(e.shiftKey)
						$this.controlZoom(0.05);
					else if(e.altKey)
						$this.controlZoom(-0.05);
					else
						$this.ENABLE_DRAG=true;					
				})
				.bind(move,function(e){//mouse move mouse coords and drag
					e.preventDefault();
					var coors=medium_coors(e,this);
					MOUSEX=coors[0];
					MOUSEY=coors[1];
					TOUCHNUM=coors[2];
					if(TOUCHNUM<=1 && $this.ENABLE_DRAG)//drag only with one touch finger
						$this.drag();
				})
				.bind(end,function(e){
					$this.ENABLE_DRAG=false;//clearTimeout(dragTimer);//clear timer on mouseup
					clearTimeout(zoomtimer);
				})
				.bind('dblclick',function(e){
					if(!e.altKey && !e.shiftKey)
					{
						var coors=medium_coors(e,this);
						MOUSEX=coors[0];
						MOUSEY=coors[1];
						$this.zoomInOut(1);
					}
				});	
				
				
				$(document).bind(end,function(ev){
					$this.ENABLE_DRAG=false;//clearTimeout(dragTimer);
					clearTimeout(zoomtimer);
				});
				
				$this.data('ENABLE-AXZ',true);
				$this.drag=function()//simple drag function, no jquery
				{
					if($this.data('ENABLE-AXZ')!=true)
						return;
					
					var new_x=MOUSEX-START_X+CURR_X;
					//set drag limits
					new_x=Math.min(new_x,0);//limit left to 0
					new_x=Math.max(new_x,MAIN_WIDTH-CURR_W);//and to div window limit 
					
					var new_y=MOUSEY-START_Y+CURR_Y;
					//set drag limits
					new_y=Math.min(new_y,0);
					new_y=Math.max(new_y,MAIN_HEIGHT-CURR_H);
					$this.css({'top':new_y, 'left':new_x});
				};			
				
				//zooom on double click only on normal not ipad
								
				/******************************ZOOOM FUNCTIONS****************************/	
				if(touchy)
				{
					windowDiv.get(0).ongesturechange=function(e)
					{
						e.preventDefault();
						if(TOUCHNUM==2)	
						{
							var delta=(e.scale<1)?-1:1;
							$this.zoomInOut(delta/settings.sensivity);
						}
						else if(TOUCHNUM>=3)
						{
							  $this.css('webkitTransform',  "rotate(" + ((e.rotation) % 360) + "deg)");
						}
					};
				}
				else
				{
					windowDiv.mousewheel(function(e,delta) { 
						e.preventDefault();
						$this.zoomInOut(delta/settings.sensivity);						
					});
				}			
				
				$this.zoomLevel=1;
				var timeoutloader;
			    
				$this.zoomInOut=function (zoom)//zoom is fracntion, percent, not level
				{
					if($this.data('ENABLE-AXZ')!=true)
						return;
					clearTimeout(timeoutloader);
					
					//calculate new dims
					CURR_W=$this.width();
					CURR_H=$this.height();
					var new_w=Math.max(CURR_W*(1+zoom),MAIN_WIDTH);//limit dim to original in small
					var new_h=Math.max(CURR_H*(1+zoom),MAIN_HEIGHT);
					
					//get current dims and position
					CURR_X=$this.get(0).offsetLeft;
					CURR_Y=$this.get(0).offsetTop;
					//calculate new position
					var tmpx=zoom*(CURR_X-MOUSEX)+CURR_X;
					tmpx+=Math.max(MAIN_WIDTH-tmpx-new_w+zoom,0);//caculate new x to mantain inside windowDiv visibility
					var new_x=Math.min(tmpx,0);//limit x to 0 coordinate in positive
					
					var tmpy=zoom*(CURR_Y-MOUSEY)+CURR_Y;
					tmpy+=Math.max(MAIN_HEIGHT-tmpy-new_h+zoom,0);//+zoom can be avoid, just to be sure
					var new_y=Math.min(tmpy,0);

					var newzoom=new_w/MAIN_WIDTH;
					if(newzoom<<0<=settings.maxZoom)//a bit faster newzoom<<0 == Math.round(newzoom)
					{
						$this.zoomLevel=newzoom;
						$this.css({width:new_w,height:new_h,top:new_y,left:new_x});
			            timeoutloader = setTimeout(function () {
			            	$this.preload_zoom();//preload zoom image meanwhile
			            }, 500);
					}		
				};
				
				//zoom image preload
				$this.preload_zoom=function()
				{	//preload only if zoom is bigger than 1.2
					if($this.zoomLevel>1.2 && ZOOM_SRC!='' && ZOOM_SRC!=null)
					{
						var load=new Image();
						load.onload=function()
						{
							$this.attr('src',ZOOM_SRC);
						};
						load.src=ZOOM_SRC;
					}
					else if($this.zoomLevel==1)
					{
						$this.attr('src',MAIN_SRC);
					}
				};	
    	    });
		},
		enable:function()
		{
			return this.each(function()
			{
				var $this = $(this);
				$this.data('ENABLE-AXZ',true);
			});
		},
		disable:function()
		{
			return this.each(function()
			{
				var $this = $(this);
				$this.data('ENABLE-AXZ',false);
			});
		},
		destroy : function()
		{
			return this.each(function()
			{
				var $this = $(this);
				$this.removeData('settings').removeClass('ax-zoom');
				var old=$this.data('init-status');
				$this.css({'position':old.position,'left':old.left,'top':old.top}) //reset init css status
				     .appendTo(old.parent)//append to orginal parent
				     .data('container').remove();//remove container created by plugin
				$(document).unbind('.axzoomer');//unbind events attach to document by plugin
			});
		},
		option : function(option, value)
		{
			return this.each(function(){
				var $this=$(this);
				var curr_setts=$this.data('settings');
				curr_setts[option]=value;
				$this.data('settings',curr_setts);
				
			});
		}
	};

	$.fn.axzoomer = function(method, options)
	{
		if(methods[method])
		{
			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if(typeof method === 'object' || !method)
		{
			return methods.init.apply(this, arguments);
		}
		else
		{
			$.error('Method ' + method + ' does not exist on jQuery.zoomer3d');
		}
	};

})(jQuery);

