////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Base Date Select Calendar Class
function calendar(id, options) {
  this.obj_name = 'calendar';
  
  // Constants
  this.day_names_letter = ['S','M','T','W','T','F','S'];
  this.day_names_abbr = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
  


  // Init basic data
  this.id = id;
  this.base_url = options.base_url;
  this.target_div_date = "calendar_"+this.id+"_datediv";
  this.target_div_time = "calendar_"+this.id+"_timediv";
  this.target_div_dump = "calendar_"+this.id+"_dump";
  
  this.options = options || { };
  
  // Debug mode
  this.debug = this.options.debug != null ? this.options.debug : false;
  
  // Options
  this.time_restrict_all = this.options.time_restrict_all  != null ? this.options.time_restrict_all : false;
  this.seperate_year_select = this.options.seperate_year_option != null ? this.options.seperate_year_option : false;
  this.link = this.options.link ? this.options.link : null;
  this.show_blank = this.options.show_blank ? this.options.show_blank : false;
  
  // Migration Options
  this.option_disable_inputs = sputil_get_option(options, 'disable_inputs', false);
  this.option_display_date_selection = sputil_get_option(options, 'display_date_selection', true);
  this.option_display_spillover_days = sputil_get_option(options, 'display_spillover_days', false);
  
  // Define events within current calendar
  // day_num_events_types['name'] = status
  // day_num_events['name']['datestr'] = num_events (Array of sphash)
  this.day_num_events_defaulttype = 'default';
  this.day_num_events_types = new sphash();
  this.day_num_events = new Array();
  
  // Define Custom Icons
  this.day_type = new Array();

  // Get Time Strings (Required)
  this.times = Array();
  this.times['cur'] = Array();
  this.times['cur']['type']  = 'cur';
  this.times['cur']['datestr'] = String(this.options.datetime);
  this.times['start'] = Array();
  this.times['start']['type'] = 'start';
	this.times['start']['datestr'] = this.options.datetime_start ? String(this.options.datetime_start) : String(this.times['cur']['datestr']);
  this.times['end'] = Array();
  this.times['end']['type'] = 'end';
  this.times['end']['datestr'] = this.options.datetime_end ? String(this.options.datetime_end) : String(this.times['cur']['datestr']);
  this.times['display'] = Array();
  this.times['display']['type'] = 'display';
  this.times['display']['datestr'] = this.times['cur']['datestr'];
  
  // Initialize Times
  this.init_time('cur');
  this.init_time('start');
  this.init_time('end');
  this.init_time('display');

  // Check Ranges
  if(this.times['start']['datenum'] > this.times['end']['datenum']) alert('StartTime After EndTime');
  if(this.times['cur']['datenum'] > this.times['end']['datenum']) alert('CurTime After EndTime');
  if(this.times['cur']['datenum'] < this.times['start']['datenum']) alert('CurTime Before StartTime');

  // Init Display Flags
  this.display_date_flag = false;
  this.display_time_flag = false;
  
  // Callbacks
  this.callbacks_onchange = new Array();
  this.callbacks_onchange_date = new Array();
  this.callbacks_onchange_month = new Array();
  this.callbacks_onchange_day = new Array();
  
  // Sync
  this.sync_obj = null;
  this.sync = function() { };
  this.sync_date = function() { };
  this.sync_time = function() { };
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Base Functions

calendar.prototype.iden = function() {
  alert(this.obj_name);
}

calendar.prototype.add_sync_obj = function(obj) {
  this.sync_obj = obj;
}

calendar.prototype.init_time = function(type) {
  var data = this.times[type];
  data['datenum'] = util_convert_string_to_int(data['datestr']);
  
  data['year']  = util_convert_string_to_int(data['datestr'].substr(0, 4));
	data['month'] = util_convert_string_to_int(data['datestr'].substr(4, 2));
	data['day']   = util_convert_string_to_int(data['datestr'].substr(6, 2));
  data['hour']  = util_convert_string_to_int(data['datestr'].substr(8, 2));
  data['min']   = util_convert_string_to_int(data['datestr'].substr(10, 2));
  
  if(data['year'] > 2050 || data['year'] < 1900) alert('Invalid Year: '+data['year']);
  if(data['month'] <= 0 || data['month'] > 12) alert('Invalid Month: '+data['month']);
  if(data['day'] <= 0 || data['day'] > 31) alert('Invalid Day: '+data['day']);
  if(data['hour'] < 0 || data['hour'] >= 24) alert('Invalid Hour: '+data['hour']);
  if(data['min'] < 0 || data['min'] >= 60) alert('Invalid Min: '+data['min']);
}

calendar.prototype.dump = function(type) {
  var html = "";
  html += this.dump_datetime('start');
  html += this.dump_datetime('end');
  html += this.dump_datetime('cur');
  html += this.dump_datetime('display');
  
  $(this.target_div_dump).innerHTML = html;
}

calendar.prototype.dump_datetime = function(type) {
  var data = this.times[type];
  var str = "Type: "+type+"  <br/>\n";
  str += "Year " + data['year'] + "  <br/>\n";
  str += "Month " + data['month'] + "  <br/>\n";
  str += "Day " + data['day'] + "  <br/>\n";
  str += "Hour " + data['hour'] + "  <br/>\n";
  str += "Minute " + data['min'] + "  <br/>\n";
  str += "<br/>\n";
  return str;
}

calendar.prototype.get_date_str = function() {
  return util_create_date_num(this.get_year(), this.get_month(), this.get_day());
}

calendar.prototype.get_time_str = function() {
  return util_create_time_num(this.get_hour(), this.get_min(), 0);
}

calendar.prototype.get_obj_from_type = function(type) {
  if(type == null || type == undefined) type = 'cur';
  else if(this.times[type] == null) { alert('Invalid Type:'+ type); }
  return this.times[type];
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Callbacks - all callback functions should take the calendar object as the parameter
calendar.prototype.register_callback_onchange = function(func_pointer) {
  this.callbacks_onchange.push(func_pointer);
}

calendar.prototype.register_callback_onchange_date = function(func_pointer) {
  this.callbacks_onchange_date.push(func_pointer);
}

calendar.prototype.register_callback_onchange_month = function(func_pointer) {
  this.callbacks_onchange_month.push(func_pointer);
}

calendar.prototype.register_callback_onchange_day = function(func_pointer) {
  this.callbacks_onchange_day.push(func_pointer);
}

calendar.prototype.call_callback_onchange = function() {
  for(var i=0;i<this.callbacks_onchange.length;i++)
    this.callbacks_onchange[i](this);
}

calendar.prototype.call_callback_onchange_date = function() {
  for(var i=0;i<this.callbacks_onchange_date.length;i++)
    this.callbacks_onchange_date[i](this);
}

calendar.prototype.call_callback_onchange_month = function() {
  for(var i=0;i<this.callbacks_onchange_month.length;i++)
    this.callbacks_onchange_month[i](this);
}

calendar.prototype.call_callback_onchange_day = function() {
  for(var i=0;i<this.callbacks_onchange_day.length;i++)
    this.callbacks_onchange_day[i](this);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors
calendar.prototype.get_year = function(type) {
  var timedata = this.get_obj_from_type(type);
  return timedata['year'];
}

calendar.prototype.get_month = function(type) { 
  var timedata = this.get_obj_from_type(type);
  return timedata['month']; 
}

calendar.prototype.get_day = function(type) {
  var timedata = this.get_obj_from_type(type);
  return timedata['day'];   
}

calendar.prototype.get_hour = function(type) {
  var timedata = this.get_obj_from_type(type);
  return timedata['hour'];   
}

calendar.prototype.get_min = function(type) {
  var timedata = this.get_obj_from_type(type);
  return timedata['min'];   
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Data Accessors
calendar.prototype.at_start_year = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(timedata['year'] == this.get_year('start')) return true;
  return false;
}
calendar.prototype.at_start_month = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_start_year(type)) return false;
  if(timedata['month'] == this.get_month('start')) return true;
  return false;
}
calendar.prototype.at_start_day = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_start_month(type)) return false;
  if(timedata['day'] == this.get_day('start')) return true;
  return false;
}
calendar.prototype.at_start_hour = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_start_day(type)) return false;
  if(timedata['hour'] == this.get_hour('start')) return true;
  return false;
}
calendar.prototype.at_start_min = function(type) { 
  var timedata = this.get_obj_from_type(type);
  if(!this.at_start_hour(type)) return false;
  if(timedata['min'] == this.get_min('start')) return true;
  return false;
}

