(function($) {
	$.extend($.fn, {
		swapClass: function(c1, c2) {
			var c1Elements = this.filter('.' + c1);
			this.filter('.' + c2).removeClass(c2).addClass(c1);
			c1Elements.removeClass(c1).addClass(c2);
			return this;
		},
		replaceClass: function(c1, c2) {
			return this.filter('.' + c1).removeClass(c1).addClass(c2).end();
		},
		prepareBranches: function(settings) {
			this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
			return this.filter(":has(>ul)");
		},
		heightHide: function(animated, callback) {
			if (animated) {
				this.animate({ height: "hide" }, animated, callback);
			} else {
				this.hide();
				if (callback)
					this.each(callback);				
			}
		},
		heightToggle: function(animated, callback) {
			animated ?
				this.animate({ height: "toggle" }, animated, callback) :
				this.each(function(){
					jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
					if(callback)
						callback.apply(this, arguments);
				});
		},
		applyClasses: function(settings, toggler) {
			this.find("a").click( toggler );
		},
		treeview: function(settings) {
			settings = $.extend({
				cookieId: "treeview"
			}, settings);

			function serialize() {
				function binary(arg) {
					return arg ? 1 : 0;
				}
				var data = [];
				branches.each(function(i, e) {
					data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0;
				});
				$.cookie(settings.cookieId, data.join(""),{expires:7, domain: settings.domain, path: settings.path});
			}

			function deserialize() {
				var stored = $.cookie(settings.cookieId);
				if ( stored ) {
					var data = stored.split("");
					branches.each(function(i, e) {
						$(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ]();
						if (parseInt(data[i]) == 1) {$(e).replaceClass("closed","open");}
					});
				}
			}

			this.addClass("exp");

			var ul = "";
			if (settings.levelFrom) {
				for (i=0; i<settings.levelFrom; i++) {
					ul += "ul ";
				}
			}
			$(this).find(ul+"li").swapClass(CLASSES.open,CLASSES.closed);
			if (ul != "") {
				ul = "ul ";
			}

			ul2 = "";
			if (settings.levelTo) {
				for (i=settings.levelTo; i>settings.levelFrom; i--) {
					ul2 += "ul ";
				}
				$(this).find(ul2+"li").swapClass(CLASSES.closed,CLASSES.open);
			}

			var branches = this.find(ul+"li").prepareBranches(settings);

			switch(settings.persist) {
				case "cookie":
					var toggleCallback = settings.toggle;
					settings.toggle = function() {
						serialize();
						if (toggleCallback) {
							toggleCallback.apply(this, arguments);
						}
					};
					deserialize();
					break;
				case "location":
					var current = this.find("a").filter(function() { return this.href.toLowerCase() == location.href.toLowerCase(); });
					if ( current.length ) {
						current.addClass("selected").parents("ul, li").add( current.next() ).show();
					}
					break;
			}

			branches.applyClasses(settings, toggler);

			function toggler() {
				$(this)
					.parent()
					.swapClass( CLASSES.open, CLASSES.closed )
					.find(">ul")
					.heightToggle( settings.animated, settings.toggle );
				if ( settings.unique ) {
					$(this).parent()
						.siblings()
						.replaceClass( CLASSES.open, CLASSES.closed )
						.find( ">ul" )
						.heightHide( settings.animated, settings.toggle );
				}
				if ($(this).parent().find(">ul").length > 0) {
					return false;
				}
			}
		}
	});
	var CLASSES = $.fn.treeview.classes = {
		open: "open",
		closed: "closed",
		last: "last"
	};
})(jQuery);
