// SpryPanAndZoomPlugin.js - version 0.2 - Spry Pre-Release 1.7
//
// Copyright (c) 2008. Adobe Systems Incorporated.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of Adobe Systems Incorporated nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

(function() { // BeginSpryComponent

if (typeof Spry == "undefined" || !Spry.Widget || !Spry.Widget.ImageSlideShow)
{
	alert("SpryPanAndZoomPlugin.js requires SpryImageSlideShow.js!");
	return;
}

var gPZP = Spry.Widget.ImageSlideShow.PanAndZoomPlugin = {
	config: {
		defaultPanZoomSettings: [
			[  50,  50, 200,  50,  50, 100 ], // Zoom out from center.
	
			[   0,   0, 200, 100, 100, 100 ], // Pan from upper-left to lower-right.
			[ 100, 100, 200,   0,   0, 100 ], // Pan from lower-right to upper-left.
	
			[   0, 100, 200, 100,   0, 100 ], // Pan from lower-left to upper-right.
			[ 100,   0, 200,   0, 100, 100 ], // Pan from upper-right to lower-left.
	
			[  50,   0, 200,  50, 100, 100 ], // Pan from top to bottom.
			[  50, 100, 200,  50,   0, 100 ], // Pan from bottom to top.
	
			[   0,  50, 200, 100,  50, 100 ], // Pan from left to right.
			[ 100,  50, 200,   0,  50, 100 ]  // Pan from right to left.
		]
	},

	initialize: function(ss)
	{
		ss.panAndZoomInfo = { animations: [], pzSettings: [] };
		ss.addObserver(this);
	},

	fitAtoBRatio: function(aW, aH, bW, bH)
	{
		return Math.max(bW/aW, bH/aH);
	},

	onPostAttachViewBehaviors: function(ss,evt)
	{
		var clip = Spry.$$("." + ss.clipClass, ss.element)[0];
		ss.makePositioned(clip);
	
		Spry.$$("." + ss.clipClass + " img", ss.element).forEach(function(n){ n.style.top = n.style.left = 0; n.style.position = "absolute"; });
	},

	onPostExtractImageInfo: function(ss, evt)
	{
		var re = evt.repeatingElements;
		var pzs = ss.panAndZoomInfo.pzSettings;

		for (var i = 0; i < re.length; i++)
		{
			var e = re[i];

			var v = e.getAttribute("rel");
			if (!v)
				v = e.getAttribute("alt");

			var p = pzs[i] = new Object;

			if (v && v.search(/\[(\s*\d+\s*,){5}\s*\d+\s*\]/) != -1)
			{
				var m = v.match(/\[(\s*\d+\s*,){5}\s*\d+\s*\]/);
				if (m)
				{
					var pz = m[0].replace(/\[|\|\s*]/g, "").split(",");
					p.x1 = parseFloat(pz[0]);
					p.y1 = parseFloat(pz[1]);
					p.z1 = parseFloat(pz[2]);
					p.x2 = parseFloat(pz[3]);
					p.y2 = parseFloat(pz[4]);
					p.z2 = parseFloat(pz[5]);
				}
			}
		}
	},

	onPostStartSlideShow: function(ss, evt)
	{
		var slideIndex = ss.getCurrentSlideIndex();
		if (slideIndex >= 0)
			gPZP.setupSlide(ss, ss.getCurrentSlide(), slideIndex);
	},
	
	onPostStopSlideShow: function(ss, evt)
	{
		gPZP.stopAnimations(ss);
	},
	
	addAnimation: function(ss, anim)
	{
		ss.panAndZoomInfo.animations.push(anim);
		anim.addObserver({ onAnimationComplete: function(){ gPZP.removeAnimation(ss, anim); } });
	},

	removeAnimation: function(ss, anim)
	{
		var anims = ss.panAndZoomInfo.animations;
		if (anims)
		{
			for (var i = 0; i < anims.length; i++)
				if (anims[i] == anim)
				{
					anims.splice(i, 1);
					return;
				}
		}
	},

	stopAnimations: function(ss)
	{
		var anims = ss.panAndZoomInfo.animations;
		while (anims && anims.length)
			anims.pop().stop();
	},

	getPanZoomSettings: function(ss, slideIndex)
	{
		var result = {};

		var info = ss.imageInfo[slideIndex];
		
		result.width = info.width;
		result.height = info.height;

		// Get any pan and zoom settings from the specified element.
	
		Spry.Widget.setOptions(result, ss.panAndZoomInfo.pzSettings[slideIndex]);
	
		// If the element had no pan and zoom settings, pick a random
		// set from the defaults.
	
		if (typeof result.x1 == "undefined")
		{
			var pzs = gPZP.config.defaultPanZoomSettings[Math.round(Math.random() * (gPZP.config.defaultPanZoomSettings.length - 1))];
	
			result.x1 = pzs[0];
			result.y1 = pzs[1];
			result.z1 = pzs[2];
			result.x2 = pzs[3];
			result.y2 = pzs[4];
			result.z2 = pzs[5];
		}
	
		return result;
	},

	getCenteredClippedViewRect: function(x, y, iw, ih, vw, vh)
	{
		var hvw = vw / 2;
		var hvh = vh / 2;
	
		var vx = x - hvw;
		var vy = y - hvh;
	
		var vx2 = x + hvw;
		var vy2 = y + hvh;
	
		var pos = { x: vx, y: vy };
	
		if (vx < 0)
			pos.x = 0;
		else if (vx2 > iw)
			pos.x = iw - vw;
	
		if (vy < 0)
			pos.y = 0;
		else if (vy2 > ih)
			pos.y = ih - vh;
	
		return { x: pos.x, y: pos.y, w: vw, h: vh };
	},


	setupSlide: function(ss, slide, slideIndex)
	{
		if (!ss || !slide)
			return;
	
		// Get the dimensions of the clip view.
	
		var clip = Spry.$$("." + ss.clipClass, ss.element)[0];
		var cw = clip.offsetWidth;
		var ch = clip.offsetHeight;
	
		// Now get the original dimensions of the image
		// in the current slide and calculate the ratio
		// that will cause the image to minimally fill up
		// the clip view area.
	
		var ele = Spry.$$("img", slide)[0];
	
		var kb = gPZP.getPanZoomSettings(ss, slideIndex);
	
		var iw = kb.width;
		var ih = kb.height;
	
		var ratio = gPZP.fitAtoBRatio(iw, ih, cw, ch);
	
		if (ss.isInPlayMode())
		{
			var startZoom = ratio * kb.z1 / 100;
			var endZoom   = ratio * kb.z2 / 100;
		
			var sw = iw * startZoom;
			var sh = ih * startZoom;
			var sx = sw * kb.x1 / 100;
			var sy = sh * kb.y1 / 100;
		
			var startRect = gPZP.getCenteredClippedViewRect(sx, sy, sw, sh, cw, ch);
		
			var ew = iw * endZoom;
			var eh = ih * endZoom;
			var ex = ew * kb.x2 / 100;
			var ey = eh * kb.y2 / 100;
		
			var endRect = gPZP.getCenteredClippedViewRect(ex, ey, ew, eh, cw, ch);
		
			ele.style.width  = sw + "px";
			ele.style.height = sh + "px";
			ele.style.left   = -startRect.x + "px";
			ele.style.top    = -startRect.y + "px";
	
			var anim = new Spry.Effect.CSSAnimator(ele, "top: " + (-endRect.y) + "px; left: " + (-endRect.x) + "px; width: " + ew + "px; height: " + eh + "px;", { dropFrames: ss.dropFrames, duration: ss.displayInterval + (ss.transitionDuration) });
			gPZP.addAnimation(ss, anim);
			anim.start();
		}
		else
		{
			if (iw > cw || ih > ch)
			{
				var ratio = gPZP.fitAtoBRatio(cw, ch, iw, ih);
	
				iw = Math.round(iw / ratio);
				ih = Math.round(ih / ratio);
			}
	
			ele.style.width = iw + "px";
			ele.style.height = ih + "px";
			ele.style.left = Math.round((cw - iw) / 2) + "px";
			ele.style.top = Math.round((ch - ih) / 2) + "px";
		}
	},

	onPostShowSlide: function(ss, evt)
	{
		if (evt.slideIndex >= 0)
			gPZP.setupSlide(ss, evt.target, evt.slideIndex);
	}
};

})(); // EndSpryComponent
