// filename: jquery.touchify.js
		
		// adaption of Ross Posterous mouse -> touch events
		// http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript
		// TODO: don't prevent default (AKA scroll) for direction opposite to that set to draggable (pass in condition to options, may have to save state in private vars...)
		(function ($) {
			
			$.fn.touchify = function() {
			
				var touchHandler = function (e) {
				
					var touches = e.changedTouches,
						first = touches[0],
						type = "",
						simulatedEvent,
						moved;
					
					switch (e.type) {
					
					case "touchstart":
					
						type = "mousedown";
						moved = false;
						
						break;
					
					case "touchmove":
					
						type = 'mousemove';
						moved = true;
						
						break;
						
					case 'touchend':
						
						type = 'mouseup';
						moved = false;
						
						break;
							
						default: return;
					}
					
					// initMouseEvent(type, canBubble, cancelable, view, clickCount, 
					//           screenX, screenY, clientX, clientY, ctrlKey, 
					//           altKey, shiftKey, metaKey, button, relatedTarget);
					
					simulatedEvent = document.createEvent('MouseEvent');
					simulatedEvent.initMouseEvent(type, true, true, window, 1, 
											  first.screenX, first.screenY, 
											  first.clientX, first.clientY, false, 
											  false, false, false, 0/*left*/, null);
					
					first.target.dispatchEvent(simulatedEvent);
					
					if (moved) { // ensure anchor links aren't prevented
						e.preventDefault();
					}
				};
			
				return this.each(function () {
				
					this.addEventListener("touchstart", touchHandler, true);
					this.addEventListener("touchmove", touchHandler, true);
					this.addEventListener("touchend", touchHandler, true);
					this.addEventListener("touchcancel", touchHandler, true);
								
				});
				
			};
			
		})(jQuery);
/*
 * jQuery UI Carousel Plugin v0.5.2
 *
 * Copyright (c) 2011 Richard Scarrott
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Requires:
 * jQuery v1.4+,
 * jQuery UI Widget Factory 1.8+
 *
 */
 
