/************************************************************\
 *															*
 *	DateWidget.js											*
 *															*
 *	Author:	   Simon Brooke									*
 *	Created:   30th July 1999								*
 *	$Revision: 1.2 $; $Date: 2004/08/13 12:39:53 $			*
 *															*
\************************************************************/

/** JavaScript client end functionality to support DateWidget.java, q.v.
 *
 *  This revision: $Author: simon_brooke $
 *  <pre>
 *  $Log: DateWidget.js,v $
 *  Revision 1.2  2004/08/13 12:39:53  simon_brooke
 *  Beginning of interface to Amazon Web Services (doesn't work yet);
 *  Altered SubscriberAuthenticator in line with changes in the Authenticator API in Jacquard 1.10.1,
 *  which hasn't even been released as testing yet. If you have problems with this, email me.
 *
 *  Revision 1.16  2003/08/27 14:12:52  simon
 *  Minor fixes and reformattings as I get used to Eclipse
 *
 *  Revision 1.15  2002/11/15 00:14:28  simon
 *  Handling money for the first time... shows just how big-organisation
 *  and intranetty Jacquard has always been!
 *
 *  Revision 1.14  2002/11/08 13:16:44  simon
 *  Now has day-name haders on columns like CalendarPseudoWidget, uses
 *  (mostly) the same CSS classes. Requires 'DateWidget.css' to be in
 *  'styles' subdirectory of webapp root.
 *
 *  Revision 1.10  2001/10/25 10:31:29  simon
 *  Servlet: removal of one unwanted throws clause; Printer, miner change
 *  to work around slight change in dom interface between Xalan 1 and 2;
 *  DateWidget.js, apologise to user when it won't work; overview.html,
 *  release notes for 1.6
 *
 *  Revision 1.9  2001/10/23 10:53:32  simon
 *  Tidy up on pop-up code. Still doesn't satisfy me: doesn't cope with
 *  multiple simultaneous pop-ups; doesn't auto-close when the underlying
 *  form disappears; only really works in Internet Explorer.
 *
 *  Revision 1.8  2001/06/20 11:33:07  thomas
 *  Only attempt to update the calendar if it has not been
 *  closed. Previously you could open the calendar, close it, change
 *  the date and then when the input tag lost the focus it tried to
 *  update the calendar which had just been closed.
 *
 *  Revision 1.7  2001/06/18 16:31:43  simon
 *  Final version of Alan Wrigley's popup code.
 *
 *  Revision 1.3  2000/05/26 14:50:47  simon
 *  Made cookie time-to-live generally configurable; gave list optional,
 *  configurable search widget
 *
 *  Revision 1.2  2000/05/12 10:17:09  simon
 *  Made allow_logout of Authenticated form configurable in servlet config;
 *  altered TextAreaWidget to allow 'soft' as well as 'hard' wrap; altered
 *  DateWidget and TimeWidget to allow 'null' entries; minor work on
 *  AutoWrapperForm, which still isn't finished and still doesn't work.
 *
 *  Revision 1.1  1999/08/15 17:09:42  simon
 *  Major changes, primarily to htform: the action despatcher has now
 *  been built in Form, supported by a new class ActionWidget. This of
 *  course has required changes in classes built on Form, and
 *  particularly in AuthenticatedForm and TableWrapperForm, which are
 *  both now much simpler and cleaner.
 *
 *  JavaScript support has been added for more widgets.
 *
 * 
 *  </pre> 
 */


function badDate( field, message) 
{
    field.focus();
    alert( message);
    field.select();
}

/** clear all sub-fields to the null value */
function dateWidgetClear( dayfield, monthfield, yearfield) 
{
    dayfield.value == "--";

    for ( i = 0; i < monthfield.options.length; i++)
	monthfield.options[ i].selected = false;

    monthfield.options[ 0].selected = true;

    yearfield.value = "--";
}

/** if any sub-field has a non-null value, all should have, and the
 *  result should be a valid date */
