(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 );
    
    if( matches ) {
        var dollars = parseInt(matches[1].replace(/[ ,]/g, ''));
        var cents = 0;

        if (matches[3] != null && matches[3].length >= 2)
            cents = parseInt(matches[3].replace('.', ''));

        return dollars*100 + cents;
    }
    else {
        return null;
    }
    
}; // parse_money

$.format_money = function ( amountInCents, trimZeroCents ) {

    var amount = parseInt(amountInCents);

    if (isNaN(amount) || amount != amountInCents )
        return null;

    var cents = amountInCents % 100;
    var dollars = (amountInCents - cents) / 100;

    cents = (cents < 10) ? ('0' + cents) : cents;
    dollars = '$' + $.commify( dollars )

    var money = dollars + '.' + cents;

    if (trimZeroCents)
        money = money.replace( /\.00$/, '' );

    return 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);
};


$.fn.find_option_index = function( val ) {

  var index = -1;

  $.each( this[0].options, function(i,option) {
    if (option.value == val)
      index = i;
  } );

  return index;
};


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

