
/*
* A jQuery extension to provide scrollbars to a container
* 
* @author Andrew Lowndes
* @date 08/12/10
*
* @requires jquery.js
* @requires jquery.event.drag.js
* @requires jquery.resize.js
* @requires jquery.mousehold.js
* @requires jquery.mousewheel.js
* @requires jquery.jswipe.js
*/
(function($) {


/*!
 * jquery.event.drag - v 2.0.0
 * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
 * Open Source MIT License - http://threedubmedia.com/code/license
 */
;(function(f){f.fn.drag=function(b,a,d){var e=typeof b=="string"?b:"",k=f.isFunction(b)?b:f.isFunction(a)?a:null;if(e.indexOf("drag")!==0)e="drag"+e;d=(b==k?a:d)||{};return k?this.bind(e,d,k):this.trigger(e)};var i=f.event,h=i.special,c=h.drag={defaults:{which:1,distance:0,not:":input",handle:null,relative:false,drop:true,click:false},datakey:"dragdata",livekey:"livedrag",add:function(b){var a=f.data(this,c.datakey),d=b.data||{};a.related+=1;if(!a.live&&b.selector){a.live=true;i.add(this,"draginit."+ c.livekey,c.delegate)}f.each(c.defaults,function(e){if(d[e]!==undefined)a[e]=d[e]})},remove:function(){f.data(this,c.datakey).related-=1},setup:function(){if(!f.data(this,c.datakey)){var b=f.extend({related:0},c.defaults);f.data(this,c.datakey,b);i.add(this,"mousedown",c.init,b);this.attachEvent&&this.attachEvent("ondragstart",c.dontstart)}},teardown:function(){if(!f.data(this,c.datakey).related){f.removeData(this,c.datakey);i.remove(this,"mousedown",c.init);i.remove(this,"draginit",c.delegate);c.textselect(true); this.detachEvent&&this.detachEvent("ondragstart",c.dontstart)}},init:function(b){var a=b.data,d;if(!(a.which>0&&b.which!=a.which))if(!f(b.target).is(a.not))if(!(a.handle&&!f(b.target).closest(a.handle,b.currentTarget).length)){a.propagates=1;a.interactions=[c.interaction(this,a)];a.target=b.target;a.pageX=b.pageX;a.pageY=b.pageY;a.dragging=null;d=c.hijack(b,"draginit",a);if(a.propagates){if((d=c.flatten(d))&&d.length){a.interactions=[];f.each(d,function(){a.interactions.push(c.interaction(this,a))})}a.propagates= a.interactions.length;a.drop!==false&&h.drop&&h.drop.handler(b,a);c.textselect(false);i.add(document,"mousemove mouseup",c.handler,a);return false}}},interaction:function(b,a){return{drag:b,callback:new c.callback,droppable:[],offset:f(b)[a.relative?"position":"offset"]()||{top:0,left:0}}},handler:function(b){var a=b.data;switch(b.type){case !a.dragging&&"mousemove":if(Math.pow(b.pageX-a.pageX,2)+Math.pow(b.pageY-a.pageY,2)<Math.pow(a.distance,2))break;b.target=a.target;c.hijack(b,"dragstart",a); if(a.propagates)a.dragging=true;case "mousemove":if(a.dragging){c.hijack(b,"drag",a);if(a.propagates){a.drop!==false&&h.drop&&h.drop.handler(b,a);break}b.type="mouseup"}case "mouseup":i.remove(document,"mousemove mouseup",c.handler);if(a.dragging){a.drop!==false&&h.drop&&h.drop.handler(b,a);c.hijack(b,"dragend",a)}c.textselect(true);if(a.click===false&&a.dragging){jQuery.event.triggered=true;setTimeout(function(){jQuery.event.triggered=false},20);a.dragging=false}break}},delegate:function(b){var a= [],d,e=f.data(this,"events")||{};f.each(e.live||[],function(k,j){if(j.preType.indexOf("drag")===0)if(d=f(b.target).closest(j.selector,b.currentTarget)[0]){i.add(d,j.origType+"."+c.livekey,j.origHandler,j.data);f.inArray(d,a)<0&&a.push(d)}});if(!a.length)return false;return f(a).bind("dragend."+c.livekey,function(){i.remove(this,"."+c.livekey)})},hijack:function(b,a,d,e,k){if(d){var j={event:b.originalEvent,type:b.type},n=a.indexOf("drop")?"drag":"drop",l,o=e||0,g,m;e=!isNaN(e)?e:d.interactions.length; b.type=a;b.originalEvent=null;d.results=[];do if(g=d.interactions[o])if(!(a!=="dragend"&&g.cancelled)){m=c.properties(b,d,g);g.results=[];f(k||g[n]||d.droppable).each(function(q,p){l=(m.target=p)?i.handle.call(p,b,m):null;if(l===false){if(n=="drag"){g.cancelled=true;d.propagates-=1}if(a=="drop")g[n][q]=null}else if(a=="dropinit")g.droppable.push(c.element(l)||p);if(a=="dragstart")g.proxy=f(c.element(l)||g.drag)[0];g.results.push(l);delete b.result;if(a!=="dropinit")return l});d.results[o]=c.flatten(g.results); if(a=="dropinit")g.droppable=c.flatten(g.droppable);a=="dragstart"&&!g.cancelled&&m.update()}while(++o<e);b.type=j.type;b.originalEvent=j.event;return c.flatten(d.results)}},properties:function(b,a,d){var e=d.callback;e.drag=d.drag;e.proxy=d.proxy||d.drag;e.startX=a.pageX;e.startY=a.pageY;e.deltaX=b.pageX-a.pageX;e.deltaY=b.pageY-a.pageY;e.originalX=d.offset.left;e.originalY=d.offset.top;e.offsetX=b.pageX-(a.pageX-e.originalX);e.offsetY=b.pageY-(a.pageY-e.originalY);e.drop=c.flatten((d.drop||[]).slice()); e.available=c.flatten((d.droppable||[]).slice());return e},element:function(b){if(b&&(b.jquery||b.nodeType==1))return b},flatten:function(b){return f.map(b,function(a){return a&&a.jquery?f.makeArray(a):a&&a.length?c.flatten(a):a})},textselect:function(b){f(document)[b?"unbind":"bind"]("selectstart",c.dontstart).attr("unselectable",b?"off":"on").css("MozUserSelect",b?"":"none")},dontstart:function(){return false},callback:function(){}};c.callback.prototype={update:function(){h.drop&&this.available.length&& f.each(this.available,function(b){h.drop.locate(this,b)})}};h.draginit=h.dragstart=h.dragend=c})(jQuery);
	/*
 * jQuery resize event - v1.1 - 3/14/2010
 * http://benalman.com/projects/jquery-resize-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);

/**
 * jQuery mousehold plugin - fires an event while the mouse is clicked down.
 * Additionally, the function, when executed, is passed a single
 * argument representing the count of times the event has been fired during
 * this session of the mouse hold.
 *
 * @author Remy Sharp (leftlogic.com)
 * @date 2006-12-15
 * @example $("img").mousehold(200, function(i){  })
 * @desc Repeats firing the passed function while the mouse is clicked down
 * @url http://remysharp.com/2006/12/15/jquery-mousehold-event/
 *
 * @name mousehold
 * @type jQuery
 * @param Number timeout The frequency to repeat the event in milliseconds
 * @param Function fn A function to execute
 * @cat Plugin
 */

(function ($) {
    $.fn.mousehold = function(timeout, f) {
    	if (timeout && typeof timeout == 'function') {
    		f = timeout;
    		timeout = 100;
    	}
    	if (f && typeof f == 'function') {
    		var timer = 0;
    		var fireStep = 0;

    		return this.each(function() {
    			var clearMousehold = function() {
    				clearInterval(timer);
    				if (fireStep == 1) f.call(this, 1);
    				fireStep = 0;
    			};

    			$(this).mousedown(function() {
    				fireStep = 1;
    				var ctr = 0;
    				var t = this;
    				timer = setInterval(function() {
    					ctr++;
    					f.call(t, ctr);
    					fireStep = 2;
    				}, timeout);
    			}).mouseout(clearMousehold).mouseup(clearMousehold);
    		});
    	}
    };
})(jQuery);




/*
 * jSwipe - jQuery Plugin
 * http://plugins.jquery.com/project/swipe
 * http://www.ryanscherf.com/demos/swipe/
 *
 * Copyright (c) 2009 Ryan Scherf (www.ryanscherf.com)
 * Licensed under the MIT license
 *
 * $Date: 2009-07-14 (Tue, 14 Jul 2009) $
 * $version: 0.1.2
 *
 * This jQuery plugin will only run on devices running Mobile Safari
 * on iPhone or iPod Touch devices running iPhone OS 2.0 or later.
 * http://developer.apple.com/iphone/library/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html#//apple_ref/doc/uid/TP40006511-SW5
 */
(function($) {
	$.fn.swipe = function(options) {

		// Default thresholds & swipe functions
		var defaults = {
			threshold: {
				x: 30,
				y: 10
			},
			swipeLeft: function() { alert('swiped left') },
			swipeRight: function() { alert('swiped right') }
		};

		var options = $.extend(defaults, options);

		if (!this) return false;

		return this.each(function() {

			var me = $(this);

			// Private variables for each element
			var originalCoord = { x: 0, y: 0 };
			var finalCoord = { x: 0, y: 0 };

			// Screen touched, store the original coordinate
			function touchStart(event) {
				//console.log('Starting swipe gesture...')
				originalCoord.x = event.targetTouches[0].pageX;
				originalCoord.y = event.targetTouches[0].pageY;
			}

			// Store coordinates as finger is swiping
			function touchMove(event) {
			    event.preventDefault();
				finalCoord.x = event.targetTouches[0].pageX; // Updated X,Y coordinates
				finalCoord.y = event.targetTouches[0].pageY;
			}

			// Done Swiping
			// Swipe should only be on X axis, ignore if swipe on Y axis
			// Calculate if the swipe was left or right
			function touchEnd(event) {
				//console.log('Ending swipe gesture...')
				var changeY = originalCoord.y - finalCoord.y;
				if(changeY < defaults.threshold.y && changeY > (defaults.threshold.y*-1)) {
					changeX = originalCoord.x - finalCoord.x;

					if(changeX > defaults.threshold.x) {
						defaults.swipeLeft();
					}
					if(changeX < (defaults.threshold.x*-1)) {
						defaults.swipeRight();
					}
				}
			}

			// Swipe was started
			function touchStart(event) {
				//console.log('Starting swipe gesture...')
				originalCoord.x = event.targetTouches[0].pageX;
				originalCoord.y = event.targetTouches[0].pageY;

				finalCoord.x = originalCoord.x;
				finalCoord.y = originalCoord.y;
			}

			// Swipe was canceled
			function touchCancel(event) {
				//console.log('Canceling swipe gesture...')
			}

			// Add gestures to all swipable areas
			this.addEventListener("touchstart", touchStart, false);
			this.addEventListener("touchmove", touchMove, false);
			this.addEventListener("touchend", touchEnd, false);
			this.addEventListener("touchcancel", touchCancel, false);

		});
	};
})(jQuery);

/* Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * 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.
 *
 * Version: 3.0.2
 *
 * Requires: 1.2.2+
 */
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);

        $.fn.scrollbars = function() {
		return $(this).each(function() {
			//transform the selected dom elements
			var $scrollable = $(this);
			
			$scrollable.addClass("scrollable");
			
			var $scrollcontent = $('<div class="scrollcontent"></div>');
			var $scrollwrap = $('<div class="scrollwrap"></div>');
			
			$scrollable.wrapInner($scrollwrap).wrapInner($scrollcontent);
			
			//as wrapinner actually dereferences the jquery object, we need to reference here
			$scrollcontent = $scrollable.find(".scrollcontent");
			$scrollwrap = $scrollable.find(".scrollwrap");
			
			var $vscroller = $('<div class="scroller vscroller"></div>');
			var $vscrollblock = $('<div class="scrollblock"></div>');
			var $vscrollarea = $('<div class="scrollarea"></div>');
			
			var $scrolltop = $('<div class="scrollbtn scrolltop">▲</div>');
			var $scrolldown = $('<div class="scrollbtn scrolldown">▼</div>');
			
			$vscrollarea.append($vscrollblock);
			$vscroller.append($vscrollarea).append($scrolltop).append($scrolldown);
			
			var $hscroller = $('<div class="scroller hscroller"></div>');
			var $hscrollblock = $('<div class="scrollblock"></div>');
			var $hscrollarea = $('<div class="scrollarea"></div>');
			
			var $scrollleft = $('<div class="scrollbtn scrollleft">◄</div>');
			var $scrollright = $('<div class="scrollbtn scrollright">►</div>');
			
			$hscrollarea.append($hscrollblock);
			$hscroller.append($hscrollarea).append($scrollleft).append($scrollright);
			
			var $filler = $('<div class="filler"></div>');
			
			$scrollable.append($vscroller).append($filler).append($hscroller);
			
			//START
			updateScrollers();
			
			//EVENTS
			$scrollcontent.add($scrollwrap).resize(function() {
				updateScrollers();
			});
			
			//allow the directional buttons to scroll the content
			$scrolltop.mousehold(100, function() {
				$scrollcontent.scrollTop($scrollcontent.scrollTop() - 40);
				updateScrollers();
			});
			
			$scrolldown.mousehold(100, function() {
				$scrollcontent.scrollTop($scrollcontent.scrollTop() + 40);
				updateScrollers();
			});
			
			$scrollleft.mousehold(100, function() {
				$scrollcontent.scrollLeft($scrollcontent.scrollLeft() - 40);
				updateScrollers();
			});
			
			$scrollright.mousehold(100, function() {
				$scrollcontent.scrollLeft($scrollcontent.scrollLeft() + 40);
				updateScrollers();
			});
			
			//allow the block to be dragged
			$vscrollblock.drag("start", function(e, dd) {
				dd.origTop = $(this).position().top;
			}).drag(function(e, dd) {
				var maxTop = $(this).parent().innerHeight() - $(this).outerHeight();
				var newTop = Math.max(0, Math.min(maxTop, dd.origTop + dd.deltaY));
				
				$(this).css({top: newTop});
				$scrollcontent.scrollTop((newTop / maxTop) * ($scrollwrap.outerHeight() - $scrollcontent.innerHeight()));
			});
			
			$hscrollblock.drag("start", function(e, dd) {
				dd.origLeft = $(this).position().left;
			}).drag(function(e, dd) {
				var maxLeft = $(this).parent().innerWidth() - $(this).outerWidth();
				var newLeft = Math.max(0, Math.min(maxLeft, dd.origLeft + dd.deltaX));
				
				$(this).css({left: newLeft});
				$scrollcontent.scrollLeft((newLeft / maxLeft) * ($scrollwrap.outerWidth() - $scrollcontent.innerWidth()));
			});
			
			//allow the mouse wheel to scroll the content
			$scrollable.mousewheel(function(e, delta) {
				$scrollcontent.scrollTop($scrollcontent.scrollTop() - (delta*30));
				updateScrollers();
				
				return false;
			});
							
			//allow pressing the scrollbox to move the scroll bar
			$vscrollarea.mousedown(function(e) {
				var maxTop = $(this).innerHeight() - $vscrollblock.outerHeight();
				var newTop = Math.max(0, Math.min(maxTop, e.pageY - $(this).offset().top - ($vscrollblock.outerHeight()/2.0)));
				
				$vscrollblock.css({top: newTop});
				$scrollcontent.scrollTop((newTop / maxTop) * ($scrollwrap.outerHeight() - $scrollcontent.innerHeight()));
			});
			
			$hscrollarea.mousedown(function(e) {
				var maxLeft = $(this).innerWidth() - $hscrollblock.outerWidth();
				var newLeft = Math.max(0, Math.min(maxLeft, e.pageX - $(this).offset().left - ($hscrollblock.outerWidth()/2.0)));
				
				$hscrollblock.css({left: newLeft});
				$scrollcontent.scrollLeft((newLeft / maxLeft) * ($scrollwrap.outerWidth() - $scrollcontent.innerWidth()));
			});
			
			//TODO: allow swipe gestures (iPhone/iPad friendly) to control the scroll
			
			
			$scrollcontent.scroll(function() {
				updateScrollers();
			});
			
			function updateScrollers() {
				//determine the size and position of the scrollbars based on the scroll position and view size/scroll size
				var amountHeight = $scrollcontent.innerHeight() / $scrollwrap.outerHeight();
				
				if (amountHeight >= 1) {
					//if we do not need some scrollbars, then we simply set the class of the scrollable element and let css remove them
					$scrollable.addClass("no_scroll_v");
				} else {
					$scrollable.removeClass("no_scroll_v");
					
					//otherwise, do the maths
					var vscrollHeight = amountHeight * $vscrollarea.innerHeight();
					
					var availableHeight = $scrollwrap.outerHeight() - $scrollcontent.innerHeight();
					var amountTop = $scrollcontent.scrollTop() / availableHeight;
					
					var vscrollTop = amountTop * ($vscrollarea.innerHeight()-vscrollHeight);
					
					//and assign the new values
					$vscrollblock.css({top: vscrollTop, height: vscrollHeight});
				}
				
				//same but with the horizontal bar
				var amountWidth = $scrollcontent.innerWidth() / $scrollwrap.outerWidth();
					
				if (amountWidth >= 1) {
					//if we do not need some scrollbars, then we simply set the class of the scrollable element and let css remove them
					$scrollable.addClass("no_scroll_h");
				} else {
					$scrollable.removeClass("no_scroll_h");
					
					//otherwise, do the maths
					var hscrollWidth = amountWidth * $hscrollarea.innerWidth();
					
					var availableWidth = $scrollwrap.outerWidth() - $scrollcontent.innerWidth();
					var amountLeft = $scrollcontent.scrollLeft() / availableWidth;
					
					var hscrollLeft = amountLeft * ($hscrollarea.innerWidth()-hscrollWidth);
					
					//and assign the new values
					$hscrollblock.css({left: hscrollLeft, width: hscrollWidth});
				}
			};
		});
	};
})(jQuery);