(function ($, undefined) {

	$.widget('ui.carousel', {
	
		version: '0.5.2',
		
		// holds original class string
		oldClass: null,
		
		options: {
			itemsPerPage: 'auto',
			itemsPerTransition: 'auto',
			orientation: 'horizontal',
			noOfRows: 1, // horizontal only
			unknownHeight: false, // horizontal only (allows unknown item height - useful if, for example, items contains textual content)
			pagination: true,
			insertPagination: null,
			nextPrevActions: true,
			insertNextAction: null,
			insertPrevAction: null,
			speed: 'normal',
			easing: 'swing',
			startAt: null,
			beforeAnimate: null,
			afterAnimate: null
		},
		
		_create: function () {
		
			this.itemIndex = 0;
			
			this._elements();
			this._addClasses();
			this._defineOrientation();
			this._addMask();
			this._setMaskDim();
			this._setItemDim();
			this._setItemsPerPage();
			this._setNoOfItems();
			this._setNoOfPages();
			this._setRunnerWidth();
			this._setMaskHeight();
			this._setLastPos();
			this._addPagination();
			this._addNextPrevActions();
			
			if (this.options.startAt) {
				this.goTo(this.options.startAt);
			}
			
			this._updateUi();
			
		},
		
		// caches DOM elements
		_elements: function () {
		
			var elems = this.elements = {};
			
			elems.mask = this.element.find('.mask');
			elems.runner = this.element.find('ul');
			elems.items = elems.runner.children('li');
			elems.pagination = null;
			elems.nextAction = null;
			elems.prevAction = null;
		
		},
		
		_addClasses: function () {
		
			if (!this.oldClass) {
				this.oldClass = this.element.attr('class');
			}
		
			this._removeClasses();
			
			var baseClass = this.widgetBaseClass,
				classes = [];
				
			classes.push(baseClass);
			classes.push(baseClass + '-' + this.options.orientation);
			classes.push(baseClass + '-items-' + this.options.itemsPerPage);
			classes.push(baseClass + '-rows-' + this.options.noOfRows);
		
			this.element.addClass(classes.join(' '));
		
		},
		
		// removes ui-carousel* classes
		_removeClasses: function () {
		
			var self = this,
				uiClasses = [],
				current,
				fragments;
		
			this.element.removeClass(function (i, currentClasses) {
				
				currentClasses = currentClasses.split(' ');
				
				$.each(currentClasses, function (i) {
					
					current = currentClasses[i];
					fragments = current.split('-');
					
					if (fragments[0] === self.namespace && fragments[1] === self.widgetName) {
						uiClasses.push(current);
					}
					
				});
				
				return uiClasses.join(' ');
				
			});
		
		},
		
		// defines obj to hold strings based on orientation for dynamic method calls
		_defineOrientation: function () {

			if (this.options.orientation === 'horizontal') {
				this.horizontal = true;
				this.helperStr = {
					pos: 'left',
					pos2: 'right',
					dim: 'width'
				};
			}
			else {
				this.horizontal = false;
				this.helperStr = {
					pos: 'top',
					pos2: 'bottom',
					dim: 'height'
				};	
				this.options.noOfRows = 1;
			}
		
		},
		
		// adds masking div (aka clipper)
		_addMask: function () {
		
			var elems = this.elements;
			
			if (elems.mask.length) {
				return;
			}
			
			elems.mask = elems.runner
				.wrap('<div class="mask" />')
				.parent();
			
			// indicates whether mask was dynamically added or already existed in mark-up
			this.maskAdded = true;
			
		},
		
		// sets maskDim to later detemine lastPos
		_setMaskDim: function () {
		
			this.maskDim = this.elements.mask[this.helperStr.dim]();
		
		},
		
		// sets masks height allowing items to have an unknown height (not applicable to vertical orientation)
		_setMaskHeight: function () {
		
			if (!this.horizontal || !this.options.unknownHeight) {
				return;
			}
			
			var elems = this.elements,
				maskHeight = elems.runner.outerHeight(true);
			
			elems.mask.height(maskHeight);
			
		},
		
		// sets itemDim to the dimension of first item incl. margin
		_setItemDim: function () {
			
			// is this ridiculous??
			this.itemDim = this.elements.items['outer' + this.helperStr.dim.charAt(0).toUpperCase() + this.helperStr.dim.slice(1)](true);
			
		},
		
		// sets options.itemsPerPage based on maskdim
		_setItemsPerPage: function () {
			
			// if itemsPerPage of type number don't dynamically calculate
			if (typeof this.options.itemsPerPage === 'number') {
				// don't directly use options.itemsPerPage as reference to 'auto' needs to be kept if not number
				this.itemsPerPage = this.options.itemsPerPage;
			}
			else {
				this.itemsPerPage = Math.floor(this.maskDim / this.itemDim);
			}
			
		},
		
		// sets no of items, not neccesarily the literal number of items if more than one row
		_setNoOfItems: function () {

			this.noOfItems = Math.ceil(this.elements.items.length / this.options.noOfRows);
			
			// fixed 9 items, 3 rows, 4 shown 
			if (this.noOfItems < this.itemsPerPage) {
				this.noOfItems = this.itemsPerPage;
			}
			
		},
		
		// sets noOfPages
		_setNoOfPages: function () {
		
			this.noOfPages = Math.ceil((this.noOfItems - this.itemsPerPage) / this._getitemsPerTransition()) + 1;
		
		},
		
		_getitemsPerTransition: function () {
		    
		    if (this.options.itemsPerTransition === 'auto') {
		        return this.itemsPerPage;
		    }
		    
		    return this.options.itemsPerTransition;
		},
		
		// sets runners width
		_setRunnerWidth: function () {
		
			if (!this.horizontal) {
				return;
			}
			
			var width = this.itemDim * this.noOfItems;
			this.elements.runner.width(width);
			
		},
		
		// sets lastPos to ensure runner doesn't move beyond mask (allowing mask to be any width and the use of margins)
		_setLastPos: function () {
			
			// noOfrows means last theoretical item might not be the last item
			var lastItem = this.elements.items.eq(this.noOfItems - 1);
			
			this.lastPos = lastItem.position()[this.helperStr.pos] + this.itemDim -
				this.maskDim - parseInt(lastItem.css('margin-' + this.helperStr.pos2), 10);
				
		},
		
		// adds pagination links and binds associated events
		_addPagination: function () {
		
			if (!this.options.pagination) {
				return;
			}
		
			var self = this,
				elems = this.elements,
				opts = this.options,
				links = [],
				i;
			
			for (i = 0; i < this.noOfPages; i++) {
				links[i] = '<li><a href="#item-' + i + '">' + (i + 1) + '</a></li>';
			}
			
			elems.pagination = $('<ol class="pagination-links" />')
				.append(links.join(''))
				.delegate('a', 'click.' + self.widgetName, function () {
				
					self.goTo(this.hash.split('-')[1] * self._getitemsPerTransition());
					
					return false;
					
				})
				
			if ($.isFunction(opts.insertPagination)) {
				$.proxy(opts.insertPagination, elems.pagination)();
			}
			else {
				elems.pagination.insertAfter(elems.mask);
			}
				
		},
		
		// refreshes pagination links
		_refreshPagination: function () {
			
			if (!this.options.pagination) {
				return;
			}
			
			this.elements.pagination.remove();
			this._setNoOfPages();
			this._addPagination();
			
		},
		
		// jumps to specific element
		goTo: function (index) {
		
			if (typeof index === 'number') {
				this.itemIndex = index;
			}
			else {
				// assume jquery or DOM element
				this.itemIndex = $(index).index();
			}
			
			this._go();
			
		},
		
		// adds next and prev links
		_addNextPrevActions: function () {
		
			if (!this.options.nextPrevActions) {
				return;
			}
		
			var self = this,
				elems = this.elements,
				opts = this.options;
				
			elems.prevAction = $('<a href="#" class="prev">Prev</a>')
				.bind('click.' + self.widgetName, function () {
					self.prev();
					return false;
				});;
			
			elems.nextAction = $('<a href="#" class="next">Next</a>')
				.bind('click.' + self.widgetName, function () {
					self.next();
					return false;
				});
			
			if ($.isFunction(opts.insertPrevAction)) {
				$.proxy(opts.insertPrevAction, elems.prevAction)();
			}
			else {
				elems.prevAction.appendTo(this.element);
			}
				
			if ($.isFunction(opts.insertNextAction)) {
				$.proxy(opts.insertNextAction, elems.nextAction)();
			}
			else {
				elems.nextAction.appendTo(this.element);
			}
			
		},
		
		// moves to next page
		next: function () {
		
			this.itemIndex = this.itemIndex + this._getitemsPerTransition();
			this._go();
			
		},
		
		// moves to prev page
		prev: function () {
		
			this.itemIndex = this.itemIndex - this._getitemsPerTransition();
			this._go();
			
		},
		
		// updates pagination, next and prev link status classes
		_updateUi: function () {
		
			var elems = this.elements,
				index = this.itemIndex,
				
				// add void class if ui doesn't make sense - can then be either hidden or styled like disabled / current
				isVoid = this.noOfItems <= this.itemsPerPage;
		
			if (this.options.pagination) {
			
				if (isVoid) {
					elems.pagination.addClass('void');
				}
				else {
					elems.pagination
						.children('li')
							.removeClass('current')
							.eq(Math.ceil(index / this._getitemsPerTransition()))
								.addClass('current');		
				}
				
			}

			if (this.options.nextPrevActions) {
			
				var nextPrev = elems.nextAction.add(elems.prevAction);
				nextPrev.removeClass('disabled');
			
				if (isVoid) {
					nextPrev.addClass('void');		
				}
				else {
					nextPrev.removeClass('void');
							
					if (index === (this.noOfItems - this.itemsPerPage)) {
						elems.nextAction.addClass('disabled');
					}
					else if (index === 0) {
						elems.prevAction.addClass('disabled');
					}
				}
			}
			
		},
		
		// validates itemIndex and initiates slide
		_go: function () {
		
			var elems = this.elements,
				pos;
			
			// check whether there are enough items to animate to
			if (this.itemIndex > (this.noOfItems - this.itemsPerPage)) {
				this.itemIndex = this.noOfItems - this.itemsPerPage; // go to last panel - items per transition
			}
			
			if (this.itemIndex < 0) {
				this.itemIndex = 0; // go to first
			}
			
			this._trigger('beforeAnimate', null, {
				index: this.itemIndex
			});
			
			pos = this.itemIndex * this.itemDim;
			
			this._slide(pos);
			this._updateUi();
		},
		
		// slides runner
		_slide: function (pos) {
		
			var self = this,
				elems = this.elements,
				animateProps = {};
				
			// check pos doesn't go past last
			if (pos > this.lastPos) {
				pos = this.lastPos;
			}
			
			animateProps[this.helperStr.pos] = -pos;
		
			elems.runner
				.stop()
				.animate(animateProps, this.options.speed, this.options.easing, function () {
					
					self._trigger('afterAnimate', null, {
						index: self.itemIndex
					});
					
				});
		
		},
		
		// refresh carousel
		_refresh: function () {
			
			this.itemIndex = 0;
			this.elements.runner.css({
				left: '',
				top: ''
			});
			this._addClasses();
			this._setMaskDim();
			this._setItemDim();
			this._setItemsPerPage();
			this._setNoOfItems();
			this._setRunnerWidth();
			this._setMaskHeight();
			this._setLastPos();
			this._refreshPagination();
			this._updateUi();
			
		},
		
		// adds items to end and refreshes carousel, items === jquery obj
		addItems: function (items) {
		
			var elems = this.elements;
		
			items.appendTo(elems.runner);
			elems.items = elems.runner.children('li');
			this._refresh();
		
		},
		
		// handles option updates
		_setOption: function (option, value) {
			
			var elems = this.elements,
				opts = this.options;
				
			$.Widget.prototype._setOption.apply(this, arguments);
			
			switch (option) {
				
			case 'itemsPerPage':
				
				this._refresh();
				
				break;
				
			case 'itemsPerTransition':
				
				this._refreshPagination();
				this._updateUi();
				
				break;
				
			case 'noOfRows':
				
				if (this.horizontal) {
					this._refresh();
				}
				else {
					// noOfRows must be 1 if vertical
					opts.noOfRows = 1;
				}
				
				break;
				
			case 'unknownHeight':
				
				if (value) {
					this._setMaskHeight();
				}
				else {
					elems.mask.height('');
				}
				
				break;
				
			case 'orientation':
						
				this._defineOrientation();
				elems.mask.height('');
				elems.runner.width('');
				this._refresh();
				
				break;
					
			case 'pagination':
				
				if (value && !elems.pagination) {
					this._addPagination();
					this._updateUi();
				}
				else if (!value && elems.pagination) {
					elems.pagination.remove();
					elems.pagination = null;
				}
				
				break;
				
			case 'nextPrevActions':

				if (value && !elems.nextAction) {
					this._addNextPrevActions();
					this._updateUi();
				}
				else if (!value && elems.nextAction) {
					elems.nextAction.remove();
					elems.nextAction = null;
					elems.prevAction.remove();
					elems.prevAction = null;
				}
				
				break;
					
			}
		
		},
		
		// returns carousel to original state
		destroy: function () {
		
			var elems = this.elements,
				cssProps = {};
				
			this.element.removeClass().addClass(this.oldClass);
		
			if ('maskAdded' in this) {
				elems.runner
					.unwrap('.mask');
			}
			else {
				elems.mask.height('');
			}
			
			// should really store original value?
			cssProps[this.helperStr.pos] = '';
			cssProps[this.helperStr.dim] = '';
			elems.runner.css(cssProps);
			
			if (elems.pagination) {
				elems.pagination.remove();
			}
			
			if (elems.nextAction) {
				elems.nextAction.remove();
				elems.prevAction.remove();
			}
			
			// overkill?
			$.each(elems, function () {
				$(this).unbind('.' + self.widgetName);
			});
			
			$.Widget.prototype.destroy.apply(this, arguments);
			
		}
		
	});
	
})(jQuery);