function dateWidgetValidate( dayfield, monthfield, yearfield) 
{
	if ( dayfield.value != "--" || yearfield.value != "--")
	{		// '--' is the null value. If any field is
			// non-null, all should be
	    if ( dayfield.value == "--")
		dayfield.value = "01";
				// silently change null fields to default

	    if ( yearfield.value == "--")
		yearfield.value = "01";
				// silently change null fields to default

	    if ( dayfield.value == "") 
		{
		    dayfield.value = "01";
		    badDate( dayfield, 
			     "Please enter a number in the day field.");
		    return updatePopup(false);
		}
	    var day = parseInt( dayfield.value);
	    var month = 
		parseInt( monthfield.options[ monthfield.selectedIndex].value)
		+ 1;
	    var year = parseInt( yearfield.value);

	    if ( ( day < 1) || ( isNaN( day))) 
		{
		    dayfield.value = "01";
		    badDate( dayfield, 
			     "Please enter a number in the day field.");
		    return updatePopup(false);
		}
	    if ( ( day > 31) ||
		 ( ( month == 2) && ( day > 29)) ||
		 ( ( month == 4 || month == 6 || month == 9 || month == 11) && 
		   ( day > 30))) 
		{
		    dayfield.value = "01";
		    badDate( dayfield, "No such date!");
		    return updatePopup(true);
		}

	    if ( isNaN( year))
		{
		    yearfield.value = "2000";
		    badDate( yearfield, 
			     "Please enter a number in the year field.");
		    return updatePopup(false);
		}
	}
		return updatePopup(true);
}

var popup = null;

//updatePopup function used by dateWidgetValidate
//to ensure that popup is not updated unless it exists
function updatePopup(ret)
{
	if (popup)
		popup.update();
	return ret;
}

//Popup object constructor
function Popup( day, month, year, name)
{
	this.widgetday = day;
	this.widgetmonth = month;
	this.widgetyear = year;
	this.name = name;
	this.days = new Array(31,29,31,30,31,30,31,31,30,31,30,31);
	this.months = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
	this.win = window.open("","","left=40,top=40,width=240,height=260");
	
	this.update = popup_update;
	this.setWidgetDay = popup_setWidgetDay;
	this.setWidgetMonth = popup_setWidgetMonth;
	this.getMaxDays = popup_getMaxDays;
}

//showPopup called from widget-generated HTML
//to open the popup window
function showPopup(d,m,y,n)
{
  if ( browserOK())
    {
      if ( ( !popup) || ( popup.win.closed))
	{
	  popup = new Popup(d,m,y,n);
	  popup.update();
	}
    }
  else
    {
      alert( "Sorry, this function does not work on your browser");
    }
}

