/**
 * pan-irama (Unobtrusive transformation of panorama images into scrolling feature panels)
 * @version 2.0
 * @author Andrew Ramsden <http://irama.org/>
 * @see http://irama.org/web/dhtml/pan-irama/
 * @license GNU GENERAL PUBLIC LICENSE (GPL) <http://www.gnu.org/licenses/gpl.html>
 * @requires jQuery (tested with version 1.2.6) <http://jquery.com/>
 * @requires jQuery Intercept (tested with version 1.1.2) <http://plugins.jquery.com/project/Intercept/>
 * @requires jQuery Timers <http://jquery.offput.ca/every/>
 * @requires jQuery jARIA plugin <http://outstandingelephant.com/jaria/>
 * @requires jQuery ARIA keyboard navigation plugin <http://irama.org/web/dhtml/aria/key-nav/>
 */
 // FIXME: Add keyboard navigation (ARIA activedescendant)
 panConf = {
	containerClass           : 'pan-container',
	windowClass              : 'pan-window',
	windowWrapClass          : 'pan-window-wrap',
	imageElClass             : 'pan-image',
	controlsClass            : 'pan-controls',
	selectedClass            : 'selected',
	defaultSpeedClass        : 'speed-medium',
	slowSpeed                : 'speed-slow',
	mediumSpeedClass         : 'speed-medium',
	fastSpeedClass           : 'speed-fast',
	controlDefaultSpeedClass : 'pan-medium',
	controlSlowSpeedClass    : 'pan-slow',
	controlMediumSpeedClass  : 'pan-medium',
	controlFastSpeedClass    : 'pan-fast',
	defaultDirection         : 'dir-forward',
	reverseDirection         : 'dir-reverse',
	pauseDirection           : 'paused',
	forwardDirection         : 'dir-forward',
	controlDefaultDirClass   : 'pan-forward',
	controlReverseDirClass   : 'pan-reverse',
	controlPauseDirClass     : 'pan-pause',
	controlForwardDirClass   : 'pan-forward',
	loadingClass             : 'loading',
	activeClass              : 'active',
	intervals                : { // in ms
		slow   : 90,
		medium : 50,
		fast   : 15,
		def    : 50 // default
	},
	increments               : { // in pixels
		slow   : 1,
		medium : 1,
		fast   : 1,
		def    : 1 // default
	}
};


$(function(){
	// On DOM Load, init for class="pan"
		$('.pan').pan();
});

