/*===========================================
 *  photoZoom v0.9		-	2009-07-11
 *	(c)Tymon H. Pigon 	-	www.whiteboxstudios.se
 *
 * Do not distribute
 * Do not criticize 
 * Do not smoke
 */

//-------------------------------------------
//Global Vars
//-------------------------------------------

var myWhitebox;
var Whitebox = Class.create();

var whitebox_orig_color = "#f83666"; //"#cc0000";
var whitebox_blackbox_opacity = 0.85;


//-------------------------------------------
//Whitebox
//-------------------------------------------
Whitebox.prototype = {
	//Called upon creation of Whitebox object
	initialize: function() {
		//Vars
		this.magnified = 0;
		this.body_ref = document.body;
		this.movable = false;
		
		this.allbox   = new Element('div', {'class':'allbox'} );
		this.blackbox = new Element('div', {'class':'blackbox'} );
		this.framebox = new Element('div', {'class':'framebox','id':'framebox'} );
		this.whitebox = new Element('div', {'class':'whitebox','id':'whitebox'} );
				
			
		//do some HTML building
		this.allbox.hide();
		
		this.body_ref.appendChild( this.allbox );
		this.allbox.insert( this.blackbox );
		this.allbox.insert( this.framebox );
		this.allbox.insert( this.whitebox );
		
		this.gloss = new Element('div', {'class':'gloss','id':'gloss'} );
		this.gloss.setStyle('position:absolute;left:0;top:0;width:100%;height:100%;');
		this.watermark = new Element('div', {'class':'watermark','id':'watermark'} );
		this.watermark.setStyle('position:absolute;left:0;top:0;width:100%;height:100%;');
		this.x_bar=new Element('div',{'id':'x_bar'});
		this.y_bar=new Element('div',{'id':'y_bar'});
		this.x_bar.hide();
		this.y_bar.hide();
		this.x_bar.setStyle('position:absolute;background:#666666;height:6px;top:3px;right:auto;outline:1px solid #333333');
		this.y_bar.setStyle('position:absolute;background:#666666;width:6px;left:3px;right:auto;outline:1px solid #333333');
				
		this.gloss.hide();
		this.watermark.hide();
		this.whitebox.insert( this.watermark );
		this.whitebox.insert( this.gloss );
		this.framebox.insert(this.x_bar);
		this.framebox.insert(this.y_bar);
		
		this.magnifyClose = new Element('div',{'class':'magnifyClose','id':'magnifyClose'});
		this.magnifyClose.setStyle('position:absolute;width:300px;height:30px;background:url(images/wbs_magnify_close);cursor:pointer;');
		this.magnifyClose.setOpacity(0.85);
		this.framebox.insert( { before: this.magnifyClose });
		this.magnifyClose.hide();
		
		//Event tracking
		//this.magnifyClose.observe('click', this.closeBox.bindAsEventListener(this));
		//this.blackbox.observe('click', this.closeBox.bindAsEventListener(this));
		this.framebox.observe('click', this.closeBox.bindAsEventListener(this));
		Event.observe(document.onresize ? document : window, "resize", this.windowResized.bindAsEventListener(this) );
		//Event.observe(document.onscroll ? document : window, "scroll", this.windowResized.bindAsEventListener(this) );
				
		//global key events
		document.observe('keydown', this.keypress.bindAsEventListener(this));
	},
	
	keypress: function(e){
		var code = (e.keyCode)? e.keyCode:e.charCode;
		//var code = e.keyCode;
		if( code == Event.KEY_ESC || code == Event.KEY_RETURN){
			if (this.magnified==2){ return this.magnify_1(e,0.1); }
			else{ return this.closeBox(e); }
		}
		if( code == Event.KEY_TAB && this.hasAltState){ return this.toggleAltPhoto(e); } 
	},
	
	windowResized: function(e){
		if( this.magnified==0 ){ return; } //quit if the photo is not being shown
		
		this.blackbox.setStyle('height:' + $('body').getHeight() + 'px');
		this.allbox.setStyle('height:' + $('body').getHeight() + 'px');
		
		//hide distracting elements and resize quickly
		this.hide_whitebox_paraphenelia();
		this.animate_whitebox(0.2);
	},
	
	doExpand: function( thumb_photo_id, filename ){
		
		this.photo = new Element('img', {'class':'photo','id':'photo'} );
		this.photo.hide();
		this.photo.setStyle('position:relative;');
		this.watermark.insert( {'before' : this.photo} );
		
		this.blackbox.setOpacity(0);
		this.allbox.setStyle('height:' + $('body').getHeight() + 'px');
		this.blackbox.setStyle('height:' + $('body').getHeight() + 'px');
		//this.blackbox.setStyle('height:100%;');
		//this.allbox.setStyle('height:100%;');
		this.whitebox.absolutize();
		this.whitebox.setOpacity(0.6);
		this.whitebox.setStyle('background-image:url(images/loading.gif);background-position:center center;background-repeat:no-repeat;');
		this.whitebox.clonePosition( $(thumb_photo_id) );
		
		new Effect.Opacity(this.blackbox, { from: 0.0, to: whitebox_blackbox_opacity, duration: 0.5 });
		this.blackbox.observe('click', this.closeBox.bindAsEventListener(this));
		this.whitebox.setStyle('overflow:hidden;');
		this.whitebox.setStyle('background-color:'+whitebox_orig_color+';');
		this.whitebox.show();
		this.blackbox.show();
		this.allbox.show();
		
		//---------------------------------------
		//Code for altenate image	
		this.showingAlt = false;
		this.photo2loading = new Element('div', {'class':'photo','id':'photo'} );
		this.photo2loading.setStyle('position:absolute;left:0;top:0;height:100%;width:100%;background-color:#f83666;');
		this.photo2loading.setStyle('background-image:url(images/loading.gif);background-position:center center;background-repeat:no-repeat;');
		this.photo2loading.hide();
		
		this.photo2 = new Element('img', {'class':'photo','id':'photo'} );
		this.photo2.hide();
		this.photo2.setStyle('position:absolute;');
		this.photo2.setStyle('top:0px;left:0px;');
		this.photo2.setOpacity(0);

		this.watermark.insert({'before': this.photo2loading });
		this.photo2loading.insert({'before': this.photo2 });
		this.hasAltState = false;
		this.altStateTip = null;
		
		try{
			this.altLoaded=false;
			this.alternate = $(thumb_photo_id).up("a[rel='whitebox']").readAttribute('linkalt');
			
			var content = "<p id='altStateTip' class='altStateTip'>press <b class='sc'>TAB</b> to toggle the original photo</p>";
			this.watermark.insert( content );
			this.altStateTip = $('altStateTip');
			this.altStateTip.hide();
			
			//onload
			this.photo2.onload = function(){
				//alert("photo2 loaded!");
				//Fade the loading graphic for photo 2 on load
				new Effect.Morph( this.photo2loading, {delay:0, duration:0.3,
						   afterUpdate:function(e){ this.photo2loading.setOpacity(1-e.position);}.bind(this),
						   afterFinish:function(e){ this.photo2loading.remove(); }.bind(this)
						   });
				this.altLoaded = true;
			}.bindAsEventListener(this);
			//end onload
			
			//Creat toggle button
			/*this.toggleTrigger = new Element('div', {'class':'toggleTrigger','id':'toggleTrigger'} );
			this.toggleTrigger.setStyle("position:absolute;top:0px;left:0%;height:38px;width:300px;background:yellow;");
			this.allbox.insert(this.toggleTrigger);
			this.toggleTrigger.observe('click', this.toggleAltPhoto.bindAsEventListener(this));*/
			
			//Set the second photo's source and trigger the onload
			if (this.alternate != null){
				this.hasAltState = true;
				//alert("writing photo2 src");
				this.photo2.writeAttribute({'src' : this.alternate });
			}
			
		} catch(err) { this.hasAltState = false; }

		this.magnified = 0;
		this.photo.observe('load', function(){
					this.photo_orig_w = this.photo.getWidth();
					this.photo_orig_h = this.photo.getHeight();
					this.fade_whitebox();
					this.magnify_1();
			}.bindAsEventListener(this) );
		this.photo.writeAttribute({'src' : filename });

	},

	closeBox: function(e){
		this.magnified = 0;
		this.hide_whitebox_paraphenelia();
	
		this.whitebox.hide();
		this.framebox.hide();
		this.blackbox.hide();
		this.allbox.hide();
		
		this.watermark.descendants().each( function(d){ d.remove(); } );
		this.photo.writeAttribute({'src' : null });
		this.photo2.writeAttribute({'src' : null });
		this.photo.stopObserving('load');
		this.photo2.stopObserving('load');
		//this.photo = null;
		//this.photo2 = null;
		
		this.gloss.setStyle('cursor:default')
		this.magnifyClose.stopObserving('click');
		this.framebox.stopObserving('click');
		this.blackbox.stopObserving('click');
		this.allbox.stopObserving('click');
		
		if(this.moveTimer){this.moveTimer.stop();}
	},

	magnifyMouseOver: function(e){
		this.gloss.observe('mouseout', this.magnifyMouseOut.bindAsEventListener(this) );
		this.gloss.setStyle('background-image:url(images/wbs_magnify_w.png);background-position:50% 50%;background-repeat:no-repeat;');
		if( this.hasAltState ){ this.altStateTip.show(); }
	},
	
	magnifyMouseOut: function(e){
		this.gloss.setStyle('background-image:none;');
		if( this.hasAltState ){ this.altStateTip.hide(); }
	},

	magnify_1: function(e, dur){
		this.magnified = 1;
		this.photo.stopObserving('load');
		this.photo2.stopObserving('load'); //	clear onLoad, IE behaves irratically with animated gifs otherwise 
		
		if (!dur){dur=1;}
		
		//Event tracking
		this.magnifyClose.stopObserving('click');
		this.blackbox.stopObserving('click');
		this.magnifyClose.observe('click', this.closeBox.bindAsEventListener(this));
		this.blackbox.observe('click', this.closeBox.bindAsEventListener(this));

		this.hide_whitebox_paraphenelia();
		this.animate_whitebox( dur );
	},
	
	hide_whitebox_paraphenelia: function(){
		this.movable = false;
	
		//clear all current animations
		if(this.moveTimer){this.moveTimer.stop();}
		var queue = Effect.Queues.get('whitebox');
		queue.each(function(effect) { effect.cancel(); });
		
		//kill event monitoring
		this.gloss.stopObserving('mouseover');
		this.gloss.stopObserving('mouseout');
		this.gloss.stopObserving('mousemove');
		this.gloss.stopObserving('mousedown');
		this.gloss.stopObserving('mouseup');
		this.gloss.stopObserving('click');	
		document.stopObserving('mouseup');
		document.stopObserving('mouseout');
		
		//Resetting visibility
		if( this.hasAltState ){ this.altStateTip.hide(); }
		this.x_bar.hide();
		this.y_bar.hide();
		this.magnifyClose.hide();
		this.watermark.hide();
		this.gloss.hide();
		this.photo.hide();
		this.photo2.hide();
		this.framebox.hide(); this.framebox.setOpacity( 0.0 );

		this.allbox.show();		
		this.blackbox.show();
		this.whitebox.show();

	},
	
	fade_whitebox: function(dur){
		new Effect.Morph( this.whitebox, { delay:0, duration:1,
					afterUpdate: function(effect){ (effect.element).setOpacity( 0.4*effect.position+0.6 ); }
		 } );
	},
	
	animate_whitebox: function( dur ){
		//----------------------------------
		//Start animation calculation
		//----------------------------------
		
		var border = (this.magnified==1)? 6 : 12;
		
		var w_w = this.whitebox.getWidth();
		var w_h = this.whitebox.getHeight();
		
		var v_w=document.viewport.getWidth();
		var v_h=document.viewport.getHeight();
	
		//calcualte the greatest applicable aperture and set the photo inside
		if( this.magnified == 1 ){
			// Case small frame (first level magnification)
			var final_w = Math.min( Math.min(this.photo_orig_w, 750), (v_w-2*border)-72);
			var final_h = Math.min( Math.min(this.photo_orig_h, 900), (v_h-2*border-72)*0.95);
			
			//compare ratios and resize the frame to an applicable size.
			var rat_w= this.photo_orig_w/final_w;
			var rat_h= this.photo_orig_h/final_h;
			(rat_w > rat_h) ? (final_h=this.photo_orig_h/rat_w) : (final_w=this.photo_orig_w/rat_h);
			
			//Set photo position
			this.photo.setStyle('top:0;left:0;');
			this.photo2.setStyle('top:0;left:0;');
			this.photo.setStyle('width:'+final_w+'px;height:'+final_h+'px;');
			this.photo2.setStyle('width:'+final_w+'px;height:'+final_h+'px;');		
		}else{
			//Case big frame (second leveel of magnification)
			var final_w = Math.round( Math.min(this.photo_orig_w, (v_w-2*border)-40) );
			var final_h = Math.round( Math.min(this.photo_orig_h, (v_h-2*border)*0.78) );
			
			//Set photo position
			this.photo.setStyle('top:0;left:0;');
			this.photo2.setStyle('top:0;left:0;');
			this.photo.setStyle('width:'+this.photo_orig_w+'px;height:'+this.photo_orig_h+'px;');
			this.photo2.setStyle('width:'+this.photo_orig_w+'px;height:'+this.photo_orig_h+'px;');
		}
		
		var photo_to_fade = ((this.showingAlt) ? this.photo2 : this.photo);
		
		//growing whitebox anim
		var whitebox_top = ( (v_h-final_h)/2 + document.viewport.getScrollOffsets().top  );
		var whitebox_left= ( (v_w-final_w)/2 + document.viewport.getScrollOffsets().left  );
		//( (this.whitebox.positionedOffset().left)-(final_w-w_w)/2 + document.viewport.getScrollOffsets().left );
		var style_str = 'width:'+(final_w)+'px; height:'+(final_h)+'px;';
		style_str += 'left:'+whitebox_left +'px;';
		style_str += 'top:'+ whitebox_top  +'px;';
		style_str += 'background-color:#f1f1f1';
		new Effect.Morph($('whitebox'), {delay:0, duration:dur, style: style_str, queue:{scope:'whitebox'},
						afterFinish: function(effect){
							if( true ){
								//this.whitebox.setStyle('overflow:hidden'); 
								this.whitebox.setStyle('background-image:none'); //hide loading gif.
								this.watermark.show();
						 		this.framebox.show();
								photo_to_fade.show();
								this.animate_framebox(
												  (whitebox_top-border), (whitebox_left-border),
												  (final_w+2*border), (final_h+2*border),
												  (border), (photo_to_fade)
								);
							}
						}.bind(this)
		});
	},
	
	animate_framebox: function( _t, _l, _w, _h, border, photo_to_fade){
		//photo show and frame show
		style_str = 'width:'+_w+'px; height:'+_h+'px;';
		style_str += 'left:'+ _l +'px;';
		style_str += 'top:'+  _t +'px;';
		this.framebox.setStyle( style_str );

		if(this.magnified == 2){ this.moveUpdate(); }
		
		new Effect.Morph( photo_to_fade, {delay:0,duration:0.3, queue:{scope:'whitebox', position:'end'},
						beforeStart: function(effect){
							(effect.element).setOpacity(0);
					 		this.framebox.show();
						}.bind(this),
						afterUpdate: function(effect){
							photo_to_fade.setOpacity( effect.position );
							this.framebox.setOpacity( effect.position*0.85 );
						}.bind(this),
						afterFinish: function(effect){
							this.magnifyClose.setStyle('top:'+_t+'px;left:'+(_l+_w/2-150)+'px;');
							if( _w>300 ){ this.magnifyClose.show(); } //don't show for very thin images, where it won't fit
							
							if( this.magnified == 1){
								//check if we can magnify.
								if( (this.photo_orig_w/_w)>1 || (this.photo_orig_h/_h)>1){ 
									this.gloss.show();
									this.gloss.observe('click', this.magnify_2.bindAsEventListener(this));
									this.gloss.observe('mouseover', this.magnifyMouseOver.bindAsEventListener(this) );
									this.gloss.setStyle('cursor:pointer');		
								}
							} else {
									this.gloss.show();
									this.gloss.observe('mousedown', this.moveInit.bindAsEventListener(this));
									this.gloss.observe('mouseout', this.moveLeave.bindAsEventListener(this));
									this.movable = true;
							}
							this.photo.show(); //If they havn't been shown yet, now is the time to show them.
							this.photo2.show();
						} .bind(this) });

		new Effect.Morph(this.magnifyClose, {delay:0.1,duration:0.3, queue:{scope:'whitebox', position:'end'},
						 style:'top:'+(_t-30)+'px;',
						 beforeStart: function(effect){ /*nothing*/ }.bind(this)	});
	},
	
	//-------------------------------------------------------------------------------------------------------------------
	toggleAltPhoto: function(e){
		e.stop();

		if(!this.hasAltState){ return; }						  //quit if there is no alternate image
		if(!this.altLoaded){ this.photo2loading.show(); return; } //show and quit if the image is not loaded yet
		var queue = Effect.Queues.get('toggleAlt');
		queue.each(function(effect) { effect.cancel(); });
		if(this.showingAlt){
			//this.photo.show();
			new Effect.Morph(this.photo2, {delay:0,duration:0.5, queue:{scope:'toggleAlt'},
						 beforeStart: function(effect){ }.bind(this),
						 afterUpdate: function(effect){ this.photo2.setOpacity(1-effect.position); }.bind(this),
						 afterFinish: function(effect){ //this.photo2.hide();
						 }.bind(this)
						 });
		}else{	
			//this.photo.show();
			this.photo2.setStyle('width:'+this.photo.getWidth()+'px;height:'+this.photo.getHeight()+'px;');
			new Effect.Morph(this.photo2, {delay:0,duration:0.2, queue:{scope:'toggleAlt'},
						 beforeStart: function(effect){ this.photo2.setOpacity(0); /*this.photo2.show();*/ }.bind(this),
						 afterUpdate: function(effect){ this.photo2.setOpacity(effect.position); }.bind(this),
						 afterFinish: function(effect){ //this.photo.hide();
						 }.bind(this)
						 });
		}
		this.showingAlt = !this.showingAlt;
	},
	
	//------------------------------------------------------------------------------------------------------------------
	magnify_2: function(e){
		this.magnified = 2;
		this.hide_whitebox_paraphenelia();
		this.gloss.setStyle('background-image:none;');
		
		//set the "revert" to mag=1 behaviour
		var _f = function(){ this.magnify_1(e,0.1); }.bind(this);
		this.blackbox.stopObserving('click'); this.blackbox.observe('click', _f );

		this.gloss.observe('dblclick', function(){ this.magnify_1(e,0.1); }.bind(this) );
		
		this.animate_whitebox( 0.1 );
	},
	
	//---------------------------------------------------------------------------------------------------------------------
	moveTimerF: function(){		

		/* Thanks Dylan
		/set your bounciness/speed by changing this
		bounce = .25
		//change this to add any elasticity
		stretch = 10
		xdist = _xmouse-_x;
		xspeed += xdist/bounce;
		xspeed *= stretch;
		this._x += xspeed;
		*/
		
		this.Ycurr = this.pointerY;
		this.Xcurr = this.pointerX;
		this.dY =this.Yold- this.Ycurr;
		this.dX =this.Xold- this.Xcurr;
		this.Yold = this.Ycurr;
		this.Xold = this.Xcurr;

		
		var damper = .77;    //0.85
		var increment= 0.27; //0.33
		this.yspeed += (this.dY*increment);
		this.yspeed *= damper;
		this.ymove = Math.round(this.yspeed);
		
		this.xspeed += (this.dX*increment);
		this.xspeed *= damper;
		this.xmove =  Math.round(this.xspeed);
		
		this.moveUpdate();
	
	},
	
	moveInit: function(e){
		if (!this.movable) return;
		this.photo.show();
		this.photo2.show(); //Both must be visivle for positionedOffset to work corectly, apparently.
		
		e.stop(); //stop propagation and default mouse-down behaviour
		this.gloss.setStyle('cursor:move;');
	
		//toggle the evnets observed
		this.gloss.stopObserving('mousedown');
		this.gloss.observe('mousemove', this.move.bindAsEventListener(this) );
		this.gloss.observe('mouseup', this.moveStop.bindAsEventListener(this) );

		//set movement variables
		this.md = {X:e.pointerX(), Y:e.pointerY()};			//remember the mouse-down coordinates
		this.md_p_off = this.photo.positionedOffset();		//remember the photo's initial offset at mouse-down	
		this.yspeed =0;
		this.ymove=0;
		this.xspeed =0;
		this.xmove=0;
		this.md_dX = 0;
		this.md_dY = 0;
		this.pointerX = e.pointerX();
		this.pointerY = e.pointerY();
		this.Xold =e.pointerX();
		this.Yold =e.pointerY();

		//update the bars and photo position
		this.moveUpdate();

		if( this.moveTimer ){ this.moveTimer.stop(); this.moveTimer = null; }
		this.moveTimer = new PeriodicalExecuter(this.moveTimerF.bind(this), 0.04);
		
	},
	
	moveUpdate: function(){		
		
		//defaultize certain variables in case this is a no-click call
		this.p_off = this.photo.positionedOffset(); //the photo's current offset
		this.ymove= (this.ymove) ? this.ymove:0;
		this.xmove= (this.xmove) ? this.xmove:0;
	
		//calculate the span for how far the photo can move in terms of top/left offset.
		var max_x = 0
		var max_y = 0
		var min_x = -( this.photo_orig_w - $('whitebox').getWidth() );
		var min_y = -( this.photo_orig_h - $('whitebox').getHeight() );
		
		//make sure the post-photomovement offsets fall within these ^^ limits
		var style_left = Math.min(max_x, this.p_off.left-this.xmove );
		var style_top =  Math.min(max_y, this.p_off.top-this.ymove );
		    style_left = Math.max(min_x, style_left );
		    style_top =  Math.max(min_y, style_top );
	
		//now for the scrollbars; calculate the possible movement span, and the percentage of maximum movement
		//the scrolbars are to be of a fixed width or height, and move within the frame's span. their size is dependant on zoom.
		var x_span = min_x-max_x;
		var y_span = min_y-max_y;
		var rat_x = style_left/x_span; (rat_x>0) ? rat_x=rat_x : rat_x=0;
		var rat_y = style_top/y_span;  (rat_y>0) ? rat_y=rat_y : rat_y=0;
		var h_perc = $('framebox').getHeight()/this.photo_orig_h;
		var w_perc = $('framebox').getWidth() /this.photo_orig_w;
		h_perc *= $('whitebox').getHeight(); //convert to an actual pixel height for the scrollbar
		w_perc *= $('whitebox').getWidth();  //- " - pixel width - " -
		
		//update the scrollbars first
		this.x_bar.setStyle('width: '+ w_perc +'px;' + 'left:' + rat_x*( $('framebox').getWidth() - w_perc) +'px');
		this.y_bar.setStyle('height:'+ h_perc +'px;' + 'top:'  + rat_y*( $('framebox').getHeight()- h_perc) +'px');
		this.x_bar.show();
		this.y_bar.show();
		
		//move the photo		
		var style_str='left:'+style_left+'px; top:'+style_top+'px;';
		this.photo.setStyle( style_str );
		this.photo2.setStyle( style_str );
		
	},
	
	
	move: function(e){
		this.pointerX = e.pointerX();
		this.pointerY = e.pointerY();
		this.md_dX = this.md.X - this.pointerX; //how much the mouse has moves since mouse-down
		this.md_dY = this.md.Y - this.pointerY; // - " -
	},
	
	moveStop: function(e){
		this.gloss.stopObserving('mousemove');
		this.gloss.stopObserving('mouseup');
	    this.gloss.observe('mousedown', this.moveInit.bindAsEventListener(this));
   	    this.gloss.setStyle('cursor:pointer;');
		e.stop();	
	},
	moveLeave: function(e){
		e.stop();	
		document.observe('mouseup', this.moveStop.bindAsEventListener(this));
		document.observe('mouseout', this.moveStop.bindAsEventListener(this));
		this.gloss.observe('mouseover', this.moveEnter.bindAsEventListener(this));
	},
	moveEnter: function(e){
		e.stop();
		document.stopObserving('mouseup');
		document.stopObserving('mouseout');
	}
	
}//end Whitebox prototype


//-------------------------------------------
//Initialize the script on window HTML load (note: still need to check the images themselves are loaded)
//-------------------------------------------

function initWhitebox(){
	myWhitebox = new Whitebox(); 
	
	var whiteboxes = $$("a[rel='whitebox']");
	//alert(whiteboxes);
	whiteboxes.each( function(box){
	  box.observe( 'click', function(e){ 
		 myWhitebox.doExpand( box.down('img').identify(), box.readAttribute('link'));
     	 } );
	  //alert( box.down('img').identify() +"|" + box.readAttribute('link') );
	  });
	
	}
Event.observe(window, 'load', initWhitebox, false);

