/*!
* jQuery.slideViewer
*
* Provides a single plug-in interface for the slide viewer used in the Slide and Pip Javascript Players
*
* Currently this is hooking into a heavily modified version the jQTouchPhotoGallery extension to the jQTouch plugin.
* http://code.google.com/p/jqextensions/wiki/JQTouchPhotoGallery
*
* This depends on both jQTouch https://github.com/senchalabs/jQTouch and jquery-mousewheel https://github.com/brandonaaron/jquery-mousewheel
* All of these scripts have been included in this single file and abstracted behind the plug-ins interface so we can swap out these dependencies
* with our own logic one day.
*
*/


/*

_/    _/_/    _/_/_/_/_/                              _/
_/    _/      _/      _/_/    _/    _/    _/_/_/  _/_/_/
_/  _/  _/_/      _/    _/    _/  _/    _/  _/        _/    _/
_/  _/    _/      _/    _/    _/  _/    _/  _/        _/    _/
_/    _/_/  _/    _/      _/_/      _/_/_/    _/_/_/  _/    _/
_/
_/

Created by David Kaneda <http://www.davidkaneda.com>
Documentation and issue tracking on GitHub <http://wiki.github.com/senchalabs/jQTouch/>

Special thanks to Jonathan Stark <http://jonathanstark.com/>
and pinch/zoom <http://www.pinchzoom.com/>

(c) 2010 by jQTouch project members.
See LICENSE.txt for license.

$Revision: 162 $
$Date: Thu Mar 17 17:32:36 EDT 2011 $
$LastChangedBy: jonathanstark $

*/