calendar.prototype.at_end_year = function(type) { 
  var timedata = this.get_obj_from_type(type);
  if(timedata['year'] == this.get_year('end')) return true;
  return false;
}
calendar.prototype.at_end_month = function(type) { 
  var timedata = this.get_obj_from_type(type);
  if(!this.at_end_year(type)) return false;
  if(timedata['month'] == this.get_month('end')) return true;
  return false;
}
calendar.prototype.at_end_day = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_end_month(type)) return false;
  if(timedata['day'] == this.get_day('end')) return true;
  return false;
}
calendar.prototype.at_end_hour = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_end_day(type)) return false;
  if(timedata['hour'] == this.get_hour('end')) return true;
  return false;
}
calendar.prototype.at_end_min = function(type) {
  var timedata = this.get_obj_from_type(type);
  if(!this.at_end_hour(type)) return false;
  if(timedata['min'] == this.get_min('end')) return true;
  return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tester
calendar.prototype.test_date_limit = function(year, month, day) {  
  // check start
  if(year < this.get_year('start')) {
    return false;
  }
  if(year == this.get_year('start')) {
    if(month < this.get_month('start')) {
      return false;
    }
    if(month == this.get_month('start')) {
      if(day < this.get_day('start')) {
        return false;
      }
    }
  }

  // check end
  if(year > this.get_year('end')) {
    return false;
  }
  if(year == this.get_year('end')) {
    if(month > this.get_month('end')) {
      return false;
    }
    if(month == this.get_month('end')) {
      if(day > this.get_day('end')) {
        return false;
      }
    }
  }
  return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Setters
calendar.prototype.set_year = function(new_year, type) {
  var timedata = this.get_obj_from_type(type);
  
  new_year = util_convert_string_to_int(new_year);
  if(new_year < this.get_year('start')) return false;
  if(new_year > this.get_year('end')) return false;
  timedata['year'] = new_year;
  
  if(!this.option_disable_inputs)
    spjs_set_value(this.id+'_year', new_year);
  return true;
}

calendar.prototype.set_month = function(new_month, type) {
  var timedata = this.get_obj_from_type(type);
  
  new_month = util_convert_string_to_int(new_month);
  if(this.at_start_year(type) && new_month < this.get_month('start')) return false;
  if(this.at_end_year(type) && new_month > this.get_month('end')) return false;
  timedata['month'] = new_month;
  
  if(!this.option_disable_inputs)
    spjs_set_value(this.id+'_month', new_month);
  return true;
}

calendar.prototype.set_day = function(new_day, type) {
  var timedata = this.get_obj_from_type(type);
  
  new_day = util_convert_string_to_int(new_day);
  if(this.at_start_month(type) && new_day < this.get_day('start')) return false;
  if(this.at_end_month(type) && new_day > this.get_day('end')) return false;
  timedata['day'] = new_day;
  
  if(!this.option_disable_inputs)
    spjs_set_value(this.id+'_day', new_day);
  return true;
}

calendar.prototype.set_hour = function(new_hour, type) {
  var timedata = this.get_obj_from_type(type);
  
  new_hour = util_convert_string_to_int(new_hour);
  if(this.at_start_day(type) && new_hour < this.get_hour('start')) return false;
  if(this.at_end_day(type) && new_hour  > this.get_hour('end')) return false;
  timedata['hour'] = new_hour;

  if(!this.option_disable_inputs)
    spjs_set_value(this.id+'_hour', new_hour);
  return true;
}

calendar.prototype.set_min = function(new_min, type) {
  var timedata = this.get_obj_from_type(type);
  
  new_min = util_convert_string_to_int(new_min);
  if(this.at_start_hour(type) && new_min < this.get_min('start')) return false;
  if(this.at_end_hour(type) && new_min > this.get_min('end')) return false;
  timedata['min'] = new_min;

  if(!this.option_disable_inputs)
    spjs_set_value(this.id+'_min', new_min);
  return true;
}

calendar.prototype.set_from_calendar = function(new_cal, type) {
  this.set_year(new_cal.get_year(type), type);
  this.set_month(new_cal.get_month(type), type);
  this.set_day(new_cal.get_day(type), type);
  this.set_hour(new_cal.get_hour(type), type);
  this.set_min(new_cal.get_min(type), type);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Increment/Decrement Calendar Functions

calendar.prototype.prev_min = function(type) {
	if(this.at_start_min(type)) return false;
  
  if(this.get_min(type) > 0)
    this.set_min(this.get_min(type) - 15, type);
  else {
    this.prev_hour(type);
    this.set_min(45, type);
  }
  
  return true;
}

calendar.prototype.next_min = function(type) {
	if(this.at_end_min(type)) return false;
  
  if(this.get_min(type) < 45)
    this.set_min(this.get_min(type) + 15, type);
	else {
    this.next_hour(type);
    this.set_min(0, type);
	}
  
  return true;
}

calendar.prototype.prev_hour = function(type) {
	if(this.at_start_hour(type)) return false;
  
  if(this.get_hour(type) > 0)
    this.set_hour(this.get_hour(type) - 1, type);
  else {
    this.prev_day(type);
    this.set_hour(23, type);
  }
  
  return true;
}

calendar.prototype.next_hour = function(type) {
	if(this.at_end_hour(type)) return false;

  if(this.get_hour(type) < 23)
    this.set_hour(this.get_hour(type) + 1, type);
	else {
    this.next_day(type);
    this.set_hour(0, type);
	}
  
  return true;
}

calendar.prototype.prev_day = function(type) {
	if(this.at_start_day(type)) return false;
  
  if(this.get_day(type) > 1)
    this.set_day(this.get_day(type) - 1, type);
  else {
    this.prev_month(type);
    this.set_day(util_get_num_monthdays(this.get_year(type), this.get_month(type), type));
  }
  
  return true;
}

calendar.prototype.next_day = function(type) {
	if(this.at_end_day(type)) return false;
  
  if(this.get_day(type) < util_get_num_monthdays(this.get_year(type), this.get_month(type)))
    this.set_day(this.get_day(type) + 1, type);
	else {
    this.next_month(type);
    this.set_day(1, type);
	}
  
  return true;
}

calendar.prototype.prev_month = function(type) {
	if(this.at_start_month(type)) return false;
  
  if(this.get_month(type) > 1)
    this.set_month(this.get_month(type) - 1, type);
  else {
    this.prev_year(type);
    this.set_month(12, type);
  }
  
  return true;
}

calendar.prototype.next_month = function(type) {
	if(this.at_end_month(type)) return false;

  if(this.get_month(type) < 12)
    this.set_month(this.get_month(type) + 1, type);
	else {
    this.next_year(type);
    this.set_month(1, type);
	}
  
  return true;
}

calendar.prototype.prev_year = function(type) {
  if(this.at_start_year(type)) return false;
  
  this.set_year(this.get_year(type) - 1, type);
  
  return true;
}

calendar.prototype.next_year = function(type) {
  if(this.at_end_year(type)) return false;

  this.set_year(this.get_year(type) + 1, type);
  
  return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// User GUI Actions

calendar.prototype.refresh_date_display_no_callback = function() {
  this.refresh_display();
}

calendar.prototype.refresh_date_display = function() {
  // change callback
  this.call_callback_onchange_month();
  this.call_callback_onchange_date();
  
  this.refresh_display();
}

calendar.prototype.next_month_button = function(type) {
  this.next_month(type);
  
  // change callback
  this.call_callback_onchange_month();
  this.call_callback_onchange_date();
  
  this.refresh_display();
  
  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.prev_month_button = function(type) {
  this.prev_month(type);
  
  // change callback
  this.call_callback_onchange_month();
  this.call_callback_onchange_date();
  
  this.refresh_display();
  
  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.next_year_button = function(type) {
  this.next_year(type);
  
  // change callback
  this.call_callback_onchange_month();
  this.call_callback_onchange_date();
  
  this.refresh_display();

  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.prev_year_button = function(type) {
  this.prev_year(type);
  
  // change callback
  this.call_callback_onchange_month();
  this.call_callback_onchange_date();
  
  this.refresh_display();
  
  // Debug line
  if(this.debug)
    this.dump();
}

// Function to change selected day base
calendar.prototype.set_cal_day = function(day) {
  // on click we now should show date
  this.show_blank = false;

  day = parseInt(day);
  
  // Grab old day
	var old_d = this.get_day();	
  
  // Grab Display
  var display_month = this.get_month('display');
  var display_year = this.get_year('display');
  
  // Set in memory new date value for 'cur'
  if(!this.test_date_limit(display_year, display_month, day)) return;
  
  this.set_year(display_year);
  this.set_month(display_month);
  this.set_day(day);
  
  // Set display date
  this.set_day(day, 'display');
	
  // Set styles to reflect GUI change
  spjs_removeclass(this.id+'_calday_'+old_d, 'calcell_selected');
  spjs_addclass(this.id+'_calday_'+old_d, 'calcell_normal');
  spjs_removeclass(this.id+'_calday_'+day, 'calcell_normal');
  spjs_addclass(this.id+'_calday_'+day, 'calcell_selected');
  
  if(this.link != null) {
    // Link, ignore call backs
    var startdt = new spdt(this.get_year(), this.get_month(), this.get_day(), this.get_hour(), this.get_min(), 0, -5);
    var enddt = new spdt(this.get_year(), this.get_month(), this.get_day() + 1, this.get_hour(), this.get_min(), 0, -5);
    var searchlink = this.link+"tn="+startdt.get_epoch()+"&tx="+enddt.get_epoch();
    document.location.href=searchlink;
  }
  else {
  
    // Call custom callback
    this.call_callback_onchange_day();
    this.call_callback_onchange_date();
    
    // Sync
    if(this.sync_obj != null) {
      this.sync_date.call(this.sync_obj);
      this.sync.call(this.sync_obj);
    }
  }
  
  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.update_toggle_buttons = function(type, newval, oldval) {
  spjs_removeclass(this.id+'_'+type+'_'+oldval, 'calcell_selected');
  spjs_addclass(this.id+'_'+type+'_'+oldval, 'calcell_normal');
  
  spjs_removeclass(this.id+'_'+type+'_'+newval, 'calcell_normal');
  spjs_addclass(this.id+'_'+type+'_'+newval, 'calcell_selected');
}

calendar.prototype.update_time_hour = function(hour) {
  
  // Get new and old hour
  var old_hour = this.get_hour();
  var hash = util_breakdown_24hour(old_hour);
  var old_hour12 = parseInt(hash['hour']);
  var ampm = hash['ampm'];
  var new_hour12 = parseInt(hour);
  var new_hour = util_create_24hour(new_hour12, ampm);
  
  // shift coloring
  //this.update_toggle_buttons('hour', new_hour12, old_hour12);
  
  // set hour
  this.set_hour(new_hour);
  this.set_hour(new_hour, 'display');
  
  // Callbacks
  this.call_callback_onchange();
  
  // Sync
  if(this.sync_obj != null) {
    this.sync_time.call(this.sync_obj);
    this.sync.call(this.sync_obj);
  }
  
  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.update_time_min = function(min) {

  // Get new and old value
  var new_val = parseInt(min);
  var old_val = this.get_min();
  
  // Shift Coloring
  //this.update_toggle_buttons('min', new_val, old_val);
  
  // set new
  this.set_min(new_val);
  this.set_min(new_val, 'display');
  
  // Callbacks
  this.call_callback_onchange();
  
  // Sync
  if(this.sync_obj != null) {
    this.sync_time.call(this.sync_obj);
    this.sync.call(this.sync_obj);
  }
  
  // Debug line
  if(this.debug)
    this.dump();
}

calendar.prototype.update_time_meridian = function(ampm) {
  
  // Get new and old hour
  var old_hour = this.get_hour();
  var hash = util_breakdown_24hour(old_hour);
  var hour12 = hash['hour'];
  var old_ampm = hash['ampm'];
  var new_ampm = ampm;
  var new_hour = util_create_24hour(hour12, new_ampm);
  
  // Shift Coloring
  //this.update_toggle_buttons('meridian', new_ampm, old_ampm);
  
  // set new
  this.set_hour(new_hour);
  this.set_hour(new_hour, 'display');
  
  // Callbacks
  this.call_callback_onchange();
  
  // Sync
  if(this.sync_obj != null) {
    this.sync_time.call(this.sync_obj);
    this.sync.call(this.sync_obj);
  }
  
  // Debug line
  if(this.debug)
    this.dump();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HTML Generator

calendar.prototype.refresh_display = function() {
  if(this.display_date_flag)
    $(this.target_div_date).innerHTML = this.gen_date_html();
  if(this.display_time_flag)
    $(this.target_div_time).innerHTML = this.gen_time_html();
}

calendar.prototype.display_dates = function() {
  $(this.target_div_date).innerHTML = this.gen_date_html();
  this.display_date_flag = true;
}

calendar.prototype.display_times = function() {
	$(this.target_div_time).innerHTML = this.gen_time_html();
  this.display_time_flag = true;
}

calendar.prototype.gen_date_html = function() {
  // 0 = sunday, 6 = saturday
  
  var cur_month = this.get_month();
  var cur_year = this.get_year();
  var cur_day = this.get_day();
  var cur_type = 'cur';
  var display_month = this.get_month('display');
  var display_year = this.get_year('display');
  var display_type = 'display';
  
  var show_cur_day = false;
  if(display_month == cur_month && display_year == cur_year && !this.show_blank)
    show_cur_day = true;
  
  // Start day of month
  var c = new Date;
  c.setDate(1);
  c.setMonth(display_month-1);
  c.setFullYear(display_year);
  var s = c.getDay() % 7;

	var month_length = util_get_num_monthdays(display_year, display_month);
  
	var h = '<div class="calendar_date_obj">';
		
	// Header
  if(this.option_display_date_selection) {
	h += '<div class="calendar_date_selector">';
  if(this.seperate_year_select) {
    h += '<div class="left">';
    h += '<div style="float:left;width:25px;text-align:left">';
    if(!this.at_start_month(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.prev_month_button(\'display\')"><img src="'+this.base_url+'layout/arrow_left.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div style="float:left;width:50px;text-align:center">'+util_month_to_shortstring(display_month)+'</div>';
    h += '<div style="float:left;width:25px;text-align:right">';
    if(!this.at_end_month(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.next_month_button(\'display\')"><img src="'+this.base_url+'layout/arrow_right.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div class="spacer"> </div>';
    h += '</div>';
    
    h += '<div class="right">';
    h += '<div style="float:left;width:25px;text-align:left">';
    if(!this.at_start_year(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.prev_year_button(\'display\')"><img src="'+this.base_url+'layout/arrow_left.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div style="float:left;width:50px;text-align:center">'+display_year+'</div>';
    h += '<div style="float:left;width:25px;text-align:right">';
    if(!this.at_end_year(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.next_year_button(\'display\')"><img src="'+this.base_url+'layout/arrow_right.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div class="spacer"> </div>';
    h += '</div>';
    
    h += '<div class="spacer"> </div>';
  }
  else {
    h += '<div>';
    h += '<div style="float:left;width:25px;text-align:left">';
    if(!this.at_start_month(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.prev_month_button(\'display\')"><img src="'+this.base_url+'layout/arrow_left.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div style="float:left;width:160px;text-align:center">'+util_month_to_shortstring(display_month)+' '+display_year+'</div>';
    h += '<div style="float:right;width:25px;text-align:right"> ';
    if(!this.at_end_month(display_type))
      h += '<a href="javascript:void(0)" onclick="'+this.id+'.next_month_button(\'display\')"><img src="'+this.base_url+'layout/arrow_right.gif"/></a>';
    else
      h += '&nbsp;';
    h += '</div>';
    h += '<div class="spacer"> </div>';
    h += '</div>';
  }
	h += '</div>';
  }
  
  // Day Row
  h += '<div class="calendar_header" style="margin-bottom:2px">';
    h += '<div class="left cal_headercell">S</div>';
    h += '<div class="left cal_headercell">M</div>';
    h += '<div class="left cal_headercell">T</div>';
    h += '<div class="left cal_headercell">W</div>';
    h += '<div class="left cal_headercell">T</div>';
    h += '<div class="left cal_headercell">F</div>';
    h += '<div class="left cal_headercell">S</div>';
    h += '<div class="spacer"> </div>';
  h += '</div>';
		
	h += '<div class="calendar">';
  
  var num_weeks = 0;
  
  h+= '<div>';

  // Range of selectable cells
	var cur_cal_day_start = 1;
	var cur_cal_day_end = month_length;
	
	if(this.at_start_month(display_type)) cur_cal_day_start = this.get_day('start');
	if(this.at_end_month(display_type)) cur_cal_day_end = this.get_day('end');

	// Add End of Last Month
	for (var i = s; i > 0; i--)
		h += '<div class="left calcell calcell_blank"> </div>';
	
	// Add This Month
	for (var i = 1; i <= month_length; i++) {
    var datestr = util_create_date_num(display_year, display_month, i);
    
    var stylestring = this.get_day_color_style(datestr);
		var js = 'onclick="'+this.id+'.set_cal_day('+i+');"';
    var class_name="normal";

		if(i == cur_day && show_cur_day) {
			class_name ="selected";
    }
			
		if(i < cur_cal_day_start || i > cur_cal_day_end) {
      stylestring = "";
			class_name ="disabled";
			js = "";
		}
    
		// See when week ends
		if (s % 7 == 0 && s != 0) {
      h += '<div class="spacer"> </div></div> <div>';
      num_weeks++;
			s = 0;
		}
		
    // Generate Actual Cell
    h += '<div id="'+this.id+'_calday_'+i+'" class="left calcell calcell_'+class_name+'" style="'+stylestring+'" '+js+' >';
    if(this.day_type[datestr] == null)
      h += i;
    else
      h += '<img style="vertical-align:middle" src="'+this.base_url+'/layout/calendar/'+this.day_type[datestr]+'"/>';
    h += '</div>';
    
		s++;
	}
	
	// Add Begin of Next Month
	for (var i = s; i < 7; i++) {
    h += '<div class="left calcell calcell_blank">';
    h += '</div>';
  }
  
  // Check if middle of week
  if(s > 0)
    num_weeks++;
  
  // Add extra spacing row so calendar doesn't shift up and down between months
  while(num_weeks < 6) {
    h += '<div class="spacer"> </div></div>';
    h += '<div><div class="left calcell calcell_spacer"> </div>';
    num_weeks++;
  }
  
  h += '<div class="spacer"> </div>';
  h+= '</div>';
	
	if(!this.disable_date_inputs) {
		h += '<input type="hidden" name="year[\''+this.id+'\']" id="'+this.id+'_year" value="'+this.get_year()+'"/>';
    h += '<input type="hidden" name="month[\''+this.id+'\']" id="'+this.id+'_month" value="'+this.get_month()+'"/>';
    h += '<input type="hidden" name="day[\''+this.id+'\']" id="'+this.id+'_day" value="'+this.get_day()+'"/>';
	}
  
  // Debug Display
  if(this.debug) {
    h += '<div>';
    h += '<a href="javascript:void(0)" onclick="'+this.id+'.dump()">Dump</a>';
    h += '</div>';
  }
		
	h += '</div>';
	return h;
}

calendar.prototype.gen_time_html = function() {
  var h = '';
  
  var cur_hour = this.get_hour();
  var cur_min = this.get_min();
  
  var hash = util_breakdown_24hour(cur_hour);
  var cur_12hour = hash['hour'];
  var cur_ampm = hash['ampm'];
  
  var default_class = "left calcell";
  var normal_class = "calcell_normal";
  var select_class = "calcell_selected";
  
  var selected_tag = 'selected="selected"';
  
  h+= '<div>';
  
  h+= '<div class="left">';
    h+= '<select id="'+this.id+'_hour_select" class="f_input" style="width:50px" onchange="'+this.id+'.update_time_hour(spjs_get_value(\''+this.id+'_hour_select\'))">';
    for(var c = 0; c < 12; c++) {
      var i = 12 + c;
      if(i > 12) i -= 12;
      var selected = "";
      if(i == cur_12hour)
        selected = selected_tag;
      h+= '<option value="'+i+'" '+selected+'>'+i+'</option>';
    }
    h+= '</select>';
  h+='</div>';
  
  h+= '<div class="left" style="height:25px;line-height:25px;margin-left:5px;margin-right:5px">:</div>';
  
  h+= '<div class="left">';
    h+= '<select id="'+this.id+'_min_select" class="f_input" style="width:50px" onchange="'+this.id+'.update_time_min(spjs_get_value(\''+this.id+'_min_select\'))">';
      h+= '<option value="0" '+(cur_min == 0 ? selected_tag : '')+'>00</option>';
      h+= '<option value="15" '+(cur_min == 15 ? selected_tag : '')+'>15</option>';
      h+= '<option value="30" '+(cur_min == 30 ? selected_tag : '')+'>30</option>';
      h+= '<option value="45" '+(cur_min == 45 ? selected_tag : '')+'>45</option>';
    h+= '</select>';
  h+='</div>';
  
  h+= '<div class="left" style="height:25px;line-height:25px;margin-left:5px;margin-right:5px"> </div>';
  
  h+= '<div class="left">';
    h+= '<select id="'+this.id+'_ampm_select" class="f_input" style="width:50px" onchange="'+this.id+'.update_time_meridian(spjs_get_value(\''+this.id+'_ampm_select\'))">';
      h+= '<option value="am" '+(cur_ampm == 'am' ? selected_tag : '')+'>am</option>';
      h+= '<option value="pm" '+(cur_ampm == 'pm' ? selected_tag : '')+'>pm</option>';
    h+= '</select>';
  h+='</div>';
  
  
  h+= '<div class="spacer"> </div>';
  
  if(!this.disable_date_inputs) {
    h += '<input type="hidden" name="hour[\''+this.id+'\']" id="'+this.id+'_hour" value="'+this.get_hour()+'"/>';
    h += '<input type="hidden" name="min[\''+this.id+'\']" id="'+this.id+'_min" value="'+this.get_min()+'"/>';
  }
  
  h+= '</div>';
  
  return h;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Day Markers

calendar.prototype.get_day_color_style = function(datestr) {
  // 0 #ffffff
  // 1-3 #ffe4d3
  // 4-6 #ffb88d
  // 6-10 #ff8a44
  // 10+ #ff6000
  
  var total = 0;
  var keypairs = this.day_num_events_types.keys();
  for(var i=0;i<keypairs.length;i++) {
    var key = keypairs[i];
    if(this.day_num_events_types.get(key) == 0)
      continue;
    if(this.day_num_events[key].exist(datestr))
      total += this.day_num_events[key].get(datestr);
  }
  
  stylestring = "";
	if(total > 0) {
    var colorstring = "#ffffff";
    if(total > 10) 
      colorstring = "#ff6000";
    else if(total >= 6) 
      colorstring = "#ff8a44";
    else if(total >= 4) 
      colorstring = "#ffb88d";
    else if(total >= 1) 
      colorstring = "#ffe4d3";
    stylestring = "background:"+colorstring;
  }
  return stylestring;
}

calendar.prototype.day_num_type_exist = function(type) {
  if(this.day_num_events_types.exist(type))
    return true;
  return false;
}

calendar.prototype.day_num_type_add = function(type) {
  var exist = this.day_num_type_exist(type);
  
  if(!exist) {
    // Create new type
    this.day_num_events_types.set(type, 1);
    
    // Create new data
    this.day_num_events[type] = new sphash();
  }
}

calendar.prototype.debug_show_day_num_types = function() {
  var keypairs = this.day_num_events_types.keys();
  for(var i=0;i<keypairs.length;i++) {
    var key = keypairs[i];
    var str = 'Type: ' + key + '\n';
    str += this.day_num_events[key].dump();
    alert(str);
  }
}

calendar.prototype.day_num_type_enable  = function(type) {  this.day_num_type_status_change(type, 1); }
calendar.prototype.day_num_type_disable = function(type) {  this.day_num_type_status_change(type, 0); }
calendar.prototype.day_num_type_status_change = function(type, status) {
  this.day_num_type_add(type);
  this.day_num_events_types.set(type, status);
}

calendar.prototype.set_day_num_events = function(datestr, num) { this.set_day_num_events_for_type(this.day_num_events_defaulttype, datestr, num); }
calendar.prototype.set_day_num_events_for_type = function(type, datestr, num) {
  if(!this.day_num_type_exist(type)) {
    this.day_num_type_add(type);
    this.day_num_type_disable(type);
  }
	this.day_num_events[type].set(datestr, num);
}

calendar.prototype.add_day_num_events = function(datestr) { this.add_day_num_events_for_type(this.day_num_events_defaulttype, datestr); }
calendar.prototype.add_day_num_events_for_type = function(type, datestr) {
  if(!this.day_num_type_exist(type)) {
    this.day_num_type_add(type);
    this.day_num_type_disable(type);
  }
  
  if(!this.day_num_events[type].exist(datestr))
    this.day_num_events[type].set(datestr, 1);
  else {
    var old_val = this.day_num_events[type].get(datestr);
    this.day_num_events[type].set(datestr, old_val + 1);
  }
}

calendar.prototype.clear_day_nums = function() {
  var keypairs = this.day_num_events_types.keys();
  for(var i=0;i<keypairs.length;i++) {
    var key = keypairs[i];
    this.day_num_events[key].clear();
  }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Day Icons

calendar.prototype.init_us_calendar = function() {
  this.day_type["20080704"] = "americanflag.gif";
}

calendar.prototype.set_day_type = function(datestr, type) {
	this.day_type[datestr] = type;
}

calendar.prototype.clear_day_types = function() {
  this.day_type = new Array();
}