(function($) {// start closure

panCount = 0;

$.fn.pan = function () {
	$(this).each(initPanirama);
	
	$('body')
		.intercept('click', '.'+panConf.controlReverseDirClass+' a', panReverse)
		.intercept('click', '.'+panConf.controlPauseDirClass+' a', panPause)
		.intercept('click', '.'+panConf.controlForwardDirClass+' a', panForward)
		.intercept('click', '.'+panConf.controlSlowSpeedClass+' a', panSlow)
		.intercept('click', '.'+panConf.controlMediumSpeedClass+' a', panMedium)
		.intercept('click', '.'+panConf.controlFastSpeedClass+' a', panFast)
	;
};


initPanirama = function (imageSrc, imageWidth, imageHeight, imageAlt) {
	
	// increment count
		panCount++;
	
	// hide original image
		$(this).hide();
	
	imageSrc = $(this).attr('src');
	imageWidth = $(this).attr('width');
	imageHeight = $(this).attr('height');
	imageAlt = $(this).attr('alt');
	panId = $(this).attr('id')||'pan-'+panCount;
	
	panTitle = getDocType() == 'xhtml2' ? $('<h>'+imageAlt+'</h>') : $('<h3>'+imageAlt+'</h3>');
	panTitle
		.ariaRole('heading')
		.attr('id',panId+'-heading')
	;
	
	panImageWrap = $('<div>')
		.addClass(panConf.windowWrapClass)
		.css('height', '0')
		.animate({'height':'100px'},500)
	;
	panWindow = $('<div>')
		.addClass(panConf.windowClass)
		.append(panImageWrap);
	;
	
	panControls = $('<ul>')
		.append('<li class="'+panConf.controlReverseDirClass+'"><a href="#" title="Pan left">Pan left</a></li>')
		.append('<li class="'+panConf.controlPauseDirClass+'"><a href="#" title="Pause">Pause</a></li>')
		.append('<li class="'+panConf.controlForwardDirClass+'"><a href="#" title="Pan right">Pan right</a></li>')
		.append('<li class="'+panConf.controlSlowSpeedClass+'"><a href="#" title="Slow">Slow</a></li>')
		.append('<li class="'+panConf.controlMediumSpeedClass+'"><a href="#" title="Medium">Medium</a></li>')
		.append('<li class="'+panConf.controlFastSpeedClass+'"><a href="#" title="Fast">Fast</a></li>')
	;
	
	panControls.find('a')
		.ariaState('selected','false')
	;
	
	panControlsDiv = $('<div><p class="label"><strong>Controls</strong></p></div>')
		.addClass(panConf.controlsClass)
		.ariaState('multiselectable', true)
		.append(panControls)
		.managefocus('a')
		.controls('#'+panId)
	;
	
	panContainer = $('<div>')
		.addClass(panConf.containerClass)
		.addClass(panConf.loadingClass)
		.attr('id', panId)
		.data('panpos', 0)
		.append(panTitle)
		.append(panWindow)
		.append(panControlsDiv)
		.ariaRole('region')
		.ariaState('labelledby',panId+'-heading')
	;
	
	// init speed
		if ($(this).hasClass(panConf.fastSpeedClass)) {
			panContainer
				.data('paninterval',panConf.intervals.fast)
				.data('panincrement',panConf.increments.fast)
				.addClass(panConf.fastSpeedClass)
				.find('.'+panConf.controlsClass+' .'+panConf.controlFastSpeedClass+' a').ariaState('selected','true')
			;
		} else if ($(this).hasClass(panConf.mediumSpeedClass)) {
			panContainer
				.data('paninterval',panConf.intervals.medium)
				.data('panincrement',panConf.increments.medium)
				.addClass(panConf.mediumSpeedClass)
				.find('.'+panConf.controlsClass+' .'+panConf.controlMediumSpeedClass+' a').ariaState('selected','true')
			;
		} else if ($(this).hasClass(panConf.slowSpeed)) {
			panContainer
				.data('paninterval',panConf.intervals.slow)
				.data('panincrement',panConf.increments.slow)
				.addClass(panConf.slowSpeed)
				.find('.'+panConf.controlsClass+' .'+panConf.controlSlowSpeedClass+' a').ariaState('selected','true')
			;
		} else {
			panContainer
				.data('paninterval',panConf.intervals.def)
				.data('panincrement',panConf.increments.def)
				.addClass(panConf.defaultSpeedClass)
				.find('.'+panConf.controlsClass+' .'+panConf.controlDefaultSpeedClass+' a').ariaState('selected','true')
			;
		}
		
	
	// init direction
		if ($(this).hasClass(panConf.forwardDirection)) {
			panContainer
				.data('pandirection',panConf.forwardDirection)
				.addClass(panConf.forwardDirection)
				.find('.'+panConf.controlsClass+' .'+panConf.controlForwardDirClass+' a').ariaState('selected','true')
			;
		} else if ($(this).hasClass(panConf.reverseDirection)) {
			panContainer
				.data('pandirection',panConf.reverseDirection)
				.addClass(panConf.reverseDirection)
				.find('.'+panConf.controlsClass+' .'+panConf.controlReverseDirClass+' a').ariaState('selected','true')
			;
		} else if ($(this).hasClass(panConf.pauseDirection)) {
			panContainer
				.data('pandirection',panConf.pauseDirection)
				.addClass(panConf.pauseDirection)
				.find('.'+panConf.controlsClass+' .'+panConf.controlPauseDirClass+' a').ariaState('selected','true')
			;
		} else {
			panContainer
				.data('pandirection',panConf.defaultDirection)
				.addClass(panConf.defaultDirection)
				.find('.'+panConf.controlsClass+' .'+panConf.controlDefaultDirClass+' a').ariaState('selected','true')
			;
		}
	
	// insert replacement
		$(this).before(panContainer);
	
	
	// load image and animate
		panImageWrap
			.append('<div><img>')
			.find('div')
				.addClass(panConf.imageElClass)
				.find('img')
					.hide()
					.load(function(){
						
						imageHeight = $(this).height();
						// set width data on container
							$(this).parents('.'+panConf.containerClass+':lt(1)').data('panwidth', $(this).width());
						
						
						// hide image container
							$(this).parent().hide();
						
						// animate height of container (grandparent)
							$(this).parent().parent().animate({'height':imageHeight},500,'linear',function(){
								
								// setup background on outer div
									$(this).find('.'+panConf.imageElClass)
										.css('height', imageHeight)
										.css('background', 'transparent url('+imageSrc+') top left repeat-x')
										.fadeIn('slow', function () {
											
											// remove loading class, add active class
												$(this).parents('.'+panConf.containerClass+':lt(1)')
													.removeClass(panConf.loadingClass)
													.addClass(panConf.activeClass)
													.panimate()
												;
											
										})
									;
							});
					})
					.attr('src', imageSrc)
		;
};

panReverse = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	prevDir = panEl.data('pandirection');
	
	panEl
		.data('pandirection', panConf.reverseDirection)
		.removeClass(panConf.forwardDirection+' '+panConf.pauseDirection)
		.addClass(panConf.reverseDirection)
	;
	// if it was paused, start it up
		if (prevDir == panConf.pauseDirection) {
			panEl.panimate();
		}
	
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlPauseDirClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlForwardDirClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
	
	return false;
};
panPause = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	panEl
		.data('pandirection', panConf.pauseDirection)
		.removeClass(panConf.forwardDirection+' '+panConf.reverseDirection)
		.addClass(panConf.pauseDirection)
	;
	
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlReverseDirClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlForwardDirClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
	
	return false;
};
panForward = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	prevDir = panEl.data('pandirection');
	
	panEl
		.data('pandirection', panConf.forwardDirection)
		.removeClass(panConf.reverseDirection+' '+panConf.pauseDirection)
		.addClass(panConf.forwardDirection)
	;
	
	// if it was paused, start it up
		if (prevDir == panConf.pauseDirection) {
			panEl.panimate();
		}
		
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlPauseDirClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlReverseDirClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
	
	return false;
};

