(function($) {



	var version = '1.4.10',

		defaults = {

			exclude: [],

			excludeWithin:[],

			offset: 0,



			// one of 'top' or 'left'

			direction: 'top',



			// jQuery set of elements you wish to scroll (for $.smoothScroll).

			//  if null (default), $('html, body').firstScrollable() is used.

			scrollElement: null,



			// only use if you want to override default behavior

			scrollTarget: null,



			// fn(opts) function to be called before scrolling occurs.

			// `this` is the element(s) being scrolled

			beforeScroll: function() {},



			// fn(opts) function to be called after scrolling occurs.

			// `this` is the triggering element

			afterScroll: function() {},

			easing: 'swing',

			speed: 400,



			// coefficient for "auto" speed

			autoCoefficent: 2

		},



		getScrollable = function(opts) {

			var scrollable = [],

				scrolled = false,

				dir = opts.dir && opts.dir == 'left' ? 'scrollLeft' : 'scrollTop';



			this.each(function() {



				if (this == document || this == window) { return; }

				var el = $(this);

				if ( el[dir]() > 0 ) {

					scrollable.push(this);

				} else {

					// if scroll(Top|Left) === 0, nudge the element 1px and see if it moves

					el[dir](1);

					scrolled = el[dir]() > 0;

					if ( scrolled ) {

						scrollable.push(this);

					}

					// then put it back, of course

					el[dir](0);

				}

			});



			// If no scrollable elements, fall back to <body>,

			// if it's in the jQuery collection

			// (doing this because Safari sets scrollTop async,

			// so can't set it to 1 and immediately get the value.)

			if (!scrollable.length) {

				this.each(function(index) {

					if (this.nodeName === 'BODY') {

						scrollable = [this];

					}

				});

			}



			// Use the first scrollable element if we're calling firstScrollable()

			if ( opts.el === 'first' && scrollable.length > 1 ) {

				scrollable = [ scrollable[0] ];

			}



			return scrollable;

		},

		isTouch = 'ontouchend' in document;



	$.fn.extend({

		scrollable: function(dir) {

			var scrl = getScrollable.call(this, {dir: dir});

			return this.pushStack(scrl);

		},

		firstScrollable: function(dir) {

			var scrl = getScrollable.call(this, {el: 'first', dir: dir});

			return this.pushStack(scrl);

		},



		smoothScroll: function(options) {

			options = options || {};

			var opts = $.extend({}, $.fn.smoothScroll.defaults, options),

				locationPath = $.smoothScroll.filterPath(location.pathname);



			this

				.unbind('click.smoothscroll')

				.bind('click.smoothscroll', function(event) {

					var link = this,

						$link = $(this),

						exclude = opts.exclude,

						excludeWithin = opts.excludeWithin,

						elCounter = 0, ewlCounter = 0,

						include = true,

						clickOpts = {},

						hostMatch = ((location.hostname === link.hostname) || !link.hostname),

						pathMatch = opts.scrollTarget || ( $.smoothScroll.filterPath(link.pathname) || locationPath ) === locationPath,

						thisHash = escapeSelector(link.hash);



					if ( !opts.scrollTarget && (!hostMatch || !pathMatch || !thisHash) ) {

						include = false;

					} else {

						while (include && elCounter < exclude.length) {

							if ($link.is(escapeSelector(exclude[elCounter++]))) {

								include = false;

							}

						}

						while ( include && ewlCounter < excludeWithin.length ) {

							if ($link.closest(excludeWithin[ewlCounter++]).length) {

								include = false;

							}

						}

					}



					if ( include ) {

						event.preventDefault();



						$.extend( clickOpts, opts, {

							scrollTarget: opts.scrollTarget || thisHash,

							link: link

						});



						$.smoothScroll( clickOpts );

					}

				});



			return this;

		}

	});



	$.smoothScroll = function(options, px) {

		var opts, $scroller, scrollTargetOffset, speed,

			scrollerOffset = 0,

			offPos = 'offset',

			scrollDir = 'scrollTop',

			aniProps = {},

			aniOpts = {},

			scrollprops = [];





		if (typeof options === 'number') {

			opts = $.fn.smoothScroll.defaults;

			scrollTargetOffset = options;

		} else {

			opts = $.extend({}, $.fn.smoothScroll.defaults, options || {});

			if (opts.scrollElement) {

				offPos = 'position';

				if (opts.scrollElement.css('position') == 'static') {

					opts.scrollElement.css('position', 'relative');

				}

			}

		}



		opts = $.extend({link: null}, opts);

		scrollDir = opts.direction == 'left' ? 'scrollLeft' : scrollDir;



		if ( opts.scrollElement ) {

			$scroller = opts.scrollElement;

			scrollerOffset = $scroller[scrollDir]();

		} else {

			$scroller = $('html, body').firstScrollable();

		}



		// beforeScroll callback function must fire before calculating offset

		opts.beforeScroll.call($scroller, opts);



		scrollTargetOffset = (typeof options === 'number') ? options :

			px ||

				( $(opts.scrollTarget)[offPos]() &&

					$(opts.scrollTarget)[offPos]()[opts.direction] ) ||

				0;



		aniProps[scrollDir] = scrollTargetOffset + scrollerOffset + opts.offset;

		speed = opts.speed;



		// automatically calculate the speed of the scroll based on distance / coefficient

		if (speed === 'auto') {



			// if aniProps[scrollDir] == 0 then we'll use scrollTop() value instead

			speed = aniProps[scrollDir] || $scroller.scrollTop();



			// divide the speed by the coefficient

			speed = speed / opts.autoCoefficent;

		}



		aniOpts = {

			duration: speed,

			easing: opts.easing,

			complete: function() {

				opts.afterScroll.call(opts.link, opts);

			}

		};



		if (opts.step) {

			aniOpts.step = opts.step;

		}



		if ($scroller.length) {

			$scroller.stop().animate(aniProps, aniOpts);

		} else {

			opts.afterScroll.call(opts.link, opts);

		}

	};



	$.smoothScroll.version = version;

	$.smoothScroll.filterPath = function(string) {

		return string

			.replace(/^\//,'')

			.replace(/(index|default).[a-zA-Z]{3,4}$/,'')

			.replace(/\/$/,'');

	};



// default options

	$.fn.smoothScroll.defaults = defaults;



	function escapeSelector (str) {

		return str.replace(/(:|\.)/g,'\\$1');

	}



})(jQuery);