/*

 * jQuery.appear

 * https://github.com/bas2k/jquery.appear/

 * http://code.google.com/p/jquery-appear/

 * http://bas2k.ru/

 *

 * Copyright (c) 2009 Michael Hixson

 * Copyright (c) 2012-2014 Alexander Brovikov

 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)

 */

(function ($) {
	$.fn.appear = function (fn, options) {
		var settings = $.extend(
			{
				//arbitrary data to pass to fn

				data: undefined,

				//call fn only on the first appear?

				one: true,

				// X & Y accuracy

				accX: 0,

				accY: 0,
			},
			options
		);

		return this.each(function () {
			var t = $(this);

			//whether the element is currently visible

			t.appeared = false;

			if (!fn) {
				//trigger the custom event

				t.trigger("appear", settings.data);

				return;
			}

			var w = $(window);

			//fires the appear event when appropriate

			var check = function () {
				//is the element hidden?

				if (!t.is(":visible")) {
					//it became hidden

					t.appeared = false;

					return;
				}

				//is the element inside the visible window?

				var a = w.scrollLeft();

				var b = w.scrollTop();

				var o = t.offset();

				var x = o.left;

				var y = o.top;

				var ax = settings.accX;

				var ay = settings.accY;

				var th = t.height();

				var wh = w.height();

				var tw = t.width();

				var ww = w.width();

				if (
					y + th + ay >= b &&
					y <= b + wh + ay &&
					x + tw + ax >= a &&
					x <= a + ww + ax
				) {
					//trigger the custom event

					if (!t.appeared) t.trigger("appear", settings.data);
				} else {
					//it scrolled out of view

					t.appeared = false;
				}
			};

			//create a modified fn with some additional logic

			var modifiedFn = function () {
				//mark the element as visible

				t.appeared = true;

				//is this supposed to happen only once?

				if (settings.one) {
					//remove the check

					w.unbind("scroll", check);

					var i = $.inArray(check, $.fn.appear.checks);

					if (i >= 0) $.fn.appear.checks.splice(i, 1);
				}

				//trigger the original fn

				fn.apply(this, arguments);
			};

			//bind the modified fn to the element

			if (settings.one) t.one("appear", settings.data, modifiedFn);
			else t.bind("appear", settings.data, modifiedFn);

			//check whenever the window scrolls

			w.scroll(check);

			//check whenever the dom changes

			$.fn.appear.checks.push(check);

			//check now

			check();
		});
	};

	//keep a queue of appearance checks

	$.extend($.fn.appear, {
		checks: [],

		timeout: null,

		//process the queue

		checkAll: function () {
			var length = $.fn.appear.checks.length;

			if (length > 0) while (length--) $.fn.appear.checks[length]();
		},

		//check the queue asynchronously

		run: function () {
			if ($.fn.appear.timeout) clearTimeout($.fn.appear.timeout);

			$.fn.appear.timeout = setTimeout($.fn.appear.checkAll, 20);
		},
	});

	//run checks when these methods are called

	$.each(
		[
			"append",
			"prepend",
			"after",
			"before",
			"attr",

			"removeAttr",
			"addClass",
			"removeClass",
			"toggleClass",

			"remove",
			"css",
			"show",
			"hide",
		],
		function (i, n) {
			var old = $.fn[n];

			if (old) {
				$.fn[n] = function () {
					var r = old.apply(this, arguments);

					$.fn.appear.run();

					return r;
				};
			}
		}
	);
})(jQuery);
