
	ocsfImageZoomers = [];
	$(document).ready(function(){
		
		$('.product_image_small').each(function(i,div){
			var img = $(div).find('img:first');
			ocsfImageZoomers.push( new OCSF_ImageZoomer( div, img, img.attr('src').replace(/^(.+)\/([0-9]+)tn([0-9]+)\./,'$1/$2tn500.') ) );
		});
		
		$('.product_image_large').each(function(i,div){
			var img = $(div).find('img:first');
			ocsfImageZoomers.push( new OCSF_ImageZoomer( div, img, img.attr('src').replace(/^(.+)\/([0-9]+)tn([0-9]+)\./,'$1/$2tn500.') ) );
		});
	});
	
	var OCSF_ImageZoomer = Class.create({
		
		config: {
			hover_time_to_start: 150,
			zoom_out_duration: 200,
			zoom_in_duration: 600
		},
		
		active: false,
		image: null,
		image_container: null,
		large_image: null,
		large_image_load: null,
		container: null,
		large_image_width: 0,
		large_image_height: 0,
		image_width: 0,
		image_height: 0,
		timeout_over: null,
		timeout_out: null,
		
		out: function(a)
		{
			//dbg('__out '+a);
			this.active = false;
			
			this.restore();
		},

		restore: function()
		{
			this.over_cancel();

			if( this.container ) {
				
				this.container.unbind('mousemove');
				this.container.unbind('mouseout');
				//this.large_image.unbind('mouseout');

				if( this.animation ) this.animation.stop();

				if( this.large_image_width > this.large_image_height ) {
					var o = {'height':this.image_height};
				} else {
					var o = {'width':this.image_width};
				}
				
				this.animation = this.large_image.animate(o,
				{
					duration: this.config.zoom_out_duration,
					complete: function()
					{
						if( this.image_container ) this.image_container.show();
						this.container.remove();
						this.container = null;
						
					}.bind(this),
					
					step: function(now, fx)
					{
						this.large_image.css(fx.prop,now+'px');
						this.move();
						
					}.bind(this)
				});

			} else {
				if( this.image_container ) this.image_container.show();
				if( this.container ) this.container.remove();
				this.container = null;
			}
		},
		
		move: function()
		{
			if( ! this.container ) return;
			
			var large_image_width = this.large_image.width();
			var large_image_height = this.large_image.height();
			
			var padding_percentage = .15;
			var paddingX = parseInt( this.image_width * padding_percentage, 10 );
			var paddingY = parseInt( this.image_height * padding_percentage, 10 );

			var offset = this.container.offset();

			var relX = document.coords.x - offset.left;
			var relY = document.coords.y - offset.top;
			
			if( large_image_width > large_image_height ) {
				//~ sw/sh = lw/lh
				//~ sh = this.image_height
				//~ sw = (lw/lh)*sh;

				var adjusted_image_height = this.image_height;
				var adjusted_image_width = parseInt( ( large_image_width / large_image_height ) * adjusted_image_height, 10 );
				
				var offset = Math.abs( adjusted_image_height - adjusted_image_width ) / 2;
				
			} else {
				//~ sw = this.image_width;
				//~ sw/sh = lw/lh
				//~ sh = (sw*lh)/lw

				var adjusted_image_width = this.image_width;
				var adjusted_image_height = parseInt( ( adjusted_image_width * large_image_height ) / large_image_width, 10 );

				var offset = Math.abs( adjusted_image_height - adjusted_image_width ) / 2;
			}
						
			var percentX = ( relX - paddingX ) / ( this.image_width - paddingX*2 );
			var percentY = ( relY - paddingY ) / ( this.image_height - paddingY*2 );

			if( percentX < 0 ) percentX=0;
			else if( percentX > 1 ) percentX=1;

			if( percentY < 0 ) percentY=0;
			else if( percentY > 1 ) percentY=1;

			var left = -parseInt(percentX* (large_image_width-adjusted_image_width),10);
			var top = -parseInt(percentY*(large_image_height-adjusted_image_height),10);

			if( large_image_width!=large_image_height && this.image_width==this.image_height ) {
				if( large_image_width > large_image_height ) {
					left -= offset;
				} else {
					top -= offset;
				}
			}
			
			this.large_image.css({
				'left':left,
				'top':top
			});
		},
		
		over_load: function()
		{
			if( ! this.active ) return;

			this.large_image = this.large_image_load;
			
			this.large_image.css({
				'position':'absolute'
			});

			if( ! this.container ) {
				this.image_container.hide();
				this.image_container.unbind('mouseout');

				this.container = $('<div>')
					.attr('class',this.image_container.attr('class'))
					.append( this.large_image )
					.css({
						'display':'block',
						'position':'relative',
						'overflow':'hidden',
						'width':this.image_width+'px',
						'height':this.image_height+'px'
					})
					.insertAfter(this.image_container);

				//if( this.large_image.width() < this.image_width ) {
				/* this bug shows up if the width is greater than the height, 
				 * and a "restore" call has been made.  The animation makes the
				 * height shrink, and so the width sometimes ends up being 1 less 
				 * than it was originally.  E.g., after restore, the large_image.width()
				 * is 299, and therefore less than 300. */
				if( ( this.large_image.width() - this.image_width ) < -2 ) {
					dbg('LESS THAN: ' + this.large_image.width() + ' < ' + this.image_width);
					this.out('less than');
					return;
				}
			}

			this.container.bind('mousemove', this.move.bind(this));
			this.container.bind('mouseout', this.out.bind(this,'container') );
			//this.large_image.bind('mouseout', this.out.bind(this,'large_image') );

			if( !this.large_image_width ) this.large_image_width = this.large_image.width();
			if( !this.large_image_height ) this.large_image_height = this.large_image.height();

			if( this.large_image_width > this.large_image_height ) {
				this.large_image.css({'width':'auto','height':this.image_height+'px'});
				var o = {'height':this.large_image_height};
			} else {
				this.large_image.css({'width':this.image_width+'px','height':'auto'});
				var o = {'width':this.large_image_width};
			}
			
			this.animation = this.large_image.animate(o,
			{
				duration: this.config.zoom_in_duration,
				step: function(now, fx)
				{
					this.large_image.css(fx.prop,now+'px');
					this.container.trigger('mousemove');
					
				}.bind(this)
			});
		},
		
		over_timeout: function()
		{
			if( ! this.active ) return;
			
			this.over_cancel();
			
			this.restore();
			
			if( this.large_image ) {
				this.over_load();
				
			} else {
				this.large_image_load = $(new Image());
				this.large_image_load.bind('load',this.over_load.bind(this));
				this.large_image_load.attr('src',this.large_image_src);
			}
		},
		
		over_cancel: function()
		{
			if( this.timeout_over ) {
				clearTimeout( this.timeout_over );
				this.timeout_over = 0;
			}
		},
		
		over: function()
		{
			this.trigger_all_stop();

			this.active = true;

			this.image_width = this.image.width();
			this.image_height = this.image.height();
			
			this.image_container.bind('mouseout', this.out.bind(this,'image') );
			
			this.timeout_over = setTimeout( this.over_timeout.bind(this), this.config.hover_time_to_start );
		},
		
		trigger_all_stop: function()
		{
			$(ocsfImageZoomers).each(function(i,ocsfImageZoomer){
				if( ocsfImageZoomer.active ) {
					ocsfImageZoomer.out('trigger_all_stop');
				}
			});
		},
		
		start_mouse_move_tracker: function()
		{
			if( ! document.coords ) {
				document.coords = {
					x: 0,
					y: 0
				};
				$(document).bind('mousemove',function(evt){
					if( evt && evt.pageX ) {
						document.coords = {
							x: evt.pageX,
							y: evt.pageY
						};
					}
				});
			}
		},
		
		init: function(image_container,image,large_image_src)
		{
			this.image_container = $(image_container);
			this.image = $(image);
			this.large_image_src = large_image_src;
			
			this.image.bind('mouseover', this.over.bind(this) );
			
			this.start_mouse_move_tracker();
		}
	});

	function dbg(a){try{console.log(a)}catch(e){}}

