;(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();
        },
        hoverClass: function(className) {
            className = className || "hover";
            return this.hover(function() {
                $(this).addClass(className);
            }, function() {
                $(this).removeClass(className);
            });
        },
        heightToggle: function(animated, callback) {
            animated ?
				this.animate({ height: "toggle" }, animated, callback) :
				this.each(function() {
				    jQuery(this)[jQuery(this).is(":hidden") ? "show" : "hide"]();
				    if (callback)
				    {
				        $(this).unbind("click");
				        callback.apply(this, arguments);
				    }
				});
        },
        heightHide: function(animated, callback) {
            if (animated) {
                this.animate({ height: "hide" }, animated, callback);
            } else {
                this.hide();
                if (callback)
                {
                    $(this).unbind("click");
                    this.each(callback);
                }
            }
        },
        prepareBranches: function(settings) {
            if (!settings.prerendered) {
                this.filter(":last-child:not(ul)").addClass(CLASSES.last);
                this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
            }
            return this.filter(":has(>ul)");
        },
        prepareAllBranches: function(settings) {
            if (!settings.prerendered) {
                this.filter("li:last-child").addClass(CLASSES.last);
                if (settings.isupdate) {
                    this.not(":last-child").filter(":has(>ul:hidden)").addClass(CLASSES.closed);
                    this.not(":last-child").not(":has(>ul)").removeClass(CLASSES.collapsable).addClass(CLASSES.closed);
                    this.filter(("." + CLASSES.expandable) + ":not(." + CLASSES.open + ")").find(">ul").hide();
                    this.filter(("." + CLASSES.closed) + ":not(." + CLASSES.open + "):not(." + CLASSES.collapsable + ")").find(">ul").hide();
                }
                else {
                    this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
                }
            }
            return this.filter(":has(>span)");
        },
        applyClasses: function(settings, toggler) {
            if (!settings.isupdate) {
                this.filter(":not(:has(>a))").find("span").unbind("click").click(function(event) {
                    var callback = settings.select;
                    callback.apply($(this).parent());
                }).bind('contextmenu', function(event){var callback = settings.rightclick;if(callback){callback.apply($(this).parent());}}).hoverClass();
            }
            if (!settings.prerendered) {
                this.filter(":has(>ul:hidden)")
						.addClass(CLASSES.expandable)
						.replaceClass(CLASSES.last, CLASSES.lastExpandable);


                
                this.filter(":has(>ul)").not(":has(>ul:hidden)")
						.addClass(CLASSES.collapsable)
						.replaceClass(CLASSES.last, CLASSES.lastCollapsable);
                if (settings.isupdate) {
                    this.filter(":has(>ul)").find("div").remove();
                }
                this.filter(":has(>ul)").prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea).each(function() {
                    var classes = "";
                    $.each($(this).parent().attr("class").split(" "), function() {
                        classes += this + "-hitarea ";
                    });
                    $(this).addClass(classes);
                });
            }
            this.find("div." + CLASSES.hitarea).unbind("click").click(toggler);
        },
        treeview: function(settings) {
            settings = $.extend({
                cookieId: "treeview"
            }, settings);

            if (settings.add) {
                return this.trigger("add", [settings.add]);
            }
            if (settings.update) {
                return this.trigger("update", [settings.update]);
            }

            if (settings.toggle) {
                var callback = settings.toggle;
                settings.toggle = function() {
                    return callback.apply($(this).parent()[0], arguments);
                };
            }
            function toggler() {
                $(this)
					.parent()
					.find(">.hitarea")
						.swapClass(CLASSES.collapsableHitarea, CLASSES.expandableHitarea)
						.swapClass(CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea)
					.end()
					.swapClass(CLASSES.collapsable, CLASSES.expandable)
					.swapClass(CLASSES.lastCollapsable, CLASSES.lastExpandable)
					.find(">ul")
					.heightToggle(settings.animated, settings.toggle);
                if (settings.unique) {
                    $(this).parent()
						.siblings()
						.find(">.hitarea")
							.replaceClass(CLASSES.collapsableHitarea, CLASSES.expandableHitarea)
							.replaceClass(CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea)
						.end()
						.replaceClass(CLASSES.collapsable, CLASSES.expandable)
						.replaceClass(CLASSES.lastCollapsable, CLASSES.lastExpandable)
						.find(">ul")
						.heightHide(settings.animated, settings.toggle);
                }
            }
            //this.addClass("treeview");
            var branches = this.find("li").prepareAllBranches(settings);

            branches.applyClasses(settings, toggler);

            return this.bind("add", function(event, branches) {
                $(branches).prev()
					.removeClass(CLASSES.last)
					.removeClass(CLASSES.lastCollapsable)
					.removeClass(CLASSES.lastExpandable)
				.find(">.hitarea")
					.removeClass(CLASSES.lastCollapsableHitarea)
					.removeClass(CLASSES.lastExpandableHitarea);
                settings.isupdate = false;
                $(branches).find("li").andSelf().prepareAllBranches(settings).applyClasses(settings, toggler);
            }).bind("update", function(event, branches) {
                $(branches).each(function() {
                    $(this)
				    .removeClass(CLASSES.expandable)
					.removeClass(CLASSES.collapsable)
					.removeClass(CLASSES.last)
					.removeClass(CLASSES.lastCollapsable)
					.removeClass(CLASSES.lastExpandable)

				.find(">.hitarea")
					.removeClass(CLASSES.lastCollapsableHitarea)
					.removeClass(CLASSES.lastExpandableHitarea)
					.removeClass(CLASSES.expandableHitarea)
					.removeClass(CLASSES.collapsableHitarea)
					.removeClass(CLASSES.hitarea);
                });
                settings.isupdate = true;
                $(branches).find("li").andSelf().prepareAllBranches(settings).applyClasses(settings, toggler);
            });
        }
    });
    var CLASSES = $.fn.treeview.classes = {
        open: "open",
        closed: "closed",
        expandable: "expandable",
        expandableHitarea: "expandable-hitarea",
        lastExpandableHitarea: "lastExpandable-hitarea",
        collapsable: "collapsable",
        collapsableHitarea: "collapsable-hitarea",
        lastCollapsableHitarea: "lastCollapsable-hitarea",
        lastCollapsable: "lastCollapsable",
        lastExpandable: "lastExpandable",
        last: "last",
        hitarea: "hitarea"
    };
    $.fn.Treeview = $.fn.treeview;

})(jQuery);