(function ($) {
	$.jQTouch = function (options) {

		// Initialize internal jQT variables
		var $body,
            $head = $('head'),
            initialPageId = '',
            hist = [],
            newPageCount = 0,
            jQTSettings = {},
            currentPage = '',
            orientation = 'portrait',
            tapReady = true,
            lastTime = 0,
            lastAnimationTime = 0,
            touchSelectors = [],
            publicObj = {},
            tapBuffer = 351,
            extensions = $.jQTouch.prototype.extensions,
            animations = [],
            hairExtensions = '',
            defaults = {
            	addGlossToIcon: true,
            	backSelector: '.back, .cancel, .goback',
            	cacheGetRequests: true,
            	debug: false,
            	fallback2dAnimation: 'fade',
            	fixedViewport: true,
            	formSelector: 'form',
            	fullScreen: true,
            	fullScreenClass: 'fullscreen',
            	hoverDelay: 50,
            	icon: null,
            	icon4: null, // experimental
            	moveThreshold: 10,
            	preloadImages: false,
            	pressDelay: 1000,
            	startupScreen: null,
            	statusBar: 'default', // other options: black-translucent, black
            	submitSelector: '.submit',
            	touchSelector: 'a, .touch',
            	useAnimations: true,
            	useFastTouch: true, // experimental
            	animations: [ // highest to lowest priority
                    {selector: '.cube', name: 'cubeleft', is3d: true },
                    { selector: '.cubeleft', name: 'cubeleft', is3d: true },
                    { selector: '.cuberight', name: 'cuberight', is3d: true },
                    { selector: '.dissolve', name: 'fade', is3d: false },
                    { selector: '.fade', name: 'fade', is3d: false },
                    { selector: '.flip', name: 'flipleft', is3d: true },
                    { selector: '.flipleft', name: 'flipleft', is3d: true },
                    { selector: '.flipright', name: 'flipright', is3d: true },
                    { selector: '.pop', name: 'pop', is3d: true },
                    { selector: '.slide', name: 'slideleft', is3d: false },
                    { selector: '.slidedown', name: 'slidedown', is3d: false },
                    { selector: '.slideleft', name: 'slideleft', is3d: false },
                    { selector: '.slideright', name: 'slideright', is3d: false },
                    { selector: '.slideup', name: 'slideup', is3d: false },
                    { selector: '.swap', name: 'swapleft', is3d: true },
                    { selector: '#jqt > * > ul li a', name: 'slideleft', is3d: false }
                ]
            };

		function _debug(message) {
			now = (new Date).getTime();
			delta = now - lastTime;
			lastTime = now;
			if (jQTSettings.debug) {
				if (message) {
					console.log(delta + ': ' + message);
				} else {
					console.log(delta + ': ' + 'Called ' + arguments.callee.caller.name);
				}
			}
		}
		function addAnimation(animation) {
			_debug();
			if (typeof (animation.selector) === 'string' && typeof (animation.name) === 'string') {
				animations.push(animation);
			}
		}
		function addPageToHistory(page, animation, reverse) {
			_debug();
			hist.unshift({
				page: page,
				animation: animation,
				hash: '#' + page.attr('id'),
				id: page.attr('id')
			});
		}
		function clickHandler(e) {
			_debug();

			if (!tapReady) {
				_debug('ClickHandler handler aborted because tap is not ready');
				e.preventDefault();
				return false;
			}

			// Figure out whether to prevent default
			var $el = $(e.target);

			// Find the nearest tappable ancestor
			if (!$el.is(touchSelectors.join(', '))) {
				var $el = $(e.target).closest(touchSelectors.join(', '));
			}

			// Prevent default if we found an internal link (relative or absolute)
			if ($el && $el.attr('href') && !$el.isExternalLink()) {
				// This heavy-handed code prevents the links in the links panel from working. -rc
				//_debug('Need to prevent default click behavior'); 
				//e.preventDefault();
			} else {
				_debug('No need to prevent default click behavior');
			}

			// Trigger a tap event if touchstart is not on the job
			if ($.support.touch) {
				_debug('Not converting click to a tap event because touch handler is on the job');
			} else {
				_debug('Converting click event to a tap event');
				$(e.target).trigger('tap', e);
			}

		}
		function doNavigation(fromPage, toPage, animation, backwards) {
			_debug();

			// Error check for target page
			if (toPage.length === 0) {
				$.fn.unselect();
				_debug('Target element is missing.');
				return false;
			}

			// Error check for fromPage===toPage
			if (toPage.hasClass('current')) {
				$.fn.unselect();
				_debug('You are already on the page you are trying to navigate to.');
				return false;
			}

			// Collapse the keyboard
			$(':focus').blur();

			// Position the incoming page so toolbar is at top of viewport regardless of scroll position on from page
			// toPage.css('top', window.pageYOffset);

			fromPage.trigger('pageAnimationStart', { direction: 'out' });
			toPage.trigger('pageAnimationStart', { direction: 'in' });

			if ($.support.animationEvents && animation && jQTSettings.useAnimations) {

				tapReady = false;

				// Fail over to 2d animation if need be
				if (!$.support.transform3d && animation.is3d) {
					animation.name = jQTSettings.fallback2dAnimation;
				}

				// Reverse animation if need be
				var finalAnimationName;
				if (backwards) {
					if (animation.name.indexOf('left') > 0) {
						finalAnimationName = animation.name.replace(/left/, 'right');
					} else if (animation.name.indexOf('right') > 0) {
						finalAnimationName = animation.name.replace(/right/, 'left');
					} else if (animation.name.indexOf('up') > 0) {
						finalAnimationName = animation.name.replace(/up/, 'down');
					} else if (animation.name.indexOf('down') > 0) {
						finalAnimationName = animation.name.replace(/down/, 'up');
					} else {
						finalAnimationName = animation.name;
					}
				} else {
					finalAnimationName = animation.name;
				}

				// _debug('finalAnimationName is ' + finalAnimationName);

				// Bind internal "cleanup" callback
				fromPage.bind('webkitAnimationEnd', navigationEndHandler);
				fromPage.bind('webkitTransitionEnd', navigationEndHandler);

				// Trigger animations
				scrollTo(0, 0);
				toPage.addClass(finalAnimationName + ' in current');
				fromPage.addClass(finalAnimationName + ' out');

			} else {
				toPage.addClass('current');
				navigationEndHandler();
			}

			// Define private navigationEnd callback
			function navigationEndHandler(event) {
				_debug();

				if ($.support.animationEvents && animation && jQTSettings.useAnimations) {
					fromPage.unbind('webkitAnimationEnd', navigationEndHandler);
					fromPage.unbind('webkitTransitionEnd', navigationEndHandler);
					fromPage.removeClass(finalAnimationName + ' out current');
					toPage.removeClass(finalAnimationName + ' in');
					// scrollTo(0, 0);
					// toPage.css('top', 0);
				} else {
					fromPage.removeClass(finalAnimationName + ' out current');
				}

				// Housekeeping
				currentPage = toPage;
				if (backwards) {
					hist.shift();
				} else {
					addPageToHistory(currentPage, animation);
				}

				fromPage.unselect();
				lastAnimationTime = (new Date()).getTime();
				setHash(currentPage.attr('id'));
				tapReady = true;

				// Trigger custom events
				toPage.trigger('pageAnimationEnd', { direction: 'in', animation: animation });
				fromPage.trigger('pageAnimationEnd', { direction: 'out', animation: animation });

			}

			// We's out
			return true;
		}
		function getOrientation() {
			_debug();
			return orientation;
		}
		function goBack() {
			_debug();

			// Error checking
			if (hist.length < 1) {
				_debug('History is empty.');
			}

			if (hist.length === 1) {
				_debug('You are on the first panel.');
			}

			var from = hist[0], to = hist[1];

			if (doNavigation(from.page, to.page, from.animation, true)) {
				return publicObj;
			} else {
				_debug('Could not go back.');
				return false;
			}

		}
		function goTo(toPage, animation, reverse) {
			_debug();

			if (reverse) {
				//console.warn('The reverse parameter was sent to goTo() function, which is bad.');
			}

			var fromPage = hist[0].page;

			if (typeof animation === 'string') {
				for (var i = 0, max = animations.length; i < max; i++) {
					if (animations[i].name === animation) {
						animation = animations[i];
						break;
					}
				}
			}

			if (typeof (toPage) === 'string') {
				var nextPage = $(toPage);
				if (nextPage.length < 1) {
					showPageByHref(toPage, {
						'animation': animation
					});
					return;
				} else {
					toPage = nextPage;
				}

			}
			if (doNavigation(fromPage, toPage, animation, reverse)) {
				return publicObj;
			} else {
				_debug('Could not animate pages.');
				return false;
			}
		}
		function hashChangeHandler(e) {
			_debug();
			if (hist[1] === undefined) {
				_debug('There is no previous page in history');
			} else {
				if (location.hash === hist[1].hash) {
					goBack();
				} else {
					_debug(location.hash + ' !== ' + hist[1].hash);
				}
			}
		}
		function init(options) {
			_debug();
			jQTSettings = $.extend({}, defaults, options);

			// Preload images
			if (jQTSettings.preloadImages) {
				for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--) {
					(new Image()).src = jQTSettings.preloadImages[i];
				};
			}

			// Set appropriate icon (retina display stuff is experimental)
			if (jQTSettings.icon || jQTSettings.icon4) {
				var precomposed, appropriateIcon;
				if (jQTSettings.icon4 && window.devicePixelRatio && window.devicePixelRatio === 2) {
					appropriateIcon = jQTSettings.icon4;
				} else if (jQTSettings.icon) {
					appropriateIcon = jQTSettings.icon;
				} else {
					appropriateIcon = false;
				}
				if (appropriateIcon) {
					precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed';
					hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" href="' + appropriateIcon + '" />';
				}
			}

			// Set startup screen
			if (jQTSettings.startupScreen) {
				hairExtensions += '<link rel="apple-touch-startup-image" href="' + jQTSettings.startupScreen + '" />';
			}

			// Set viewport
			if (jQTSettings.fixedViewport) {
				hairExtensions += '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"/>';
			}

			// Set full-screen
			if (jQTSettings.fullScreen) {
				hairExtensions += '<meta name="apple-mobile-web-app-capable" content="yes" />';
				if (jQTSettings.statusBar) {
					hairExtensions += '<meta name="apple-mobile-web-app-status-bar-style" content="' + jQTSettings.statusBar + '" />';
				}
			}

			// Attach hair extensions
			if (hairExtensions) {
				$head.prepend(hairExtensions);
			}

		}
		function insertPages(nodes, animation) {
			_debug();

			var targetPage = null;
			$(nodes).each(function (index, node) {
				var $node = $(this);
				if (!$node.attr('id')) {
					$node.attr('id', 'page-' + (++newPageCount));
				}

				// Remove any existing instance
				$('#' + $node.attr('id')).remove();

				$body.trigger('pageInserted', { page: $node.appendTo($body) });

				if ($node.hasClass('current') || !targetPage) {
					targetPage = $node;
				}
			});
			if (targetPage !== null) {
				goTo(targetPage, animation);
				return targetPage;
			} else {
				return false;
			}
		}
		function mousedownHandler(e) {
			var timeDiff = (new Date()).getTime() - lastAnimationTime;
			if (timeDiff < tapBuffer) {
				return false;
			}
		}
		function orientationChangeHandler() {
			_debug();

			orientation = Math.abs(window.orientation) == 90 ? 'landscape' : 'portrait';
			$body.removeClass('portrait landscape').addClass(orientation).trigger('turn', { orientation: orientation });
		}
		function setHash(hash) {
			_debug('setHash is off at the moment');
			return;

			// Trim leading # if need be
			hash = hash.replace(/^#/, ''),

			// Remove listener
            window.onhashchange = null;

			// Change hash
			location.hash = '#' + hash;

			// Add listener
			window.onhashchange = hashChangeHandler;

		}
		function showPageByHref(href, options) {
			_debug();

			var defaults = {
				data: null,
				method: 'GET',
				animation: null,
				callback: null,
				$referrer: null
			};

			var settings = $.extend({}, defaults, options);

			if (href != '#') {
				$.ajax({
					url: href,
					data: settings.data,
					type: settings.method,
					success: function (data, textStatus) {
						var firstPage = insertPages(data, settings.animation);
						if (firstPage) {
							if (settings.method == 'GET' && jQTSettings.cacheGetRequests === true && settings.$referrer) {
								settings.$referrer.attr('href', '#' + firstPage.attr('id'));
							}
							if (settings.callback) {
								settings.callback(true);
							}
						}
					},
					error: function (data) {
						if (settings.$referrer) {
							settings.$referrer.unselect();
						}
						if (settings.callback) {
							settings.callback(false);
						}
					}
				});
			} else if (settings.$referrer) {
				settings.$referrer.unselect();
			}
		}
		function submitHandler(e, callback) {
			_debug();

			$(':focus').blur();

			e.preventDefault();

			var $form = (typeof (e) === 'string') ? $(e).eq(0) : (e.target ? $(e.target) : $(e));

			_debug($form.attr('action'));

			if ($form.length && $form.is(jQTSettings.formSelector) && $form.attr('action')) {
				showPageByHref($form.attr('action'), {
					data: $form.serialize(),
					method: $form.attr('method') || "POST",
					animation: animations[0] || null,
					callback: callback
				});
				return false;
			}
			return false;
		}
		function submitParentForm($el) {
			_debug();

			var $form = $el.closest('form');
			if ($form.length === 0) {
				_debug('No parent form found');
			} else {
				_debug('About to submit parent form');
				var evt = $.Event('submit');
				evt.preventDefault();
				$form.trigger(evt);
				return false;
			}
			return true;
		}
		function supportForAnimationEvents() {
			_debug();

			return (typeof WebKitAnimationEvent != 'undefined');
		}
		function supportForCssMatrix() {
			_debug();

			return (typeof WebKitCSSMatrix != 'undefined');
		}
		function supportForTouchEvents() {
			_debug();

			// If dev wants fast touch off, shut off touch whether device supports it or not
			if (!jQTSettings.useFastTouch) {
				return false
			}

			// Dev must want touch, so check for support
			if (typeof TouchEvent != 'undefined') {
				if (window.navigator.userAgent.indexOf('Mobile') > -1) { // Grrrr...
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		};
		function supportForTransform3d() {
			_debug();

			var head, body, style, div, result;

			head = document.getElementsByTagName('head')[0];
			body = document.body;

			style = document.createElement('style');
			style.textContent = '@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){#jqtTestFor3dSupport{height:3px}}';

			div = document.createElement('div');
			div.id = 'jqtTestFor3dSupport';

			// Add to the page
			head.appendChild(style);
			body.appendChild(div);

			// Check the result
			result = div.offsetHeight === 3;

			// Clean up
			style.parentNode.removeChild(style);
			div.parentNode.removeChild(div);

			// Pass back result
			// _debug('Support for 3d transforms: ' + result);
			return result;
		};
		function tapHandler(e) {
			_debug();

			if (!tapReady) {
				_debug('Tap is not ready');
				return false;
			}

			// Grab the target element
			var $el = $(e.target);

			// Find the nearest tappable ancestor
			if (!$el.is(touchSelectors.join(', '))) {
				var $el = $(e.target).closest(touchSelectors.join(', '));
			}

			// Make sure we have a tappable element
			if (!$el.length || !$el.attr('href')) {
				_debug('Could not find a link related to tapped element');
				return false;
			}

			// Init some vars
			var target = $el.attr('target'),
                hash = $el.attr('hash'),
                animation = null;

			if ($el.isExternalLink()) {
				$el.unselect();
				return true;

			} else if ($el.is(jQTSettings.backSelector)) {
				// User clicked or tapped a back button
				goBack(hash);

			} else if ($el.is(jQTSettings.submitSelector)) {
				// User clicked or tapped a submit element
				submitParentForm($el);

			} else if (target === '_webapp') {
				// User clicked or tapped an internal link, fullscreen mode
				window.location = $el.attr('href');
				return false;

			} else if ($el.attr('href') === '#') {
				// Allow tap on item with no href
				$el.unselect();
				return true;

			} else {

				// Figure out the animation to use
				for (var i = 0, max = animations.length; i < max; i++) {
					if ($el.is(animations[i].selector)) {
						animation = animations[i];
						break;
					}
				};

				if (!animation) {
					//console.warn('Animation could not be found. Using slideleft.');
					animation = 'slideleft';
				}

				if (hash && hash !== '#') {
					// Internal href
					$el.addClass('active');
					goTo($(hash).data('referrer', $el), animation, $(this).hasClass('reverse'));
					return false;

				} else {
					// External href
					$el.addClass('loading active');
					showPageByHref($el.attr('href'), {
						animation: animation,
						callback: function () {
							$el.removeClass('loading');
							setTimeout($.fn.unselect, 250, $el);
						},
						$referrer: $el
					});
					return false;
				}
			}
		}
		function touchStartHandler(e) {
			_debug();

			if (!tapReady) {
				_debug('TouchStart handler aborted because tap is not ready');
				e.preventDefault();
				return false;
			}

			var $el = $(e.target);

			// Error check
			if (!$el.length) {
				_debug('Could not find target of touchstart event.');
				return;
			}

			var startTime = (new Date).getTime(),
                hoverTimeout = null,
                pressTimeout = null,
                touch,
                startX,
                startY,
                deltaX = 0,
                deltaY = 0,
                deltaT = 0;

			if (event.changedTouches && event.changedTouches.length) {
				touch = event.changedTouches[0];
				startX = touch.pageX;
				startY = touch.pageY;
			}

			// Prep the element
			$el.bind('touchmove', touchMoveHandler).bind('touchend', touchEndHandler).bind('touchcancel', touchCancelHandler);

			hoverTimeout = setTimeout(function () {
				$el.makeActive();
			}, jQTSettings.hoverDelay);

			pressTimeout = setTimeout(function () {
				$el.unbind('touchmove', touchMoveHandler).unbind('touchend', touchEndHandler).unbind('touchcancel', touchCancelHandler);
				$el.unselect();
				clearTimeout(hoverTimeout);
				$el.trigger('press');
			}, jQTSettings.pressDelay);

			// Private touch functions
			function touchCancelHandler(e) {
				_debug();
				clearTimeout(hoverTimeout);
				$el.unselect();
				$el.unbind('touchmove', touchMoveHandler).unbind('touchend', touchEndHandler).unbind('touchcancel', touchCancelHandler);
			}

			function touchEndHandler(e) {
				_debug();
				// updateChanges();
				$el.unbind('touchend', touchEndHandler).unbind('touchcancel', touchCancelHandler);
				clearTimeout(hoverTimeout);
				clearTimeout(pressTimeout);
				if (Math.abs(deltaX) < jQTSettings.moveThreshold && Math.abs(deltaY) < jQTSettings.moveThreshold && deltaT < jQTSettings.pressDelay) {
					// e.preventDefault();
					// e.stopImmediatePropagation();
					$el.trigger('tap', e);
				} else {
					$el.unselect();
				}
			}

			function touchMoveHandler(e) {
				// _debug();
				updateChanges();
				var absX = Math.abs(deltaX);
				var absY = Math.abs(deltaY);
				var direction;
				if (absX > absY && (absX > 35) && deltaT < 1000) {
					if (deltaX < 0) {
						direction = 'left';
					} else {
						direction = 'right';
					}
					$el.unbind('touchmove', touchMoveHandler).unbind('touchend', touchEndHandler).unbind('touchcancel', touchCancelHandler);
					$el.trigger('swipe', { direction: direction, deltaX: deltaX, deltaY: deltaY });
				}
				$el.unselect();
				clearTimeout(hoverTimeout);
				if (absX > jQTSettings.moveThreshold || absY > jQTSettings.moveThreshold) {
					clearTimeout(pressTimeout);
				}
			}

			function updateChanges() {
				// _debug();
				var firstFinger = event.changedTouches[0] || null;
				deltaX = firstFinger.pageX - startX;
				deltaY = firstFinger.pageY - startY;
				deltaT = (new Date).getTime() - startTime;
				// _debug('deltaX:'+deltaX+';deltaY:'+deltaY+';');
			}

		} // End touch handler

		// Get the party started
		init(options);

		// Document ready stuff
		$(document).ready(function () {

			// Store some properties in the jQuery support object
			$.support.animationEvents = supportForAnimationEvents();
			$.support.cssMatrix = supportForCssMatrix();
			$.support.touch = supportForTouchEvents();
			$.support.transform3d = supportForTransform3d();

			if (!$.support.touch) {
				//console.warn('This device does not support touch interaction, or it has been deactivated by the developer. Some features might be unavailable.');
			}
			if (!$.support.transform3d) {
				//console.warn('This device does not support 3d animation. 2d animations will be used instead.');
			}

			// Define public jQuery functions
			$.fn.isExternalLink = function () {
				var $el = $(this);
				return ($el.attr('target') == '_blank' || $el.attr('rel') == 'external' || $el.is('a[href^="http://maps.google.com"], a[href^="mailto:"], a[href^="tel:"], a[href^="javascript:"], a[href*="youtube.com/v"], a[href*="youtube.com/watch"]'));
			}
			$.fn.makeActive = function () {
				return $(this).addClass('active');
			}
			$.fn.press = function (fn) {
				if ($.isFunction(fn)) {
					return $(this).live('press', fn);
				} else {
					return $(this).trigger('press');
				}
			}
			$.fn.swipe = function (fn) {
				if ($.isFunction(fn)) {
					return $(this).live('swipe', fn);
				} else {
					return $(this).trigger('swipe');
				}
			}
			$.fn.tap = function (fn) {
				if ($.isFunction(fn)) {
					return $(this).live('tap', fn);
				} else {
					return $(this).trigger('tap');
				}
			}
			$.fn.unselect = function (obj) {
				if (obj) {
					obj.removeClass('active');
				} else {
					$('.active').removeClass('active');
				}
			}

			// Add extensions
			for (var i = 0, max = extensions.length; i < max; i++) {
				var fn = extensions[i];
				if ($.isFunction(fn)) {
					$.extend(publicObj, fn(publicObj));
				}
			}

			// Set up animations array
			if (jQTSettings['cubeSelector']) {
				//console.warn('NOTE: cubeSelector has been deprecated. Please use cubeleftSelector instead.');
				jQTSettings['cubeleftSelector'] = jQTSettings['cubeSelector'];
			}
			if (jQTSettings['flipSelector']) {
				//console.warn('NOTE: flipSelector has been deprecated. Please use flipleftSelector instead.');
				jQTSettings['flipleftSelector'] = jQTSettings['flipSelector'];
			}
			if (jQTSettings['slideSelector']) {
				//console.warn('NOTE: slideSelector has been deprecated. Please use slideleftSelector instead.');
				jQTSettings['slideleftSelector'] = jQTSettings['slideSelector'];
			}
			for (var i = 0, max = defaults.animations.length; i < max; i++) {
				var animation = defaults.animations[i];
				if (jQTSettings[animation.name + 'Selector'] !== undefined) {
					animation.selector = jQTSettings[animation.name + 'Selector'];
				}
				addAnimation(animation);
			}

			// Create an array of stuff that needs touch event handling
			touchSelectors.push('input'); // TODO: Ask DK why inputs are considered touch selectors
			touchSelectors.push(jQTSettings.touchSelector);
			touchSelectors.push(jQTSettings.backSelector);
			touchSelectors.push(jQTSettings.submitSelector);
			$(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none');

			// Make sure we have a jqt element
			$body = $('#jqt');
			if ($body.length === 0) {
				//console.warn('Could not find an element with the id "jqt", so the body id has been set to "jqt". If you are having any problems, wrapping your panels in a div with the id "jqt" might help.');
				$body = $('body').attr('id', 'jqt');
			}

			// Add some specific css if need be
			if ($.support.transform3d) {
				$body.addClass('supports3d');
			}
			if (jQTSettings.fullScreenClass && window.navigator.standalone == true) {
				$body.addClass(jQTSettings.fullScreenClass + ' ' + jQTSettings.statusBar);
			}
			if (window.navigator.userAgent.match(/Android/ig)) { // Grr... added to postion checkbox labels. Lame I know. - js
				$body.addClass('android');
			}

			// Bind events
			$body.bind('touchstart', touchStartHandler)
                .bind('click', clickHandler)
                .bind('mousedown', mousedownHandler)
                .bind('orientationchange', orientationChangeHandler)
                .bind('submit', submitHandler)
                .bind('tap', tapHandler)
                .trigger('orientationchange');

			// Determine what the "current" (initial) panel should be
			if ($('#jqt > .current').length == 0) {
				currentPage = $('#jqt > *:first');
			} else {
				currentPage = $('#jqt > .current:first');
				$('#jqt > .current').removeClass('current');
			}

			// Go to the top of the "current" page
			$(currentPage).addClass('current');
			initialPageId = $(currentPage).attr('id');
			setHash(initialPageId);
			addPageToHistory(currentPage);
			scrollTo(0, 0);

			// Make sure none of the panels yank the location bar into view
			//$('#jqt > *').css('minHeight', window.innerHeight);

		});

		// Expose public methods and properties
		publicObj = {
			animations: animations,
			hist: hist,
			settings: jQTSettings,
			support: $.support,
			getOrientation: getOrientation,
			goBack: goBack,
			goTo: goTo,
			addAnimation: addAnimation,
			submitForm: submitHandler
		}
		return publicObj;
	}

	// Extensions directly manipulate the jQTouch object, before it's initialized.
	$.jQTouch.prototype.extensions = [];
	$.jQTouch.addExtension = function (extension) {
		$.jQTouch.prototype.extensions.push(extension);
	}

})(jQuery);


/*!*
 *    Provides event handling for iPhone like photo gallery (without thumbnail portion)
 *
 *    @author Sam Shull <http://www.google.com/profiles/brickysam26>
 *    @copyright 2010 Sam Shull <http://samshull.blogspot.com/>
 *
 *    Special Thanks to Steve Simitzis <http://saturn5.com>
 *
 *    @license <http://www.opensource.org/licenses/mit-license.html>
 *
 *    Permission is hereby granted, free of charge, to any person obtaining a copy
 *    of this software and associated documentation files (the "Software"), to deal
 *    in the Software without restriction, including without limitation the rights
 *    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the Software is
 *    furnished to do so, subject to the following conditions:
 *    
 *    The above copyright notice and this permission notice shall be included in
 *    all copies or substantial portions of the Software.
 *    
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 *
 *	  Heavily modified for use as a Mediasite Slide Viewer
 *
 *	  Requires jquery.mousewheel.js for mouse wheel zoom zupport (https://github.com/brandonaaron/jquery-mousewheel)
 *
 */

(function($){
    //if (!"WebKitCSSMatrix" in this) {
    //    return null;
    //}
	if (!(Modernizr.csstransforms || Modernizr.csstransforms3d)) {
		return null;
	}

	var galleries = [],
        supportsTouch = "Touch" in this,
        events = supportsTouch ? {
            start: "touchstart",
            move: "touchmove",
            end: "touchend"
        } : {
            start: "mousedown",
            move: "mousemove",
            end: "mouseup"
        },
        controlPoints,
        parseRuleSet = false,
        window = this,
        document = window.document,
        orientation = Math.abs(window.orientation) == 90 ? "landscape" : "portrait",
        defaults = {
            data: [],
            defaultIndex: 0,
            transitionProperty: getTransformProperty(),
            timingFunction: "cubic-bezier(0,0,.25,1)",
            transform: Modernizr.csstransforms3d ? "translate3d({0}px,0,0)" : "translate({0}px,0)",
            imageTransform: "scale({0}) translate3d({1}px, {2}px, 0)",
            maxSlidesBefore: 2,
            maxSlidesAfter: 2,
            slideDelay: 5000,
            scrollSpeed: 500,
            scaleSpeed: 500,
            appendTo: "#jqt",
            dataAttribute: "data-index",
            tagNames: "A,INPUT,SELECT,TEXTAREA",
            blankImage: '<img class="jqt-slide-img"/>',
            slideTemplate: '<td class="jqt-slide-cell">\
                                <div class="jqt-slide-not-loaded">\
                                    <img class="jqt-slide-img"/>\
                                </div>\
                            </td>',
            parsedClassTemplate: "#jqt .jqt-slide .jqt-slide-cell > .{0}{max-width:auto;max-height:auto;}",
            galleryTemplate: '<div class="jqt-slide">\
                                    <table class="jqt-slide-table">\
                                        <tr class="jqt-slide-slide-list"><\/tr>\
                                    <\/table>\
                                <\/div>',
            slideTo: slideTo,
            goTo: goTo,
			mouseWheel: mouseWheel,
            touchStart: touchStart,
            dragStart: dragStart,
            drag: drag,
            dragEnd: dragEnd,
            scaleStart: scaleStart,
            scale: scale,
            scaleEnd: scaleEnd,
            loader: loader,
            createSlide: createSlide,
            rearrange: rearrange,
            presizedClass: "jqt-slide-presized",
            parsedClass: "jqt-slide-parsed",
            currentClass: "jqt-slide-current",
            notLoadedClass: "jqt-slide-not-loaded",
            tableSelector: ".jqt-slide-table",
            slideSelector: ".jqt-slide-cell",
            imageSelector: "img.jqt-slide-img",
            listSelector: ".jqt-slide-slide-list",
            gallerySelector: ".jqt-slide"
        },
		pipHeight = null,
		pipWidth = null,
        height = function() { return (pipHeight === null) ? window.innerHeight + "px" : pipHeight + "px"; },
        width = function() { return (pipWidth === null) ? window.innerWidth + "px" : pipWidth + "px"; },
        cssRules = {
            defaults: {
                "#jqt .jqt-slide .jqt-slide-cell > div" : {
                    width: width,
                    height: height,
                    "line-height": height
                },
				"#jqt .jqt-slide img" : {
					"-webkit-transform": "translate3d(0,0,0)",
					"-moz-transform": "translate3d(0,0,0)",
					"-o-transform": "translate3d(0,0,0)",
					"transform": "translate3d(0,0,0)"
				}
            },
            portrait: {
                "#jqt.portrait .jqt-slide" : {
                    height: height,
                    width: width
                },
                "#jqt.portrait .jqt-slide .jqt-slide-cell > div" : {
                    width: width,
                    height: height,
                    "line-height": height
                }
            },
            landscape: {
                "#jqt.landscape .jqt-slide" : {
                    height: height,
                    width: width
                },
                "#jqt.landscape .jqt-slide .jqt-slide-table" : {
                    height: height
                },
                "#jqt.landscape .jqt-slide .jqt-slide-cell > div" : {
                    height: height,
                    width: width,
                    "line-height": height
                }
            }
        };
    
    if ($.jQTouch) {
        $.jQTouch.addExtension(function(jqt) {   
            return {
                generateSlides: generateSlides,
				goToSlide: goToSlide,
				addSlide: addSlide,
				updateCSS: updateCSS
            };
        });
    }

	function getTransformProperty() {
		var baseStyle = document.body ? document.body.style : {};
		
		if (typeof(baseStyle.transform) !== 'undefined') {
			return "transform";
		} else if (typeof(baseStyle.webkitTransform) !== 'undefined') {
			return "-webkit-transform";
		} else if (typeof(baseStyle.MozTransform) !== 'undefined') {
			return "-moz-transform";
		} else if (typeof(baseStyle.OTransform) !== 'undefined') {
			return "-o-transform";
		} else if (typeof(baseStyle.msTransfrom) !== 'undefined') {
			return "-ms-transform";
		} else {
			return undefined;
		}
	}

	function getTransitionEndEvent() {
		if ($.browser.webkit) {
			return "webkitTransitionEnd";
		} else if ($.browser.mozilla) {
			return "transitionend";
		} else if ($.browser.opera) {
			return "oTransitionEnd";
		}

		return "TransitionEnd";
	}

	function getVendorDOMProperty(property) {
		var props = ["webkit" + capitalize(property), "Moz" + capitalize(property), "O" + capitalize(property), "ms" + capitalize(property)];
		var root = document.body;

		for (var i = 0; i < props.length; i++) {
			if (typeof(root.style[props[i]]) !== 'undefined') {
				return props[i];
			}
		}

		// return back the original property if no prefixed ones are supported
		return property;

		function capitalize(string) {
			return string.charAt(0).toUpperCase() + string.slice(1);
		}
	}

	function addSlide(gallery, image) {
		var g = $(gallery),
			options = g.data('jqt-slide-options'),
			slides = g.find(options.slideSelector),
			list = options.list,
			i = slides.length,
			currentIndex = parseInt($('.jqt-slide-current', g).attr('data-index'), 10),
			lower = currentIndex - options.maxSlidesBefore,
            upper = currentIndex + options.maxSlidesAfter;

		options.data.push(image);
		list.append(options.createSlide(image, i, options, i >= lower && i <= upper));

		return g;
	}

	function goToSlide(gallery, index, duration) {
		var g = $(gallery),
			options = g.data('jqt-slide-options'),
			slides = g.find(options.slideSelector);

		if (duration) {
			g.triggerHandler("jqt-slide-slideto", [index, duration, options, slides]);
		} else {
			g.triggerHandler("jqt-slide-goto", [index, duration, options, slides]);
		}

		return g;
	}

    function generateSlides (id, images, options) {
        options = $.extend({}, defaults, options);
        options.data = images;
        
        if (!parseRuleSet) {
            parseRuleSet = true;
            var sheet = document.styleSheets[document.styleSheets.length - 1];
			sheet.insertRule(
                format(options.parsedClassTemplate, options.parsedClass), 
                sheet.cssRules.length
            );
        }
        
        options.gallery = $(options.galleryTemplate).appendTo(options.appendTo);
        
        var list = options.list = options.gallery.find(options.listSelector),
            lower = options.defaultIndex - options.maxSlidesBefore,
            upper = options.defaultIndex + options.maxSlidesAfter;
        
        $.each(images, function(i, data) {
            list.append(options.createSlide(data, i, options, i >= lower && i <= upper));
        });
        
        return jqtPhoto(options.gallery[0], options);
    }
    
    $.fn.jqtPhoto = function(options) {
        options = $.extend({}, defaults, options || {});
        return this.each(function(){jqtPhoto(this, options)});
    };
    
    $.jqtPhoto = {
        generateSlides: generateSlides,
        defaults: function(options) {
            if (options) {
                defaults = $.extend(defaults, options);
            }
            
            return $.extend({}, defaults);
        },
        defaultCSS: function(options) {
            if (options !== undefined) {
                cssRules = $.extend(true, cssRules, options);
            }
            
            return $.extend({}, cssRules);
        }
    };


    function jqtPhoto (element, options) {
        var $elem = $(element),
            slides = attachEvents($elem, options).find(options.slideSelector),
            images = slides.find(options.imageSelector);
        
        options.list = $elem.find(options.listSelector);
        options.table = tableData($elem.data("jqt-slide-options", options), options);
        options.blankImage = parseImageData($elem, $(options.blankImage).load(options.loader), options);
        
        if (!slides.filter("." + options.currentClass).length) {
            if (!slides.filter(format("[{0}={1}]", options.dataAttribute, options.defaultIndex)).addClass(options.currentClass).length) {
                slides.eq(0).addClass(options.currentClass);
            }
        }

		/*
        $elem.find(options.tableSelector).css({
            webkitTransitionProperty: options.transitionProperty,
            webkitTransitionTimingFunction: options.timingFunction,
            webkitTransitionDuration: options.defaultDuration + "s",
            webkitTransform: format(options.transform, -slides.filter("."+options.currentClass).attr("offsetLeft") || 0)
        });
		*/

		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionProperty")] = options.transitionProperty;
		transitionCSS[getVendorDOMProperty("transitionTimingFunction")] = options.timingFunction;
		transitionCSS[getVendorDOMProperty("transitionDuration")] = options.defaultDuration + "s";
		transitionCSS[getVendorDOMProperty("transform")] = format(options.transform, -slides.filter("."+options.currentClass).attr("offsetLeft") || 0);
		$elem.find(options.tableSelector).css(transitionCSS);
        
        parseImageData($elem, images, options);
        galleries = galleries.concat($.makeArray($elem));
        return $elem;
    }
    
    function tableData (target, options) {
        var table = target.find(options.tableSelector),
            //transform = window.getComputedStyle(table[0]).webkitTransform,
            //matrix = new WebKitCSSMatrix(transform.replace('none',''));
			matrix = { m41: 0};
        
        return table.data("jqt-slide-position", {x: Number(matrix.m41 || 0), gallery: target});
    }
    
    function attachEvents (target, options) {
        return target
                .each(function() {
					this.addEventListener(events.start, options.touchStart, false);
				})
                .bind("jqt-slide-slideto", options.slideTo)
                .bind("jqt-slide-goto", options.goTo)
				.bind("mousewheel", options.mouseWheel);
    }
    
    function parseImageData (target, images, options) {
        return images.each(function() {
            var t = $(this).addClass(options.parsedClass),
                //matrix = new WebKitCSSMatrix(window.getComputedStyle(this).webkitTransform.replace('none','')),
                //scale = Number(matrix.m11),
				scale = 1,
                data = t.data("jqt-slide-info") || {}, 
                c = {
                    scale: scale,
                    //top: Number(matrix.m42),
                    //left: Number(matrix.m41),
					top: 0,
					left: 0,
                    width: t.width() * scale,
                    height: t.height() * scale,
                    parent: t.parent()
                };

			adjustSize(t);

            data.galleryOptions = options;
            data.gallery = data.gallery || target;
            data[orientation] = {current: c, original: $.extend({}, c)};
            t.data("jqt-slide-info", data);
        });
    }

	function adjustImageSize(images) {
		return images.each(function() {
			adjustSize($(this));
		});
	}

	function adjustSize(image) {
		// make sure the images fill the whole area
		var parent = $(window);
		if (parent.height() > parent.width()) {
			image.css('width', '100%').css('height', '');
		} else {
			image.css('width', '').css('height', '100%');
		}
	}

    function touchStart (event) {
        var target = $(event.target.nodeType == 3 ? event.target.parentNode : event.target), 
            $this = $(this),
            options = $this.data("jqt-slide-options"),
            tt = supportsTouch ? event.targetTouches : {length: !event.ctrlKey ? 1 : 2};
        
        if (target.is(options.tagNames)) {
            return null;
        }
        
		if (tt.length == 2 && target.closest(options.imageSelector).length && options.scaleStart) {
            return options.scaleStart.call(this, event);
            
        } else if (tt.length == 1 && options.dragStart) {
            return options.dragStart.call(this, event);
        }
        
        return true;
    }
        
    function dragStart (event) {
        event.preventDefault();
        //window.scrollTo(0,0);
        
        var $this = $(this),
            options = $this.data("jqt-slide-options"),
            //table = $this.find(options.tableSelector).css("webkitTransitionDuration", "0s"),
			table = $this.find(options.tableSelector).css(getVendorDOMProperty("transitionDuration"), "0s"),
            position = table.data("jqt-slide-position"),
            target = $(event.target).closest(options.imageSelector),
            data = target.data("jqt-slide-info") || false,
            offset = !!data && data[orientation] || false,
            current = !!offset && offset.current || {},
            tt = supportsTouch ? event.targetTouches[0] : event;
        
        this.addEventListener(events.move, options.drag, false);
        this.addEventListener(events.end, options.dragEnd, false);
        
        $this.data("jqt-slide-event", {
                table: table,
                position: {current: position.x, original: position.x},
                //target: target.css("webkitTransitionDuration", "0s"),
				target: target.css(getVendorDOMProperty("transitionDuration"), "0s"),
                options: options,
                slides: table.find(options.slideSelector),
                moved: false,
                x: tt.pageX,
                y: tt.pageY,
                left: current.left || 0,
                top: current.top || 0,
                timeStamp: +new Date
            });
            
        return true;
    }
    
    function drag (event) {
        event.preventDefault();
        
        var $this = $(this),
            original = $this.data("jqt-slide-event"),
            table = original.table,
            position = original.position,
            target = original.target,
            info = target.data("jqt-slide-info"),
            data = !!info && info[orientation] || false,
            current = data.current || false,
            tt = supportsTouch ? event.targetTouches[0] : event,
            distanceX = original.x - tt.pageX,
            distanceY = original.y - tt.pageY,
            w, h, x, y, l, t, d, 
            s = current.scale;
        
        original.moved = true;
        
        if (data) {
            if (distanceX) {
                x = target.width() * s;
                w = current.parent.width();
                l = original.left;
                
                if (x > w) {
                    l -= distanceX / s;
                    distanceX = 0;
                    d = ((x - w) / s) / 2;
                    
                    if (l > d) {
                        distanceX = d - l;
                        l = d;
                    } else if (l < -d) {
                        distanceX = -(l + d);
                        l = -d;
                    }
                }
                
                current.left = Math.floor(l);
            }
            
            if (distanceY) {
                y = target.height() * s;
                h = current.parent.height(),
                t = original.top;
                
                if (y > h) {
                    t -= distanceY / s;
                }
                
                current.top = Math.floor(t);
            }
            
			/*
            target.css({
                webkitTransitionDuration: "0s",
                webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
            });
			*/

			var transitionCSS = {};
			transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
			transitionCSS[getVendorDOMProperty("transform")] = format(original.options.imageTransform, s, current.left, current.top);
			target.css(transitionCSS);
        }
        
        position.current = position.original - distanceX;
        
		/*
		table.css({
                webkitTransitionDuration: "0s",
                webkitTransform: format(original.options.transform, position.current)
        });
		*/

		var tableTransitionCSS = {};
		tableTransitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
		tableTransitionCSS[getVendorDOMProperty("transform")] = format(original.options.transform, position.current);
		table.css(tableTransitionCSS);
        
        return true;
    }
    
    function dragEnd (event) {
        var $this = $(this),
            original = $this.data("jqt-slide-event"),
            table = original.table,
            tablePosition = table.data("jqt-slide-position"),
            position = original.position,
            target = original.target,
            info = target.data("jqt-slide-info"),
            data = !!info && info[orientation] || false,
            current = data.current || false,
            slides = original.slides,
            h, x, y, t, d, n, w,
            s = current.scale;
        
        this.removeEventListener(events.move, original.options.drag, false);
        this.removeEventListener(events.end, original.options.dragEnd, false);
        
        if (!original.moved) {
            return dispatchClick(event.target);
        }
        
        event.preventDefault();
        
        if (data) {
            y = target.height() * s;
            h = current.parent.height(),
            t = current.top;
            
            if (y > h) {
                d = ((y - h) / s) / 2;
                
                if (t > d) {
                    t = d;
                } else if (t < -d) {
                    t = -d;
                }
            }
            
            current.top = Math.floor(t);
            
			/*
			target.css({
                webkitTransitionDuration: original.options.scaleSpeed + "ms",
                webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
            });
			*/

			var transitionCSS = {};
			transitionCSS[getVendorDOMProperty("transitionDuration")] =  original.options.scaleSpeed + "ms";
			transitionCSS[getVendorDOMProperty("transform")] = format(original.options.imageTransform, s, current.left, current.top);
			target.css(transitionCSS);
        }
        
        d = (+new Date() - original.timeStamp);

        if (position.current > 0) {
            tablePosition.x = 0;
            
        } else if (position.current < (slides.eq(slides.length-1).width() - table.outerWidth())) {
            tablePosition.x = (slides.eq(slides.length-1).width() - table.outerWidth());
        } else {
            n = tablePosition.x < position.current ? -1 : 1;
            x = slides.filter("."+original.options.currentClass);
            t = Math.abs(x.attr("offsetLeft") + position.current);
            w = x.width() / 3;
            
            if (t > w) {
                $this.triggerHandler("jqt-slide-slideto", [slides.index(x[0]) + n, (d > 1000 ? 1000 : d), original.options, slides]);
                return true;
            }
        }

		/*
        table.css({
            webkitTransitionDuration: (d > 1000 ? 1000 : d) + "ms",
            webkitTransform: format(original.options.transform, tablePosition.x)
        });
		*/

		var tableTransitionCSS = {};
		tableTransitionCSS[getTransformProperty("transitionDuration")] = (d > 1000 ? 1000 : d) + "ms";
		tableTransitionCSS[getTransformProperty("transform")] = format(original.options.transform, tablePosition.x);
        table.css(tableTransitionCSS);
        
        return true;
    }
    
    function scaleStart (event) {
        event.preventDefault();
        //window.scrollTo(0,0);
        
        var $this = $(this),
            options = $this.data("jqt-slide-options"),
            tt = supportsTouch ? 
                    event.targetTouches : 
                    [
                        {pageX: controlPoints.x - event.pageX, pageY: controlPoints.y - event.pageY},
                        event
                    ];
        
        this.addEventListener(events.move, options.scale, false);
        this.addEventListener(events.end, options.scaleEnd, false);
        
        try{
            this.removeEventListener(events.move, options.drag, false);
            this.removeEventListener(events.end, options.dragEnd, false);
        }catch(e){}
        
        $this.data("jqt-slide-event", {
                //target: $(event.target).closest(options.imageSelector).css("webkitTransitionDuration", "0s"),
				target: $(event.target).closest(options.imageSelector).css(getVendorDOMProperty("transitionDuration"), "0s"),
                options: options,
                distance: Math.sqrt(Math.pow((tt[1].pageX - tt[0].pageX), 2) + Math.pow((tt[1].pageY - tt[0].pageY), 2))
            })
            .find(".image-list")
            //.css("webkitTransitionDuration", "0s");
			.css(getVendorDOMProperty("transitionDuration"), "0s");
            
        return true;
    }

    function scale (event) {
        if (event.targetTouches.length != 2) {
            return true;
        }
        
        event.preventDefault();
        
        var $this = $(this),
            original = $this.data("jqt-slide-event"),
            target = original.target,
            info = target.data("jqt-slide-info"),
            data = !!info && info[orientation] || false,
            tt = event.targetTouches,
            distance = Math.sqrt(Math.pow((tt[1].pageX - tt[0].pageX), 2) + Math.pow((tt[1].pageY - tt[0].pageY), 2)),
            difference = distance - original.distance,
            percentChange = (difference / original.distance) / 2,
            current = data.current,
            transform;

        if (!current) {
            parseImageData($this, target, original.options);
            info = target.data("jqt-slide-info");
            data = !!info && info[orientation] || false;
            current = data.current;
        }

        original.distance = distance;
            
		zoom($this, percentChange);

        return true;
    }

	function zoom($eventTarget, percentChange) {
		var original = $eventTarget.data("jqt-slide-event"),
            target = original.target,
			info = target.data("jqt-slide-info"),
            data = !!info && info[orientation] || false,
			current = data.current;

		var scale = (current.scale + (current.scale * percentChange));

		if (scale < 1) {
			// reset
			transform = format(original.options.imageTransform, (current.scale = 1), (currentleft = 0), (current.top = 0));
		} else {
			transform = format(original.options.imageTransform, 
							(current.scale = scale), 
							current.left, 
							current.top                        
					   );
		}

		//target.css({webkitTransitionDuration: "0s", webkitTransform: transform});
		
		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
		transitionCSS[getVendorDOMProperty("transform")] = transform;
		target.css(transitionCSS);
	}
    
    function scaleEnd (event) {
        event.preventDefault();
        
        var $this = $(this),
            original = $this.data("jqt-slide-event"),
            info = original.target.data("jqt-slide-info"),
            data = !!info && info[orientation] || false,
            current = data.current;
        
        this.removeEventListener(events.move, original.options.scale, false);
        this.removeEventListener(events.end, original.options.scaleEnd, false);
        
        if (!current) {
            parseImageData($this, original.target, original.options);
            info = original.target.data("jqt-slide-info");
            data = !!info && info[orientation] || false;
            current = data.current;
        }
        
        if (current.scale < data.original.scale) {
            current.scale = data.original.scale;
            current.left = data.original.left;
            current.top = data.original.top;
        }
        
		/*
        original.target.css({
            webkitTransitionDuration: original.options.scaleSpeed + "ms",
            webkitTransform: format(original.options.imageTransform, current.scale, current.left, current.top)
        });
		*/
		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionDuration")] = original.options.scaleSpeed + "ms";
		transitionCSS[getVendorDOMProperty("transform")] = format(original.options.imageTransform, current.scale, current.left, current.top);
		original.target.css(transitionCSS);
        
        return true;
    }

	function mouseWheel(event, delta) {
		var $this = $(this),
            options = $this.data("jqt-slide-options"),
            //table = $this.find(options.tableSelector).css("webkitTransitionDuration", "0s"),
			table = $this.find(options.tableSelector).css(getVendorDOMProperty("transitionDuration"), "0s"),
            position = table.data("jqt-slide-position"),
            target = $(event.target).closest(options.imageSelector),
            data = target.data("jqt-slide-info") || false,
            offset = !!data && data[orientation] || false,
            current = !!offset && offset.current || {},
            tt = supportsTouch ? event.targetTouches[0] : event;
        
        $this.data("jqt-slide-event", {
                table: table,
                position: {current: position.x, original: position.x},
                //target: target.css("webkitTransitionDuration", "0s"),
				target: target.css(getVendorDOMProperty("transitionDuration"), "0s"),
                options: options,
                slides: table.find(options.slideSelector),
                moved: false,
                x: tt.pageX,
                y: tt.pageY,
                left: current.left || 0,
                top: current.top || 0,
                timeStamp: +new Date
            });
		
		// zoom in/out 25% each wheel tick
		zoom($this, delta / 4);
	}
    
    function dispatchClick (target) {
        var theEvent = target.ownerDocument.createEvent("MouseEvents");
        theEvent.initEvent("click", true, true);
        target.dispatchEvent(theEvent);
        return true;
    }

    function slideTo (event, index, duration, options, slides) {
        event.preventDefault && event.preventDefault();
        
        var $this = $(this), 
			table,
			current, 
			last, 
			img;
            
        options = options || $this.data("jqt-slide-options");
        slides = slides || $this.find(options.slideSelector);
        table = options.table;
        index = Number(index || 0);
        duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
        
        if (index >= options.data.length) {
            $this.triggerHandler("jqt-slide-goto", [0, duration, options, slides]);
            return true;
        }
        
        if (index < 0) {
            index = options.data.length + index;
        }
        
        last = slides.filter("." + options.currentClass);
        current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
        img = current.find(options.imageSelector);
        
        if (img.attr("src") != options.data[index].src) {
            img.parent().addClass(options.notLoadedClass);
            img.attr("src", options.data[index].src);
			img.attr("alt", options.data[index].alt);
			img.attr("title", options.data[index].title);
        }
        
        position = -current.addClass(options.currentClass).attr("offsetLeft");
    
        if (position > 0) {
            position = 0;
        } else if (position < -(table.width()-current.width())) {
            position = -(table.width()-current.width());
        }
        
        $this.trigger("jqt-slide-slide-change", [index, duration, position]);
        table.data("jqt-slide-position").x = position;

		/*
        table.one("webkitTransitionEnd", function() {
            options.rearrange(current, last, options);
        })
        .css({
            webkitTransitionDuration: duration+"ms",
            webkitTransform: format(options.transform, position)
        });
		*/
		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionDuration")] = duration + "ms";
		transitionCSS[getVendorDOMProperty("transform")] = format(options.transform, position);

		table.one(getTransitionEndEvent(), function() {
			options.rearrange(current, last, options);
		}).css(transitionCSS);
        
        return true;
    }

    function goTo (event, index, duration, options, slides) {
        event.preventDefault && event.preventDefault();
        
        var $this = $(this), 
			table, 
			current, 
			last, 
			img;
        
        options = options || $this.data("jqt-slide-options");
        slides = slides || $this.find(options.slideSelector);
        table = options.table;
        index = Number(index || 0);
        duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
        
        if (index >= options.data.length) {
            index = 0;
        }
        
        if (index < 0) {
            index = options.data.length + index;
        }
        
        last = slides.filter("."+options.currentClass);
        current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
        img = current.find(options.imageSelector);
        
        if (img.attr("src") != options.data[index].src) {
            img.parent().addClass(options.notLoadedClass);
            img.attr("src", options.data[index].src);
			img.attr("alt", options.data[index].alt);
			img.attr("title", options.data[index].title);
        }
        
        position = -current.addClass(options.currentClass).attr("offsetLeft");
        
        if (position > 0) {
            position = 0;
        } else if (position < -(table.width() - current.width())) {
            position = -(table.width() - current.width());
        }

        table.data("jqt-slide-position").x = position;

		/*
        table.css({
            webkitTransitionDuration: "0s",
            webkitTransform: format(options.transform, position)
        });
		*/

		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
		transitionCSS[getVendorDOMProperty("transform")] = format(options.transform, position);
		table.css(transitionCSS);
        
        options.rearrange(current, last, options);
        return true;
    }
    
    function rearrange (current, last, options) {
		current.prevAll().slice(0, options.maxSlidesBefore).each(function() {
			var $this = $(this),
				$img = $this.find(options.imageSelector);
				
			$img.attr("src", options.data[$this.attr(options.dataAttribute)].src)
				.attr("alt", options.data[$this.attr(options.dataAttribute)].alt)
				.attr("title", options.data[$this.attr(options.dataAttribute)].title);
		});

		current.nextAll().slice(0, options.maxSlidesAfter).each(function() {
			var $this = $(this),
				$img = $this.find(options.imageSelector);

			$img.attr("src", options.data[$this.attr(options.dataAttribute)].src)
				.attr("alt", options.data[$this.attr(options.dataAttribute)].alt)
				.attr("title", options.data[$this.attr(options.dataAttribute)].title);
		});
        
        current.prevAll().slice(options.maxSlidesBefore)
            .find(options.imageSelector)
            .filter("[src]")
            .each(function() {
                var clone = options.blankImage.clone(true),
                    $this = $(this);

                $this.parent().addClass(options.notLoadedClass);
                $this.replaceWith(clone);
                
                parseImageData($this, clone, options);
            });
            
        current.nextAll().slice(options.maxSlidesAfter)
            .find(options.imageSelector)
            .filter("[src]")
            .each(function() {
                var clone = options.blankImage.clone(true),
                    $this = $(this);
                
                $this.parent().addClass(options.notLoadedClass);
                $this.replaceWith(clone);
                
                parseImageData($this, clone, options);
            });
        
        if (last[0] !== current[0]) {
            resetDimensions(last.find(options.imageSelector));
        }
        
        return true;
    }

    function createSlide (data, index, options, inWindow) {
        var slide = $(data.template || options.slideTemplate).attr(options.dataAttribute, index),
            img = slide.find(options.imageSelector);

		var loadedImage = img.load(options.loader);

        parseImageData(options.gallery, loadedImage, options);

        if (inWindow) {
            img.attr("src", data.src).attr("alt", data.alt).attr("title", data.title);
        }
                        
        return slide;               
    }
    
    function resetDimensions (images) {
        return images.each(function(i) {
            var t = $(this),
                data = t.data("jqt-slide-info"),
                d = data[orientation];//,p;
            
            if (d && d.current.scale != 1 && d.current.scale != d.original.scale) {
				/*
                t.css({
                    webkitTransitionDuration: "0s", 
                    webkitTransform: format(data.galleryOptions.imageTransform, 
                                            d.current.scale = d.original.scale,
                                            d.current.left = d.original.left,
                                            d.current.top = d.original.top
                                     )
                });
				*/

				var transitionCSS = {};
				transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
				transitionCSS[getVendorDOMProperty("transform")] = format(data.galleryOptions.imageTransform, 
																		d.current.scale = d.original.scale,
																		d.current.left = d.original.left,
																		d.current.top = d.original.top
																 );
				t.css(transitionCSS);
                
                d.current.width = d.original.width;
                d.current.height = d.original.height;
            }
            return null;
        });
    }
    
    function addHover (gallery, element) {
        $(element).addClass("hover")
        gallery.one("touchend", function(){$(element).removeClass("hover")});
        return null;
    }
    
    function format (s) {
        var args = arguments;
        return s.replace(/\{(\d+)\}/g, function(a,b){return args[Number(b)+1] + ""});
    }
    
    function onorientationchange () {
        orientation = Math.abs(window.orientation) == 90 ? "landscape" : "portrait";
        $("#jqt").removeClass("portrait landscape").addClass(orientation);
        
        setTimeout(function(){
              var loop = function() {
                    var $this = $(this), 
                        options = $this.data("jqt-slide-options"),
                        slides = $this.find(options.slideSelector),
                        images =  slides.find(options.imageSelector);
                    
					alignSlides($this, slides, options);
                    resetDimensions(images.filter("[src]"));
					adjustImageSize(images);
                };
            
            $(galleries).filter(":visible").each(loop).end().filter(":not(:visible)").each(loop);
        }, 10);
            
        controlPoints = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
        return null;
    }

    function alignSlides ($this, slides, options) {
        table = $this.find(options.tableSelector),
        position = -slides.filter("." + options.currentClass).attr("offsetLeft");  
        table.data("jqt-slide-position").x = position;

		/*
        table.css({
            webkitTransitionDuration: "0s",
            webkitTransform: format(options.transform, position)
        });
		*/

		var transitionCSS = {};
		transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
		transitionCSS[getVendorDOMProperty("transform")] = format(options.transform, position);
		table.css(transitionCSS);
        
        return null;
    }
    
    function loader () {
        var innerWidth = window.innerWidth,
            innerHeight = window.innerHeight,
            $this = $(this),
            w = $this.width(),
            h = $this.height(),
            top, left,
            scale = Math.min(Math.min(w, innerWidth)/w, Math.min(h, innerHeight)/h),
            width = w * scale,
            height = h * scale,
            data = $this.data("jqt-slide-info"),
            options = data.galleryOptions;
        
        $this.parent().removeClass(options.notLoadedClass);
        
        if (!$this.is(":visible")) {
            data.gallery.one("pageAnimationEnd", function(){parseImageData(data.gallery, $this, data.galleryOptions)});
        } else if (width > innerWidth || height > innerHeight) {
            left = -Math.floor(((width - innerWidth) / scale) / 2);
            top = -Math.floor(((height - innerHeight) / scale) / 2);
            
			/*
            $this.css({
                 webkitTransitionDuration: "0s",
                 webkitTransform: format(options.imageTransform, scale, left, top)
            }).addClass(options.presizedClass);
			*/

			var transitionCSS = {};
			transitionCSS[getVendorDOMProperty("transitionDuration")] = "0s";
			transitionCSS[getVendorDOMProperty("transform")] = format(options.imageTransform, scale, left, top);
			$this.css(transitionCSS).addClass(options.presizedClass);
            
            data = data[orientation];
            data.current.scale = data.original.scale = scale;
            data.current.left = data.original.left = left;
            data.current.top = data.original.top = top;
            data.current.width = data.original.width = width * scale;
            data.current.height = data.original.height = height * scale;
        } 
        
        return null;
    }

	var stringRules = "", 
		rules = cssRules, 
		o = window.innerHeight > window.innerWidth ? "portrait" : "landscape",
		buildProperties = function(name, value) {
			stringRules += name + ":" + ($.isFunction(value) ? value(rules.variables) : value) + ";";
		},
		buildRules = function(name, properties) {
			stringRules += name + "{";
			$.each(properties, buildProperties);
			stringRules += "}";
		};

	function updateCSS(togglePip, isPip, width, height) {
		if (togglePip) {
			if (isPip) {
				pipWidth = width;
				pipHeight = height;
			} else {
				pipWidth = null;
				pipHeight = null;
			}
		}

		stringRules = "",
		$.each(rules.defaults, buildRules);
		$.each(rules[o], buildRules);
		$("#jqt-photo-css").html(stringRules);

		onorientationchange();
	}
    
    $(function() {
        //window.scrollTo(0,0);
        controlPoints = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
        
        $.each(rules.defaults, buildRules);
        $.each(rules[o], buildRules);
          
        $(document.createElement("style"))
            .attr({type:"text/css",media:"screen", id:"jqt-photo-css"})
            .html(stringRules)
            .appendTo("head");

		$(window).bind("resize", function() {
			clearTimeout(this.resizeTimer);
			this.resizeTimer = setTimeout(finishedResizing, 200);
		});

		function finishedResizing() {
			updateCSS();
			adjustImageSize($(".jqt-slide img"));
		}
    });
})(jQuery);


/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 * Thanks to: Seamus Leahy for adding deltaX and deltaY
 *
 * Version: 3.0.3
 * 
 * Requires: 1.2.2+
 */

(function($) {
	var types = ['DOMMouseScroll', 'mousewheel'];

	$.event.special.mousewheel = {
		setup: function() {
			if ( this.addEventListener ) {
				for ( var i=types.length; i; ) {
					this.addEventListener( types[--i], handler, false );
				}
			} else {
				this.onmousewheel = handler;
			}
		},
    
		teardown: function() {
			if ( this.removeEventListener ) {
				for ( var i=types.length; i; ) {
					this.removeEventListener( types[--i], handler, false );
				}
			} else {
				this.onmousewheel = null;
			}
		}
	};

	$.fn.extend({
		mousewheel: function(fn) {
			return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
		},
    
		unmousewheel: function(fn) {
			return this.unbind("mousewheel", fn);
		}
	});


	function handler(event) {
		var orgEvent = event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
    
		event = $.event.fix(event || window.event);
		event.type = "mousewheel";
    
		// Old school scrollwheel delta
		if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
		if ( event.detail     ) { delta = -event.detail/3; }
    
		// New school multidimensional scroll (touchpads) deltas
		deltaY = delta;
    
		// Gecko
		if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
			deltaY = 0;
			deltaX = -1*delta;
		}
    
		// Webkit
		if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
		if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
    
		// Add event and delta to the front of the arguments
		args.unshift(event, delta, deltaX, deltaY);
    
		return $.event.handle.apply(this, args);
	}
})(jQuery);


/* 
 * jquery.slideViewer.js Plug-In
 *
 * Wraps up jQTouch dependences to abstract away jqt.photo.js w/ our own plug-in interface
 *
 * Method support for:
 *		goToSlide(index, duration)
 *		addNewSlide(slide, advance)
 *
 * Raises event 'slidechanged' to the jQuery element with an extra index parameter of the current slide
 * when there is a user-initiated slide change due to a swipe/drag
 *
 */

(function($) {
	var jQT = new $.jQTouch(),
		selectors = {
			gallery: '.jqt-slide',
			currentSlide: '.jqt-slide-current',
			slideEvent: 'jqt-slide-slideto'
		},
		changingSlide = false,
		supportsCSS3,
		currentImg,
		o,
		currentIndex = 0,
		methods = {
			init: function (options) {
				var defaults = {
					slides: []
				}
                 
				o = $.extend(defaults, options);

				// jQT-photo extension requires CSS3 transforms 
				supportsCSS3 = Modernizr.csstransforms || Modernizr.csstransforms3d;

				if (!supportsCSS3) {
					$(window).resize(function() {
						adjustSize(currentImg);
					});
				}

				return this.each(function (i, e) {
					if (supportsCSS3) {
						jQT.generateSlides("slideViewer", o.slides, { appendTo: e});

						$(selectors.gallery).bind(selectors.slideEvent, function() {
							if (!changingSlide) {
								var index = parseInt($(selectors.currentSlide, $(this)).attr('data-index'), 10);
								$(this).trigger('slidechanged', [index]);
							}
						});
					} else {
						currentImg = $('<img>').appendTo(this);
						adjustSize(currentImg);
						loadSlide(0);
					}
				});
			},
			goToSlide: function (index, duration) {
				changingSlide = true;

				if (supportsCSS3) {
					jQT.goToSlide(selectors.gallery, index, duration);
				} else {
					loadSlide(index);
				}

				changingSlide = false;
			},
			addNewSlide: function(slide) {
				if (supportsCSS3) {
					jQT.addSlide(selectors.gallery, slide);
				} else {
					o.slides.push(slide);
				}
			},
			updateCSS: function(isPip, width, height) {
				if (supportsCSS3) {
					jQT.updateCSS(true, isPip, width, height);
				}
			}	
		};

	function loadSlide(index) {
		if (o.slides && index >= 0 && index < o.slides.length) {
			var slide = o.slides[index];
			currentImg.attr({ src: slide.src, title: slide.title, alt: slide.alt});
		}
	}

	function adjustSize(image) {
		var parent = $(window);
		if (parent.height() > parent.width()) {
			image.css('width', '100%').css('height', '');
		} else {
			image.css('width', '').css('height', '100%');
		}
	}

    $.fn.slideViewer = function (method) {
        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.slideViewer');
        }
    }
})(jQuery);