(function ($) {


// Create a detached dom node from an html string

$.from_html = function (html) {
  var div = document.createElement('div');
  div.innerHTML = html;
  var elem = div.childNodes[0];
  div.removeChild(elem);
  return elem;
};


// Get all attributes of an element as a hash.
// The returned hash is suitable input for the $(...).attr.(...) setter.

$.fn.attr_all = function () {
  var attr = {};

  for (var i=0; i<this[0].attributes.length; i++) {
    var node = this[0].attributes[i];
    var key = node.nodeName;
    if (key == 'class') key = 'className';
    attr[key] = node.value;
  }

  return attr;
};


// Parse a query string into a flat hash structure.
// If the same key is encountered multiple times, the value
// under that key will be an array.

$.parse_query = function (str) {

  var i = str.indexOf('?');
  if (i >= 0) str = str.substr(i+1);

  var pairs = str.split('&');
  var param = {};

  for (var i in pairs) {
    var kv = pairs[i].split('=');
    var k = decodeURIComponent(kv[0]);
    var v = decodeURIComponent(kv[1]);
    k = k.replace(/\+/g, ' ');
    v = v.replace(/\+/g, ' ');

    if (param[k] == null) {
      param[k] = v;
    }
    else {
      if (typeof(param[k]) == 'string')
        param[k] = [ param[k] ];
      param[k].push(v);
    }
  }

  return param;
};


/*
  Turn a nested structure into a flat hash suitable for
  constructing a query string. Example:
 
   { foo: { bar: 42, baz: [ 5, 6, 7 ] } }
 
  becomes
 
   {
     "foo.bar" : 42,
     "foo.baz.0" : 5,
     "foo.baz.1" : 6,
     "foo.baz.2" : 7
   }
*/

$.flatten_param = function (param) {
  var newparam = {};

  for (var k1 in param) {
    var v1 = param[k1];

    if (v1 != null && typeof v1 == 'object') {

      var subparam = $.flatten_param(v1);
      for (var k2 in subparam) {
        var v2 = subparam[k2];
        newparam[k1+'.'+k2] = v2;
      }
    }
    else {
      newparam[k1] = v1;
    }
  }

  return newparam;
};


// Takes a date object and returns a DB suitable date string

$.format_date = function (dateObj) {
    var monthStr = dateObj.getMonth() + 1;
    if( monthStr < 10 )
	monthStr = '0' + '' + monthStr;

    var dayStr = dateObj.getDate();
    if( dayStr < 10 )
	dayStr = '0' + '' + dayStr;

    return dateObj.getFullYear() + '-' 
	 + monthStr + '-'
         + dayStr
         ;
}; // format_date

// Parse a date string "mm/dd/YYYY" and return a Date object
// Will return null if parsing fails

$.parse_date = function (dateStr) {

    // Validate date
    var matchDate = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
    if( typeof dateStr === "undefined"
	|| !dateStr 
	|| !matchDate.exec( dateStr ) )
    {
	return null;
    }
    
    // Parse date
    var dateParts = dateStr.split( '/' );
    for( var i = 0; i < dateParts.length; i++ ) {
	dateParts[i] = dateParts[i].replace( /^0/, "" );
	dateParts[i] = parseInt( dateParts[i] );
    }

    // And do some more validation
    if( dateParts[0] < 1 || dateParts[0] > 12           // validate month
	|| dateParts[1] < 0 || dateParts[1] > 31        // validate day
	|| dateParts[2] < 1900 || dateParts[2] > 3000 ) // validate year
    {
	return null;
    }
    
    // Finally, enough validate, create our date object
    var date = new Date();
    date.setFullYear( dateParts[2] );
    date.setMonth( dateParts[0] - 1 );
    date.setDate( dateParts[1] );
    
    return date;
};

$.parse_number = function ( number ) {
    var match_number = /^[\n\f\r\s\t]*([0-9, ]+)[\n\f\r\s\t]*$/;
    var matches      = match_number.exec( number );
    
    var parsed_number = null;

    if( matches ) {
	parsed_number = matches[0].replace( ',', '' ).replace( ' ', '' );
    }
    
    return parsed_number;
}; // parse_number

$.parse_money = function ( money ) {
    var match_money  = /^[\n\f\r\s\t]*\$?[\n\f\r\s\t]*([0-9, ]+(\.\d{1,2}|\.)?)[\n\f\r\s\t]*$/;
    var matches      = match_money.exec( money );
    
    var parsed_money = null;

    if( matches ) {
	parsed_money = matches[1].replace( ',', '' ).replace( ' ', '' );
    }
    
    return parsed_money;
} // parse_money

$.format_money = function ( money, trimZeroCents ) {
    var formatted_money = money;

    var missing_zero = /^\d+\.\d$/;
    if( missing_zero.exec( money ) )
    {
	formatted_money += '0';
    }
    else if( (new String( formatted_money )).indexOf( '.' ) == -1 )
    {
	formatted_money += '.00';
    }

    formatted_money = '$' + $.commify( formatted_money );

    formatted_money = formatted_money.replace( /\.00$/, '' );

    return formatted_money;
} // format_money

$.commify = function ( number ) {
    var commified_number = number;

    commified_number = $.reverse_string( commified_number );
    commified_number = commified_number.replace( /(\d{3})(?=\d)(?!\d*\.)/g, "$1," );
    commified_number = $.reverse_string( commified_number );
    
    return commified_number;
} // commify

$.reverse_string = function (str) {
    str = new String( str );
    var reversed_string = null;

    if( str != null )
    {
	reversed_string = "";
	
	if( str.length > 0 ) {
	    for( var i = str.length-1; i >= 0; i-- ) {
		reversed_string += str.charAt( i );
	    }
	}
    }

    return reversed_string;
} // reverse_string

$.max = function () {
  var max = null;

  $( arguments ).each( function() {
    if (max == null || this > max)
      max = this;
  } );

  return max;
};

$.min = function() {
  var min = null;

  $( arguments ).each( function() {
    if (min == null || this < min)
      min = this;
  } );

  return min;
};


$.ucfirst = function( str ) {
  return str.substr(0,1).toUpperCase() + str.substr(1);
};

$.lcfirst = function( str ) {
  return str.substr(0,1).toLowerCase() + str.substr(1);
};


$.rubyConfirm = function ( opts ) {

  var defaults = {
    title: "Confirm",
    msg: "Are you sure you want to proceed?",
    onconfirm: function() {},
    onreject: function() {}
  };

  var params = $.extend( true, defaults, opts );

  var dlg = $( $.from_html('<div></div>') ).dialog({
    modal: true,
    title: params.title,
    autoOpen: false,
    buttons: {
      Yes: function() {
        $(this).dialog('close');
        $(this).data('onconfirm').call();
      },
      No: function() {
        $(this).dialog('close');
        $(this).data('onreject').call();
      }
    }
  });

  dlg.data( 'onconfirm', params.onconfirm );
  dlg.data( 'onreject', params.onreject );
  dlg.html( params.msg );
  dlg.dialog("open");

}; // rubyConfirm


$.rubyFormConfirm = function ( opts ) {
    opts.onconfirm = function () {
	var act = $('#action');
	act.val( opts.action );
	act.parents( 'form' )[0].submit();
    };
    
    $.rubyConfirm( opts );
}; // rubyConfirmCancel


$.rubyError = function ( title, message ) {

  var dlg = $( $.from_html('<div></div>') ).dialog({
    modal: true,
    title: title,
    autoOpen: false,
    buttons: {
      Ok: function() {
        $(this).dialog('close');
      }
    }
  });

  dlg.html(message);
  dlg.dialog("open");

}; // rubyError


// Ajax request error handler

$.rubyAjaxError = function (req, textStatus, errorThrown) {
  if (req.status == 403)   // Session expired
    window.location.reload();
  else
    $.rubyError(
      "AJAX Error",
      "An error occurred while making an ajax request: " +
        "(" + req.status + ") " + req.statusText
    );
}; // rubyAjaxError

// Enable or disable a jQuery ui widget

$.fn.enableWidget = function ( flag ) {

    if (flag) {
      $( this ).removeClass( 'ui-state-disabled' );
      $( this ).removeAttr( 'disabled' );
    }
    else {
      $( this ).addClass( 'ui-state-disabled' );
      $( this ).attr( 'disabled', 'disabled' );
    }

}; // $.fn.enableWidget

$.selectmenuSwap = function () {
    // Loop through each select option with the rtg-swap class
    var select = this;
    var swaps  = $( '.rtg-swap', select );
    if( swaps.length > 0 ) {
	swaps.each( function () {

	    // Parse out the id of the HTML element to toggle visibility
	    var option    = this;
	    var swapClass = $.grep( 
		option.className.split( / / ),
		function (n) {
		    return n.indexOf( 'rtg-swap-' ) == 0;
	    });
	    var swapId = swapClass[0].substr( "rtg-swap-".length );

	    // Show or hide that element based on if this option is selected
	    if( option.selected ) {
		$( '#' + swapId ).show();
	    }
	    else {
		$( '#' + swapId ).hide();
	    }
	} );
    }

    return;
}; // selectmenuSwap

})(jQuery); // function ($)

