$(document).ready(function() {
  
  // convert a CSS pixel value to an integer
  String.prototype.pxToInt = function(){
    return this == "" ? 0 : parseInt(this.replace('px',''), 10);
  };
  
  // stolen from jQuery UI, this gets/sets the z-index of an element
  jQuery.fn.zIndex = function(zIndex) {
    if (zIndex !== undefined) {
      return this.css('zIndex', zIndex);
    }
    if (this.length) {
      var elem = $(this[0]), position, value;
      while (elem.length && elem[0] !== document) {
        // Ignore z-index if position is set to a value where z-index is ignored by the browser
        // This makes behavior of this function consistent across browsers
        // WebKit always returns auto if the element is positioned
        position = elem.css('position');
        if (position == 'absolute' || position == 'relative' || position == 'fixed')
        {
          // IE returns 0 when zIndex is not specified
          // other browsers return a string
          // we ignore the case of nested elements with an explicit value of 0
          // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
          value = parseInt(elem.css('zIndex'),10);
          if (!isNaN(value) && value != 0) {
            return value;
          }
        }
        elem = elem.parent();
      }
    }
    return 0;
  };
  
  // center a layer within the middle of the window
  jQuery.fn.centerLayer = function(){
    var window_scrolltop      = Math.round($(window).scrollTop());
    var window_height         = Math.round($(window).height());
    var window_width          = Math.round($(window).width());
    var layer_height          = Math.round($(this).height());
    var layer_width           = Math.round($(this).width());
    var layer_border_x        = Math.round($(this).css('border-left-width').pxToInt() + $(this).css('border-right-width').pxToInt());
    var layer_border_y        = Math.round($(this).css('border-top-width').pxToInt() + $(this).css('border-bottom-width').pxToInt());
    var layer_padding_x       = Math.round($(this).css('padding-left').pxToInt() + $(this).css('padding-right').pxToInt());
    var layer_padding_y       = Math.round($(this).css('padding-top').pxToInt() + $(this).css('padding-bottom').pxToInt());
    var layer_combined_width  = layer_width + layer_border_x + layer_padding_x;
    var layer_combined_height = layer_height + layer_border_y + layer_padding_y;
 
    $(this).css({
      'top': window_scrolltop + Math.round((window_height - layer_height)/2),
      'left': Math.round((window_width - layer_width) / 2)
    });
  };

  // create an overlay layer darkening background content
  jQuery.fn.overlay = function(modal, callback){
    var overlay = $('<div id="overlay"></div>');

    if (modal) {
      $(document).bind('focus.overlay mousedown.overlay mouseup.overlay click.overlay', function(event){
        if ($('div#overlay')) {
          return ($(event.target).zIndex() >= overlay.zIndex());
        } else {
          return true;
        }
      });
    } else {
      
      // pressing escape dismisses overlay
      $(window).bind('keyup.overlay', function(event){
        if(event.keyCode == "27"){
          $(document).trigger('dismiss.overlay');
        }
      });
      
      // clicking anywhere dismisses overlay
      overlay.bind('click.overlay', function(event){
        $(document).trigger('dismiss.overlay');
      });
      
      $(document).bind('dismiss.overlay', function(event, callback){
        overlay.fadeOut('fast', function(){
          overlay.remove();
          if (typeof(callback) == "function") callback();
        });
      });
    }

    // bind resize/scroll events, and resize the new overlay
    $(window).bind('resize.overlay scroll.overlay', function(){
      overlay.css({width: 0, height: 0}).css({width: $(document).width(), height: $(document).height()});
    });
    $(window).trigger('resize');

    $('body').append(overlay.css({width: 0, height: 0}).css({width: $(document).width(), height: $(document).height(), zIndex: 1000, display: 'none'}));
    $(overlay).fadeIn('fast', function(){
      if (typeof(callback) == "function") callback();
    });
  };
  
  // cycle between alternating classes to a set of elements, table rows or list items
  jQuery.fn.cycle = function(classes) {
    classes = classes || { even: '', odd: 'alt' };

    var parent = null;
    var children = null;
    var i = 0;
    var removed = [];      

    switch ($(this).get(0).nodeName)
    {
      case 'TABLE':
        parent = $(this);
        children = parent.children('tbody').children('tr');
        break;
      case 'UL':
      case 'OL':
        parent = $(this);
        children = parent.children('li');
        break;
      case 'DL':
        parent = $(this);
        children = parent.children('dt');
        break;
      default:
        parent = $(this).parent();
        children = $(this);
        break;
    }

    children.each(function(){
      if ($(this).css('display') == 'none') {
        removed.push({position: i, element: this});
        $(this).remove();
      }
      i++;
    });

    children.removeClass(classes.even + ' ' + classes.odd);
    children.filter(':odd').addClass(classes.odd);
    children.filter(':even').addClass(classes.even);

    jQuery.each(removed, function(i, item) {                
      var beforeElement = $(children[item.position-1]);
      if (beforeElement.length != 0) {
        beforeElement.before(item.element);
      } else {
        parent.append(item.element);
      }
    });
  };
  
  // blink a class on/off a number of times on a given element
  jQuery.fn.blink = function blink(options) {
    var options = jQuery.extend({
      speed: 150,
      count: 3,
      classname: 'on'
    }, options);
    
    if (options.count == 0) return true;
    setTimeout(function(){
      $(this).addClass(options.classname);
      setTimeout(function(){
        options.count--;
        $(this).removeClass(options.classname);
        $(this).blink(options);
      }, options.speed);
    }, options.speed);
    return true;
  };
  
});

