var largeLoadingSpinnerSrc = '/images/spinners/loading_large.gif';
var largeLoadingSpinnerImg = '<img src="'+largeLoadingSpinnerSrc+'" alt="loading" class="spinner" style="display:inline;" />';
var largeLoadingSpinnerHtml = '<div style="text-align:center;">'+largeLoadingSpinnerImg+'</div>';

var mediumLoadingSpinnerSrc = '/images/spinners/loading_medium.gif';
var mediumLoadingSpinnerImg = '<img src="'+mediumLoadingSpinnerSrc+'" alt="loading" class="spinner" style="display:inline;" />';
var mediumLoadingSpinnerHtml = '<div style="text-align:center;">'+mediumLoadingSpinnerImg+'</div>';

var buttonSpinnerSrc = '/images/spinners/button_spinner.gif';
var buttonSpinnerImg = '<img src="'+buttonSpinnerSrc+'" alt="loading" class="spinner" style="display:inline;" />';
var buttonSpinnerHtml = '<div style="text-align:center;">'+buttonSpinnerImg+'</div>';

function s4() {
  return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function guid() {
  return (s4()+s4()+'-'+s4()+'-'+s4()+'-'+s4()+'-'+s4()+s4()+s4());
}

// TODO Would like to do Object.prototype.propCount but that causes an error of
// 'G.replace' does not exist in firebug for some reason?
//Object.prototype.propCount = function() {
function propCount(obj) {
  var count = 0;
  for (k in obj) if (obj.hasOwnProperty(k)) count++;
  return count;
}


// Provide bind function to allow evaluating against this within a function
jQuery.bind = function() {
  var func = arguments[0] || null;
  var obj = arguments[1] || this;
  var args = jQuery.grep(arguments, function(v,i) { return i > 1; });

  return function() {
    return func.apply(obj, arguments);
  }
}

jQuery.callIf = function() {
  var cond = arguments[0];
  var func = arguments[1];
  var args = jQuery.grep(arguments, function(v,i) { return i > 1; });

  if (typeof(cond) != 'undefined' && cond)
    return function() { func(arguments); }
  else
     return function() {;}
}

String.prototype.beginsWith = function(t,i) {
  if (i==false) {
    return (t == this.substring(0, t.length));
  } else {
    return (t.toLowerCase() == this.substring(0, t.length).toLowerCase());
  }
}

Date.prototype.stdTimezoneOffset = function() {
  var jan = new Date(this.getFullYear(), 0, 1);
  var jul = new Date(this.getFullYear(), 6, 1);
  return Math.min(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
Date.prototype.dst = function() {
  return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

Array.prototype.find = function(searchStr) {
  var returnArray = false;
  for (var i=0; i<this.length; i++) {
    if (typeof(searchStr) == 'function') {
      if (searchStr.test(this[i])) {
        if (!returnArray) { returnArray = [] }
        returnArray.push(i);
      }
    } else {
      if (this[i] == searchStr) {
        if (!returnArray) { returnArray = [] }
        returnArray.push(i);
      }
    }
  }
  return returnArray;
}

// This will call func for retries with retryDelay in between;
//  the signature of the function must be func(oArgs) and it must return true/false
var retryWorker = function() {
  var _defaultRetryDelay = 1000;
  var _defaultRetries = 0;

  function apiFailOnException(func, oArgs) {
    // Support robust error checking so this can be used in lots of places
    // Set retry values; override if specified
    var retries = _defaultRetries;
    var retryDelay = _defaultRetryDelay;
    if (typeof(oArgs) != 'undefined' && oArgs != null && typeof(oArgs.retries) != 'undefined' && oArgs.retries != null)
      retries = oArgs.retries;
    if (typeof(oArgs) != 'undefined' && oArgs != null && typeof(oArgs.retryDelay) != 'undefined' && oArgs.retryDelay != null)
      retryDelay = oArgs.retryDelay;

    // Set functions, override if specified
    var success = null;
    var failure = null;
    if (typeof(oArgs) != 'undefined' && oArgs != null && typeof(oArgs.success) != 'undefined' && oArgs.success != null)
      success = oArgs.success;
    if (typeof(oArgs) != 'undefined' && oArgs != null && typeof(oArgs.failure) != 'undefined' && oArgs.failure != null)
      failure = oArgs.failure;

    // Make an attempt to call the function
    var tryAgain = false;
    try { func(); }
    catch (e) { tryAgain = true; logError(e); }

    // Check result to determine action
    if (!tryAgain) {
      if (success != null) success();
    } else {
      // Check to see if we have retries specified and left
      if (retries > 0) {
        logDebug("Retrying failed function");
        retries--;
        // FIXME Make this retry work?
        setTimeout(jQuery.bind(function() { call(func, {
          retries: retries, retryDelay: retryDelay,
          success: success, failure: failure
        }); }, this), retryDelay);
      } else { // Out of retries, report failure to join
        logError('Failed function: ' + func + ' ('+retries+' retries)');
        if (failure != null) failure();
      }
    }
  }
  // API public
  return{
    failOnException : function(func, oArgs) { apiFailOnException(func, oArgs); }
  };
}();

function dateDaysFromNow(days) {
  var today = new Date();
  today.setTime(today.getTime() + days * 24 * 60 * 60 * 1000);
  return today;
}

function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

function addUnloadEvent(func) {
  var oldonunload = window.onunload;
  if (typeof window.onunload != 'function') {
    window.onunload = func;
  } else {
    window.onunload = function() {
      if (oldonunload) {
        oldonunload();
      }
      func();
    }
  }
}

// TODO Would like to extend console to have an errorAJAX function, didn't seem to work?
function consoleErrorAJAX(XMLHttpRequest, textStatus, errorThrown) {
  if (XMLHttpRequest != null) logError('XMLHttpRequest, status='+XMLHttpRequest.status+', statusText='+XMLHttpRequest.statusText+', responseText='+XMLHttpRequest.responseText);
  if (textStatus != null) logError('textStatus= '+textStatus);
  if (errorThrown != null) logError('errorThrown= '+errorThrown);
}

(function($) {
  $.fn.scrollToTop = function() {
    this.each(function() {
      $(this).scrollTo({location: 0});
    });
  }
  $.fn.scrollToBottom = function() {
    this.each(function() {
      $(this).scrollTo({location: this.scrollHeight});
    });
  }
  $.fn.scrollTo = function(options) {
    var settings = $.extend({
      location: 0,
      duration: 1000
    }, options);

    this.each(function() {
      $(this).animate({
        scrollTop: settings.location
        }, settings.duration);
    });
  }

  $.fn.listSlideIn = function(options) {
    // Get an extend options
    var opts = $.extend({}, $.fn.listSlideIn.defaults, options);

    // iterate and reformat each matched element
    var lastIndex = this.length - 1;
    return this.each(function(index) {
      var $this = $(this);
      var initialOffset = opts.initialDelay ? 1 : 0;
      setTimeout(function() {
        $this.slideDown('normal', function() {
          if ((index == lastIndex) && opts.complete) opts.complete();
        });
      }, (opts.delay * (index+initialOffset)));
    });
  }
  $.fn.listSlideIn.defaults = {
    delay:            3000, // ms
    initialDelay:     false,
    complete:         null
  }

  $.fn.ajaxLoad = function(url, data, complete) {
    // Get the actual complete function
    var actualComplete = complete;
    if (typeof(data) == "function") actualComplete = data;

    // Fade out this div, replace with loading an fade in, then load
    $(this).fadeOut('normal', function() {
      $(this).html(largeLoadingSpinnerHtml);
      jQuery('#standard-layout-content-box > .content-box').fadeIn('normal');
      jQuery('#standard-layout-content-box > .content-box').load(url, data, function(responseText, textStatus, XMLHttpRequest) {
        if (SWFAddress) SWFAddress.setValue(url);
        if (actualComplete) {
          actualComplete(responseText, textStatus, XMLHttpRequest);
        } else if (textStatus == 'error') {
          // If there was no complete method and an error print out a div
          jQuery(this).html('<div class="error">There was an error with the request, please try again.</div>');
        }
      });
    });
  }

  $.fn.showButtonSpinner = function(options) {
    // Get an extend options
    var opts = $.extend({}, $.fn.showButtonSpinner.defaults, options);

    // Iterate
    return this.each(function() {
      var $this = $(this);

      // Disable button so it isn't clickable again
      $this.addClass('ui-state-disabled').disable();
      // TODO Remove the hover css stuff

      // Adjust height and width to make it look good
      // TODO See if we can auto-figure this out in all cases?
      $this.css('width', $this.outerWidth() - opts.widthAdjust);
      $this.css('height', $this.outerHeight() - opts.heightAdjust);
      $this.find('.button-spinner img').css('padding-top', opts.paddingTop+'px');

      // Hide the text and show spinner
      $this.find('.button-text').hide();
      $this.find('.button-spinner').show(); //.find('img')
      if (opts.reloadSource) {
        // TODO Figure out why at least the second of these isn't needed?
        //$this.find('.button-spinner').html($this.find('.button-spinner').show().html());
        //$this.find('.button-spinner img').attr('src', buttonSpinnerSrc);
      }
    });
  }
  $.fn.showButtonSpinner.defaults = {
    reloadSource:     false,
    widthAdjust:      0,
    heightAdjust:     0,
    paddingTop:       0
  }

  $.fn.hideButtonSpinner = function(options) {
    var opts = $.extend({}, $.fn.hideButtonSpinner.defaults, options);
    var obj = opts.button
    if (!obj)
      obj = this
    // Iterate
    return obj.each(function() {
      var $this = $(this);

      $this.find('.button-spinner').hide();
      $this.find('.button-text').show();
      $this.removeClass('ui-state-disabled').enable();
      $this.css('width', $this.outerWidth() - opts.widthAdjust);
      $this.css('height', $this.outerHeight() - opts.heightAdjust);
      // TODO Add the hover css stuff

    });
  }
  $.fn.hideButtonSpinner.defaults = {
    reloadSource:     false,
    widthAdjust:      0,
    heightAdjust:     0,
    paddingTop:       0
  }

  // Provide a function that searches for an item and either replaces an existing one or adds a new one
  // TODO Allow different append/prepend/etc methods
  $.fn.replaceOrAppend = function(selector, content) {
    $(this).each(function() {
      var $item = $(this).find(selector);
      if ($item.length == 0) {
        $(this).append(content);
      } else { // Found, replace
        $item.replaceWith(content);
      }
    });
  }

  // Extend toggle
  $.fn.toggleFade = function(speed, easing, callback) {
    return this.animate({opacity:'toggle'}, speed, easing, callback);
  }

  // Make a swap that fades out the selected item, then fades in a specified target
  $.fn.swapFade = function(targetSelector, speed, callback) {
    return this.fadeOut(speed, function() {
      jQuery(targetSelector).fadeIn(speed, callback);
    });
  }

  // FIXME We're loading jquery-ui later, this isn't working
  // Extend UI dialog only if it is defined
  if (typeof($.ui) != 'undefined') {
    // Extend dialog only if it is defined
    if(typeof($.ui.dialog) != 'undefined') {
      $.extend($.ui.dialog.prototype, {
        'addButton': function(buttonName, func) {
          var buttons = this.element.dialog('option', 'buttons');
          buttons[buttonName] = func;
          this.element.dialog('option', 'buttons', buttons);
        }
      });
      $.extend($.ui.dialog.prototype, {
        'removeButton': function(buttonName) {
          var buttons = this.element.dialog('option', 'buttons');
          delete buttons[buttonName];
          this.element.dialog('option', 'buttons', buttons);
        }
      });
    }
  }
})(jQuery);
