/* R2 mod
 * Treeview 1.4 - jQuery plugin to hide and show branches of a tree
 * 
 * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
 * http://docs.jquery.com/Plugins/Treeview
 *
 * Copyright (c) 2007 Jörn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.treeview.js 4684 2008-02-07 19:08:06Z joern.zaefferer $
 *
 */

;
(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)
               callback.apply(this, arguments);
         });
      },
      heightHide : function(animated, callback) {
         if (animated) {
            this.animate( {
               height : "hide"
            }, animated, callback);
         } else {
            this.hide();
            if (callback)
               this.each(callback);
         }
      },
      prepareBranches : function(settings) {
         if (!settings.prerendered) {
            // mark last tree items
            this.filter(":last-child:not(ul)").addClass(CLASSES.last);
            // collapse whole tree, or only those marked as closed, anyway
            // except those marked as open
            this.filter(
                  (settings.collapsed ? "" : "." + CLASSES.closed) + ":not(."
                        + CLASSES.open + ")").find(">ul").hide();
         }
         // return all items with sublists
         return this.filter(":has(>ul)");
      },
      applyClasses : function(settings, toggler) {
         // R2 removed:
      // this.filter(":has(>ul):not(:has(>a))").find(">span").click(function(event)
      // {
      // toggler.apply($(this).next());
      // }).add( $("a", this) ).hoverClass();

      // R2 added:
      this.filter("li").find("span").hoverClass();

      if (!settings.prerendered) {
         // handle closed ones first
         this.filter(":has(>ul:hidden)")
               .addClass(CLASSES.expandable)
               // .replaceClass(CLASSES.last, CLASSES.lastCollapsable);
               .replaceClass(CLASSES.last, CLASSES.lastExpandable);
/*
 * [+-] fixes
 * @author Хатламаджиян Виталий
 * @date Fri Feb 12 11:07:21 MSK 2010
 */
         if ("undefined" != typeof(settings.persist_id)) {
            var node = jQuery('#' + settings.persist_id).parent();
            do {
               node.filter(':has(>ul.folder)')
                   .addClass(CLASSES.collapsable)
                   .removeClass(CLASSES.expandable);
               if (node.parent().parent().is('li')) {
                  node = node.parent().parent();
               } else {
                  node.filter(':has(>ul.folder)')
                      .addClass(CLASSES.lastCollapsable)
                      .removeClass(CLASSES.last)
                      .removeClass(CLASSES.lastExpandable);
                  break;
               }
            } while (true);
         }
/* end [+-] fixes */

         // handle open ones
         this.not(":has(>ul:hidden)")
             .addClass(CLASSES.collapsable)
             .replaceClass(CLASSES.last, CLASSES.lastCollapsable);

         // create hitarea
         this.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);
         });
      }

      // apply event to hitarea
      this.find("div." + CLASSES.hitarea).click(toggler);
   },
   treeview : function(settings) {

      settings = $.extend( {
         cookieId : "treeview"
      }, settings);

      if (settings.add) {
         return this.trigger("add", [ settings.add ]);
      }

      if (settings.toggle) {
         var callback = settings.toggle;
         settings.toggle = function() {
            return callback.apply($(this).parent()[0], arguments);
         };
      }

      // factory for treecontroller
      function treeController(tree, control) {
         // factory for click handlers
         function handler(filter) {
            return function() {
               // reuse toggle event handler, applying the elements to toggle
               // start searching for all hitareas
               toggler.apply($("div." + CLASSES.hitarea, tree).filter(
                     function() {
                        // for plain toggle, no filter is provided, otherwise we
                        // need to check the parent element
                        return filter ? $(this).parent("." + filter).length
                              : true;
                     }));
               return false;
            };
         }
         // click on first element to collapse tree
         $("a:eq(0)", control).click(handler(CLASSES.collapsable));
         // click on second to expand tree
         $("a:eq(1)", control).click(handler(CLASSES.expandable));
         // click on third to toggle tree
         $("a:eq(2)", control).click(handler());
      }

      // handle toggle event
      function toggler() {
         $(this).parent()
         // swap classes for hitarea
               .find(">.hitarea").swapClass(CLASSES.collapsableHitarea,
                     CLASSES.expandableHitarea).swapClass(
                     CLASSES.lastCollapsableHitarea,
                     CLASSES.lastExpandableHitarea).end()
               // swap classes for parent li
               .swapClass(CLASSES.collapsable, CLASSES.expandable).swapClass(
                     CLASSES.lastCollapsable, CLASSES.lastExpandable)
               // find child lists
               .find(">ul")
               // toggle them
               .heightToggle(settings.animated, settings.toggle);
         if (settings.unique) {
            $(this).parent().siblings()
            // swap classes for hitarea
                  .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);
         }
      }

      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(""));
      }

      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"]();
            });
         }
      }

      // add treeview class to activate styles
      this.addClass("treeview");

      // prepare branches and find all tree items with child lists
      var branches = this.find("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;
      // R2 added:
      case "id":
      var current = $('#' + settings.persist_id);
      if (current.length) {
         current.addClass("selected").parents("ul, li").add(current.next())
               .show();
      }
      break;
      }

      branches.applyClasses(settings, toggler);

      // if control option is set, create the treecontroller and show it
      if (settings.control) {
         treeController(this, settings.control);
         $(settings.control).show();
      }

      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);
         $(branches).find("li").andSelf().prepareBranches(settings)
               .applyClasses(settings, toggler);
      });
   }
   });

   // classes used by the plugin
   // need to be styled via external stylesheet, see first example
   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"
   };

   // provide backwards compability
   $.fn.Treeview = $.fn.treeview;

})(jQuery);