;(function ( $, window, document, undefined ) {
	var pluginName = "mutationObserver",
		defaults = {};

	function Plugin( element, options ) {
		this.element = element;
		this.options = $.extend( {}, defaults, options) ;
		this._defaults = defaults;
		this._name = pluginName;
		this._callbacks = [];

		this.init();
	}

	Plugin.prototype = {

		init: function() {
			var self = this;
			this.observer = new MutationObserver(function(mutations) {
				mutations.forEach(function(mutation) {
					self._callbacks.forEach(function(callbackFn) {
						callbackFn(mutation);
					});
				});
			});

			this.observer.observe(this.element, {
				attributes: true,
				childList: true,
				characterData: true
			});
		},

		add: function(callbackFn) {
			this._callbacks.push(callbackFn);
		},

		remove: function(callbackFn) {
			var callbackIndex = this._callbacks.indexOf(callbackFn);
			if (callbackIndex >= 0) {
				this._callbacks.splice(callbackIndex, 1);
			}
		},

		callbacks: function(callbackFn) {
			if (callbackFn && callbackFn.length >= 0) {
				// used as a setter
				this._callbacks = callbackFn;
			} else {
				// used as a getter for the first element
				return this._callbacks;
			}
		},

		destroy: function() {
			this.observer.disconnect();
			this.observer = null;
			$.removeData(this.element, pluginName);
		}

	};

	// A really lightweight plugin wrapper around the constructor,
	// preventing against multiple instantiations
	$.fn[pluginName] = function ( options ) {
		var result,
			restArgs = Array.prototype.slice.call(arguments, 1);
		this.each(function () {
			var instance = $.data(this, "plugin_" + pluginName);
			if (!instance) {
				instance = new Plugin( this, options );
				$.data(this, "plugin_" + pluginName, instance);
			}

			if (typeof options === "function") {
				instance.add(options);
			} else if (typeof options === "string" && // method name
				options[0] !== "_" && // protect private methods
				typeof instance[options] === "function") {
				result = instance[options].apply(instance, restArgs);
				if (result !== undefined) {
					return false; // break the $.fn.each() iteration
				}
			}
		});
		return result !== undefined ? result : this;
	};

})( jQuery, window, document );
