/*
   Contains utility functions for manipulating dom elements
   where there is no easy css solution.
*/
(function($){
   // useful for floated divs that expand dynamically
   // especially with a visual style like a border applied to each
   $.fn.equalHeight = function (options) {
      options = options || {};
      var highest = 0;
      var elements = $(this).find(options.selector || '> div');
      elements.each(function(i, e){
         if($(e).height() > highest)
            highest = $(e).height();
      });
      elements.css({'height': highest + 'px'});
   }
   
   // not all browsers support css min-height/width
   $.fn.minimumHeight = function (options) {
      options = options || {};
      var min = options.min || 50;
      var el = options.selector ? $(this).find(options.selector) : $(this);
      el.each(function(i, e){
         if($(e).height() < min)
            $(e).css({'height': min + 'px'});
      });
   }
   $.fn.minimumWidth = function (options) {
      options = options || {};
      var min = options.min || 50;
      var el = options.selector ? $(this).find(options.selector) : $(this);
      el.each(function(i, e){
         if($(e).width() < min)
            $(e).css({'width': min + 'px'});
      });
   }
   
   // this will take a ul with floated li's and center them horizontally
   // If they are too large for the container, it will try to reduce the
   // padding and font size
   $.fn.centerFloatedLIs = function (options) {
      /* ======= setup ======= */
      options = options || {};
      var bound = options.bound || $(this).width();
      
      // A bug exists in Firefox for Mac OS X relating to bound.
      var user = window.navigator.userAgent || '';
      if(user.indexOf('Mac') && user.indexOf('Firefox'))
         bound --;
         
      // children are the DOM nodes being positioned
      var children = $(this).find('> li');
      var padding_at_bounds = true;
      if(options.padding_at_bounds == false)
         padding_at_bounds = !padding_at_bounds;
      
      // targets are the DOM nodes that get manipulated if the children don't fit
      var targets = children;
      if(options.target)
         targets = $(this).find(options.target);
      // The user can optionally specify padding and font target selectors
      var font_targets = targets;
      var padding_targets = targets;
      if(options.padding_target)
         padding_targets = $(this).find(options.padding_target);
      if(options.font_target)
         font_targets = $(this).find(options.font_target);
      
      // If we don't have children or targets, fail silently
      if(children.length == 0 || targets.length == 0 || font_targets.length == 0 || padding_targets.length == 0)
         return;
      
      // These limits determine how far we can reduce the padding and font size before we give up
      var pad_limit = options.pad_limit || 0.5; // 50%
      var font_limit = options.font_limit || 0.5; // 50%
      if(pad_limit < 0 ) pad_limit = 0; if(pad_limit > 1 ) pad_limit = 1;
      if(font_limit < 0 ) font_limit = 0; if(font_limit > 1 ) font_limit = 1;
      
      /* ======= resize and reposition ======= */
      var pad = {
         // take padding from the last element, in case we've run this function
         // already with the padding_at_bounds property set to false
         orig: $(padding_targets[padding_targets.length - 1]).css('padding-left'),
         current: 1,
         hit: false
      };
      var font = {
         orig: $(font_targets[0]).css('font-size'),
         current: 1,
         hit: false
      };
      removePaddingAtBounds();
      
      var decrement = 0.005; // 0.5%
      // flooring any remainer, better to come up an iteration short
      var max_iterations = Math.floor(1 / decrement);
      
      // Attempt to reduce the size of the menu items if they are too large
      var i = max_iterations;
      while(cW(children) > bound && i > 0){
         // try padding first, then font size
         if(!pad.hit)
            removePadding();
         if(pad.hit && !font.hit)
            removeFontSize();
         // Once i hits 0 we've failed to shrink enough, probably the user adding way too many menus
         i--;
      }
      
      var finalWidth = cW(children);
      // If the menu is smaller than the container bounds, we want to insert space until they span the containers width
      if(finalWidth < bound){
         // We want to distribute the remaining pixels between the menus as best we can 
         var diff = bound - finalWidth;
         var dc = 0;
         var idx = 0;
         // flipflop is used to flip between the leftmost and rightmost item plus or minus dc
         var flipflop = true;
         for(diff; diff > 0;){
            idx = dc % children.length;
            var e, cLft, cRgt, pad;
            if(flipflop){
               e = $(children[idx]);
               if(idx > 0) {
                  pad = parseInt(e.css('padding-left')) + 1;
                  e.css('padding-left', pad + 'px');
                  diff--;
               }
            } else {
               var e = $(children[children.length - 1 - idx]);
               if(idx > 0) {
                  pad = parseInt(e.css('padding-right')) + 1;
                  e.css('padding-right', pad + 'px');
                  diff--;
               }
               dc++;
            }
            flipflop = !flipflop;
         }
      }
      
      
      /* ======= utility functions ======= */
      
      // calculates the outerWidth of all the menu items
      function cW(c){
         var result = 0;
         c.each(function(idx, child){ result += $(child).outerWidth(); });
         return result;
      }
      
      // reduce the padding of every menu item
      // unless we've hit the pad_limt
      function removePadding(){
         if(pad.current <= pad_limit){
            pad.hit = true;
            return;
         }
         pad.current -= decrement;
         if(pad.current <= pad_limit)
            pad.current = pad_limit;
         var newPad = Math.floor(parseInt(pad.orig) * pad.current);
         padding_targets.css('padding-right', newPad + 'px');
         padding_targets.css('padding-left', newPad + 'px');
         removePaddingAtBounds();
      }
      // If padding_at_bounds == false, it will remove:
      // padding-left from the first-child
      // padding-right from the last-child
      function removePaddingAtBounds(){
         if(!padding_at_bounds){
            $(padding_targets[0]).css('padding-left', '0px');
            $(padding_targets[padding_targets.length - 1]).css('padding-right', '0px');
         }
      }
      // reduce the font size of every menu item
      // unless we've hit the font_limt
      function removeFontSize(){
         if(font.current <= font_limit){
            font.hit = true;
            return;
         }
         font.current -= decrement;
         if(font.current <= font_limit)
            font.current = font_limit;
         var newSize = Math.floor(parseInt(font.orig) * font.current);
         font_targets.css('font-size', newSize + 'px');
      }
   }
   
})(jQuery);

