﻿(function($) {
	$.fn.pan = function(options) {
		var opts = options.constructor == String || options.constructor == Boolean ? options : $.extend({}, $.fn.pan.defaults, options);
		return this.each(function() {
			var _fe = this;
			var _frame = $(_fe);

			var o = new Object();
			if (opts.constructor == String || opts.constructor == Boolean) {
				switch (opts) {
					case "start", true:
						_frame.data("stop.pan", false);
						break;
					case "stop", false:
						_frame.data("stop.pan", true);
						break;
				}
				o = _frame.data("options.pan");
				o = o === undefined ? $.extend({}, $.fn.pan.defaults, options) : o;
			}
			else {
				o = opts;
				_frame.data("options.pan", o);
				_frame.data("stop.pan", false);
			}

			var _children = _frame.children(o.children);
			_children.each(function() {
				var _pe = this;
				var _pan = $(_pe);
				var co = o;

				var _parallax = new Array();
				$.each(co.parallax, function() {
					_parallax.push({
						element: $(this.selector),
						offset: this.offset
					})
				});

				var _step = null;
				var _delay = null;

				var f_width = null;
				var f_left = null;

				var p_width = null;
				var p_left = null;

				var _diff = null;
				var _ratio = null;

				// Initialiser
				function init() {
					clearTimer();

					_pan.data("timeout.pan", null);
					_pan.data("mousemove.pan", null);
					_pan.data("die.pan", false);

					_step = co.step;
					_delay = co.delay;

					f_width = Number(_frame.width());
					f_left = Number(_frame.offset().left);

					p_width = Number(_pan.width());
					p_left = Number(_pan.position().left);

					_diff = p_width - f_width;
					_ratio = _diff / f_width;

					_frame.bind("mousemove.pan", frame_mousemove);
				};

				function dispose() {
					_frame.unbind("mousemove.pan");
					clearTimer();

					_pan.data("die.pan", true);

					_step = null;
					_delay = null;

					f_width = null;
					f_left = null;

					p_width = null;
					p_left = null;

					_diff = null;
					_ratio = null;
				};

				// Helper Methods
				function getOffset(value) {
					if (_ratio == null)
						init();

					if (!isNaN(_ratio))
						return -Math.floor(value * _ratio);
					else
						throw "Ratio has not been set.";
				};

				function getLeft(x) {
					return getOffset(x - f_left);
				};

				function move() {
					if (_pan.data("die.pan") != true) {
						var panMouse = _pan.data("mousemove.pan");
						if (panMouse != null) {
							p_left = _pan.position().left;

							var left = getLeft(panMouse);
							var step = Math.abs(p_left - left);
							step = step < _step ? step : _step;

							if (step > 0 && (left <= (p_left - step) || left >= (p_left + step))) {
								var newLeft = p_left + (left > p_left ? step : -step);

								if (newLeft <= 0 && newLeft >= -_diff) {
									p_left = newLeft;
									_pan.css("left", newLeft + "px");

									// Parallax
									var ratio = newLeft / f_width;
									$.each(_parallax, function() {
										var offset = this.offset * ratio;
										this.element.css("margin-left", offset + "px");
									});
								}

								setTimer();
							}
							else
								setTimer();
						}
					}
				};

				function clearTimer() {
					var panTimeout = _pan.data("timeout.pan");

					if (panTimeout != null)
						clearTimeout(panTimeout);
				};

				function setTimer() {
					clearTimer();
					_pan.data("timeout.pan", setTimeout(move, _delay));
				};

				function setMouse(x) {
					_pan.data("mousemove.pan", x);
				};

				// Events
				function frame_mousemove(evt) {
					setMouse(evt.clientX);

					if (_pan.data("timeout.pan") == null)
						move();
				};

				if (_frame.data("stop.pan") == false)
					init();
				else
					dispose();
			});
		});
	};

	$.fn.pan.defaults = {
		step: 4,
		delay: 5,
		children: ".pan",
		parallax: new Array()
	};
})(jQuery);