//update function updates the popup display
function popup_update()
{
	// Don't try to update the popup if it is closed!
	if (popup.win.closed) return;

	var doc = this.win.document;
	var today = parseInt(this.widgetday.value);
	var thismonth = parseInt(this.widgetmonth.options[this.widgetmonth.selectedIndex].value);
	var monthdays = this.getMaxDays(thismonth);
	doc.title = this.name;
	doc.open();
	
	//Show days of month in grid of table cells
	//Provide onclick handler for each cell which calls
	//the popup's setWidgetDay function
	doc.write("<html><head><title>" + this.name + "</title>");
	doc.write("  <link rel='StyleSheet' href='styles/DateWidget.css' type='text/css' media='screen'>");
	/* Right. Slightly dodgy, this. Increasingly I'm using servlet mappings
	 * which make the servlets appear to be in the webapp's root directory, 
	 * rather than appearing to be in a 'servlets' subdirectory. On such 
	 * systems you need to replace '../styles' with just 'styles' */

	doc.write( "</head>\n");
	doc.write("<body><table>\n");
	doc.write("<tr><th colspan=7>"+this.months[thismonth]+" "+this.widgetyear.value);

	doc.write("</th></tr>\n");

	doc.write("<tr>");
	doc.write("<th class='weHdr'>Su</th>");
				// doesn't seem any way of getting locale
				// specific day names
	doc.write("<th class='dayHdr'>Mo</th>");
	doc.write("<th class='dayHdr'>Tu</th>");
	doc.write("<th class='dayHdr'>W</th>");
	doc.write("<th class='dayHdr'>Th</th>");
	doc.write("<th class='dayHdr'>Fr</th>");
	doc.write("<th class='weHdr'>Sa</th>");
	doc.write("</tr>");

	var fDate = new Date( parseInt(this.widgetyear.value),
			      this.widgetmonth.selectedIndex - 1,
			      1, 0, 0, 0);

	var first = fDate.getDay();

	for (var i = 0; i < 6; i++)
	  {
	    doc.write("<tr>\n");

	    for (var j = 1; j < 8; j++)
	      {
		var cell = i*7+j; 

		var day = cell - first;

		var daystr = "&nbsp;";
		if ( ( day >= 1) && (day <= monthdays))
		  daystr =  day.toString();

		doc.write( "<td ");

		if ( today == day)
		  doc.write( "class='selected'");
		else
		  {
		    if ( ( cell % 7)  < 2)
		      doc.write( "class='weekend'");
		    else
		      doc.write( "class='weekday'");
		  }

		if (day <= monthdays)
		  doc.write(" onclick=\"opener.popup.setWidgetDay("+daystr+")\"");
		doc.write(">"+daystr+"</td>\n");
	      }
	    doc.write("</tr>\n");
	}
	doc.write("</table>");
	doc.write("<div class='buttons'>");
	
	//Display buttons to move to next and previous months
	//onclick handler calles popup's setWidgetMonth function
	var prev = (thismonth == 0) ? 11 : thismonth - 1;
	var next = (thismonth == 11) ? 0 : thismonth + 1;
	var prevmonth = "&lt;&lt; "+this.months[prev].substr(0,3);
	var nextmonth = this.months[next].substr(0,3)+" &gt;&gt;";

	doc.write( "<div class='next'><a onclick='opener.popup.setWidgetMonth(1)'> " +
		   nextmonth + "</a></div>");
	doc.write( "<div class='prev'><a onclick='opener.popup.setWidgetMonth(-1)'> " +
		   prevmonth + "</a></div>&nbsp;");
//	doc.write( "<div class='close'><a onclick='window.close()'>X</a></div>");
	doc.write("</div>");
	doc.write("</body></html>\n");
	doc.close();
}

//setWidgetDay function sets the day displayed
//in the widget to be the one selected in the popup
function popup_setWidgetDay(day)
{
	this.widgetday.value = day;
	this.update();
}

//setWidgetMonth function sets the month displayed
//in the widget to be the one selected in the popup.
//The offset parameter moves the month forward or
//backward by one from the current month.
function popup_setWidgetMonth(offset)
{
	var m = parseInt(this.widgetmonth.options[this.widgetmonth.selectedIndex].value);
	if ((m == 0) && (offset == -1))
	{
		//wrap around to previous year
		m = 12;
		var y = parseInt(this.widgetyear.value);
		this.widgetyear.value = y - 1;
	}
	else if ((m == 11) && (offset == 1))
	{
		//wrap around to next year
		m = 1;
		var y = parseInt(this.widgetyear.value);
		this.widgetyear.value = y + 1;
	}
	else
		m += offset + 1;

	this.widgetmonth.options[m].selected = true;
	
	//check that the day is not greater than the max
	//days in the month to which we are moving.
	//Set it to the new max if it is.
	var d = parseInt(this.widgetday.value);
	if (d > this.getMaxDays(m-1))
		this.widgetday.value = this.getMaxDays(m-1);

	this.update();
}

//getMaxDays fundtion returns the max number of
//days in the given month
function popup_getMaxDays(m)
{
	var thisyear = parseInt(this.widgetyear.value);
	
	//If it's Feb, check if it's a leap year
	if (m == 1)
		return (thisyear % 4) ? 28 : 29;
	
	return this.days[m];
}

/* Do we recognise this browser as one in which the popup
 * functionality works reliably? Currently it only really works in
 * IE */
function browserOK()
{
  var n = navigator.appName;
  var v = navigator.appVersion.substr(0,3);
	
  if ((n == "Microsoft Internet Explorer") && (v >= "4.0"))
    return true;

  if ((n == "Konqueror") && ( v >= "3.0"))
    return true;

  if ((n == "Netscape") && ( v >= "5.0"))
    return true;

  if ( ( n == "Opera") && ( v >= "6.0")) 
     return true; 

  return true;
}


/* close the popup, if open. */
function dateWidgetSubmitHandler()
{
  if ( popup && ! popup.win.closed)
    {
      alert( "Close popup window");
    }
}