// filename jquery-ui-touchCarousel.js
		
		(function ($, undefined) {
		
			var _super = $.ui.carousel.prototype;
			
			$.widget('ui.touchCarousel', $.ui.carousel, {
			
				options: {
					sensitivity: 0.8
				},
				
				_create: function () {
					
					_super._create.apply(this);
					
					var self = this;
					
					this._setAxis();
				
					this.elements.runner
						.touchify() // maps draggables mouse events to touch events
						.draggable({
							axis: this.axis,
							start: function (e) {
								e = e.originalEvent.touches ? e.originalEvent.touches[0] : e;
								self._dragStartHandler(e);
							},
							stop: function (e) {	
								e = e.originalEvent.touches ? e.originalEvent.touches[0] : e;
								self._dragStopHandler(e);
							}
								
						});
					
				},
				
				_setAxis: function () {
					
					this.axis = this.horizontal ? 'x' : 'y';
				
				},
				
				_dragStartHandler: function (e) {
				
					this.startTime = this._getTime();
					
					this.startPos = {
						x: e.pageX,
						y: e.pageY
					};
				
				},
				
				_dragStopHandler: function (e) {
				
					var time,
						distance,
						speed,
						direction;
						
					// if touch direction changes start date should prob be reset to correctly determine speed...
					this.endTime = this._getTime();
					
					time = this.endTime - this.startTime;
					
					this.endPos = {
						x: e.pageX,
						y: e.pageY
					};
					
					distance = Math.abs(this.startPos[this.axis] - this.endPos[this.axis]);
					speed = distance / time;
					
					direction = this.startPos[this.axis] > this.endPos[this.axis] ? 'next' : 'prev';
					
					if (speed > this.options.sensitivity || distance > (this.itemDim * this._getitemsPerTransition() / 2)) {
						this[direction]();
					}
					else {
						this.goTo(this.itemIndex); // go to current element
					}
				
				},
				
				_getTime: function () {
					
					var date = new Date();
					return date.getTime();
				
				},
				
				_setOption: function (option, value) {
				
					_super._setOption.apply(this, arguments);
					
					switch (option) {
						
					case 'orientation':
						
						this._setAxis();
						this._switchAxis();
						
						break;
					
					}
					
				},
				
				_switchAxis: function () {
				
					this.elements.runner.draggable('option', 'axis', this.axis);
				
				},
				
				_destroy: function () {
					
					_super._destroy.apply(this);
					
					this.elements.runner.draggable('destroy');
					
					// must destroy touchify events...$.fn.untouchify?
					
				}
				
			});
		
		})(jQuery);
		