panSlow = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	panEl
		.data('paninterval', panConf.intervals.slow)
		.data('panincrement', panConf.increments.slow)
		.removeClass(panConf.mediumSpeedClass+' '+panConf.fastSpeedClass)
		.addClass(panConf.slowSpeed)
	;
	
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlMediumSpeedClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlFastSpeedClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
	
	return false;
};
panMedium = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	panEl
		.data('paninterval', panConf.intervals.medium)
		.data('panincrement', panConf.increments.medium)
		.removeClass(panConf.slowSpeed+' '+panConf.fastSpeedClass)
		.addClass(panConf.mediumSpeedClass)
	;
	
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlSlowSpeedClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlFastSpeedClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
		
	return false;
};
panFast = function () {
	panEl = $(this).parents('.'+panConf.containerClass+':lt(1)');
	
	panEl
		.data('paninterval', panConf.intervals.fast)
		.data('panincrement', panConf.increments.fast)
		.removeClass(panConf.mediumSpeedClass+' '+panConf.slowSpeed)
		.addClass(panConf.fastSpeedClass)
	;
	
	// update ARIA
		panEl.find(
			 '.'+panConf.controlsClass+' .'+panConf.controlMediumSpeedClass+' a,'
			+'.'+panConf.controlsClass+' .'+panConf.controlSlowSpeedClass+' a'
		).ariaState('selected','false');
		$(this).ariaState('selected','true');
		
	return false;
};



$.fn.panimate = function () {
	
	// Opera will only display backgrounds offset by a certain number of pixels (~9999).
	// this shouldn't matter for our purposes, we will just reset once we reach the end of the image.
	// This should appear seemless, as we jump the position back the width of the entire image.
	// Seems to produce a flicker on Opera as it resets (damn Opera! can't please it).
	// see *Opera Fix*
	
	if ($(this).data('pandirection') == panConf.forwardDirection) {
		$(this).data(
			'panpos',
			$(this).data('panpos') + $(this).data('panincrement')
		);
		// *Opera Fix*
		if ($(this).data('panpos') > $(this).data('panwidth')) {
			$(this).data(
				'panpos',
				$(this).data('panpos') - $(this).data('panwidth')
			);
		}
		
	} else if ($(this).data('pandirection') == panConf.reverseDirection) {
		$(this).data(
			'panpos',
			$(this).data('panpos') - $(this).data('panincrement')
		);
		// *Opera Fix*
		if ($(this).data('panpos') < $(this).data('panwidth')) {
			$(this).data(
				'panpos',
				$(this).data('panpos') + $(this).data('panwidth')
			);
		}
	} else {
		// paused
		return;
	}
	
	$(this).find('.'+panConf.imageElClass).css(
		'background-position',
		$(this).data('panpos')+'px top'
	);
	
	$(this).oneTime(
		$(this).data('paninterval'),
		'pantimer',
		function(){
			$(this).panimate();
		}
	);
};


/**
 * document.doctype.entities
 * document.doctype.notation
 * document.doctype.publicId
 * document.doctype.systemId
 */
getDocType = function () {
	if (!document.doctype || !document.doctype.systemId) return 'html';
	if (document.doctype.systemId.indexOf('xhtml1') != -1) return 'xhtml1';
	if (document.doctype.systemId.indexOf('xhtml2') != -1) return 'xhtml2';
	return 'html';
};

})(jQuery); /* end closure */
