// *****************************************************************************
//   Simple Time Widget - Cross-Browser Javascript pop-up time picker.
//
//   Based on the Copyright (C) 2005-2006  Anthony Garrett
//
//   This library is free software; you can redistribute it and/or
//   modify it under the terms of the GNU Lesser General Public
//   License as published by the Free Software Foundation; either
//   version 2.1 of the License, or (at your option) any later version.
//
//   This library is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//   Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public
//   License along with this library; if not, it is available at
//   the GNU web site (http://www.gnu.org/) or by writing to the
//   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//   Boston, MA  02110-1301  USA
//
// *****************************************************************************
//
// See http://www.tarrget.info/calendar/scw.htm for a complete version history
//
// *****************************************************************************

// ************************************
// Start of Simple Calendar Widget Code
// ************************************

// This date is used throughout to determine today's date.

    var stwDateNow = new Date(Date.parse(new Date().toDateString()));

//******************************************************************************
//------------------------------------------------------------------------------
// Customisation section
//------------------------------------------------------------------------------
//******************************************************************************

    // Set the bounds for the calendar here...
    // If you want the year to roll forward you can use something like this...
    //      var stwBaseYear = stwDateNow.getFullYear()-5;
    // alternatively, hard code a date like this...
    //      var stwBaseYear = 1990;

    // All language-dependent changes can be made here...

    // If you wish to work in a single language (other than English) then
    // just replace the English (in the function stwSetLanguage below) with
    // your own text.

    // Using multiple languages:
    // In order to keep this script to a resonable size I have not included
    // languages here.  You can set language fields in a function that you
    // should call  stwSetLanguage  the script will use your languages.
    // I have included all the translations that have been sent to me in
    // such a function on the demonstration page.

    var stwLanguage;

    function stwSetDefaultLanguage()
        {try
            {stwSetLanguage();}
         catch (exception)
            {// English
             stwToday               = 'Today:';
             stwDrag                = 'click here to drag';
             stwArrHours            = ['12:00am','12:30am','1:00am','1:30am','2:00am','2:30am',
                                       '3:00am','3:30am','4:00am','4:30am','5:00am','5:30am',
                                       '6:00am','6:30am','7:00am','7:30am','8:00am','8:30am',
                                       '9:00am','9:30am','10:00am','10:30am','11:00am','11:30am',
                                       '12:00pm','12:30pm','1:00pm','1:30pm','2:00pm','2:30pm',
                                       '3:00pm','3:30pm','4:00pm','4:30pm','5:00pm','5:30pm',
                                       '6:00pm','6:30pm','7:00pm','7:30pm','8:00pm','8:30pm',
                                       '9:00pm','9:30pm','10:00pm','10:30pm','11:00pm','11:30pm'];
             stwInvalidDateMsg      = 'The entered date is invalid.\n';
             stwOutOfRangeMsg       = 'The entered date is out of range.';
             stwDoesNotExistMsg     = 'The entered date does not exist.';
             stwInvalidAlert        = ['Invalid date (',') ignored.'];
             stwDateDisablingError  = ['Error ',' is not a Date object.'];
             stwRangeDisablingError = ['Error ',
                                       ' should consist of two elements.'];
            }
        }

    // Each of the calendar's alert message types can be disabled
    // independently here.

    var stwShowInvalidDateMsg       = true,
        stwShowOutOfRangeMsg        = true,
        stwShowDoesNotExistMsg      = true,
        stwShowInvalidAlert         = true,
        stwShowDateDisablingError   = true,
        stwShowRangeDisablingError  = true;

    // Set the allowed input date delimiters here...
    // E.g. To set the rising slash, hyphen, full-stop (aka stop or point),
    //      comma and space as delimiters use
    //              var stwArrDelimiters   = ['/','-','.',',',' '];

    var stwArrDelimiters   = ['/','-','.',',',' '];

    // Set the format for the displayed 'Today' date and for the output
    // date here.
    //
    // The format is described using delimiters of your choice (as set
    // in stwArrDelimiters above) and case insensitive letters D, M and Y.
    //
    // Definition               Returns
    // ----------               -------
    // D            date in the month without zero filling
    // DD           date in the month left zero filled
    // M            month number without zero filling
    // MM           month number left zero filled
    // MMM          month string from stwArrMonthNames
    // YY           year number in two digits
    // YYYY         year number in four digits

    // Displayed "Today" date format

    var stwDateDisplayFormat = 'dd mmm yy';     // e.g. 'MMM-DD-YYYY' for the US

    // Output date format

    var stwDateOutputFormat  = 'DD MMM YY'; // e.g. 'MMM-DD-YYYY' for the US

    // The input date is fully parsed so a format is not required,
    // but there is no way to differentiate the sequence reliably.
    //
    // e.g. Is 05/08/03     5th August 2003,
    //                      8th May    2003 or even
    //                      3rd August 2005?
    //
    // So, you have to state how the code should interpret input dates.
    //
    // The sequence should always contain one D, one M and one Y only,
    // in any order.

    var stwDateInputSequence = 'DMY';           // e.g. 'MDY' for the US

    // Note: Because the user may select a date then trigger the
    //       calendar again to select another, it is necessary to
    //       have the input date sequence in the same order as the
    //       output display format.  To allow the flexibility of having
    //       a full input date and a partial (e.g. only Month and Year)
    //       output, the input sequence is set separately.
    //
    //       The same reason determines that the delimiters used should
    //       be in stwArrDelimiters.

    // stwZindex controls how the pop-up calendar interacts with the rest
    // of the page.  It is usually adequate to leave it as 1 (One) but I
    // have made it available here to help anyone who needs to alter the
    // level in order to ensure that the calendar displays correctly in
    // relation to all other elements on the page.

    var stwZindex          = 1;

    // Personally I like the fact that entering 31-Sep-2005 displays
    // 1-Oct-2005, however you may want that to be an error.  If so,
    // set stwBlnStrict = true.  That will cause an error message to
    // display and the selected month is displayed without a selected
    // day. Thanks to Brad Allan for his feedback prompting this feature.

    var stwBlnStrict       = false;

    // If you wish to disable any displayed day, e.g. Every Monday,
    // you can do it by setting the following array.  The array elements
    // match the displayed cells.
    //
    // You could put something like the following in your calling page
    // to disable all weekend days;
    //
    //  for (var i=0;i<stwEnabledDay.length;i++)
    //      {if (i%7%6==0) stwEnabledDay[i] = false;}
    //
    // The above approach will allow you to disable days of the week
    // for the whole of your page easily.  If you need to set different
    // disabled days for a number of date input fields on your page
    // there is an easier way: You can pass additional arguments to
    // stwShow. The syntax is described at the top of this script in
    // the section:
    //    "How to use the Calendar once it is defined for your page:"
    //
    // It is possible to use these two approaches in combination.

    //var stwEnabledDay      = [true, true, true, true, true, true, true,
    //                          true, true, true, true, true, true, true,
    //                          true, true, true, true, true, true, true,
    //                          true, true, true, true, true, true, true,
    //                          true, true, true, true, true, true, true,
    //                          true, true, true, true, true, true, true];

    // You can disable any specific date (e.g. 24-Jan-2006 or Today) by
    // creating an element of the array stwDisabledDates as a date object
    // with the value you want to disable.  Date ranges can be disabled
    // by placing an array of two values (Start and End) into an element
    // of this array.

    var stwDisabledDates   = new Array();

    // e.g. To disable 10-Dec-2005:
    //          stwDisabledDates[0] = new Date(2005,11,10);
    //
    //      or a range from 2004-Dec-25 to 2005-Jan-01:
    //          stwDisabledDates[1] = [new Date(2004,11,25),new Date(2005,0,1)];
    //
    // Remember that Javascript months are Zero-based.

    // The disabling by date and date range does prevent the current day
    // from being selected.  Disabling days of the week does not so you can set
    // the stwActiveToday value to false to prevent selection.

    var stwActiveToday = true;

    // Dates that are out of the displayed month are shown at the start
    // (unless the month starts on the first day of the week) and end of each
    // month.
    //
    // Set stwOutOfMonthDisable to  true  to disable these dates (or  false  
    // to allow their selection).
    //
    // Set stwOutOfMonthHide    to  true  to hide    these dates (or  false  
    // to make them visible).

    var stwOutOfMonthDisable = false;
    var stwOutOfMonthHide    = false;

    // Dates that are out of the specified range can be displayed at the start
    // of the very first month and end of the very last.  Set
    // stwOutOfRangeDisable to  true  to disable these dates (or  false  to
    // allow their selection).

    var stwOutOfRangeDisable = true;

    // You can allow the calendar to be dragged around the screen by
    // using the setting stwAllowDrag to true.
    // I can't say I recommend it because of the danger of the user
    // forgetting which date field the calendar will update when there
    // are multiple date fields on a page.

    var stwAllowDrag = false;

    // Closing the calendar by clicking on it (rather than elsewhere on the
    // main page) can be inconvenient.  The stwClickToHide boolean value
    // controls this feature.

    var stwClickToHide = false;

    // I have made every effort to isolate the pop-up script from any
    // CSS defined on the main page but if you have anything set that
    // affects the pop-up (or you may want to change the way it looks)
    // then you can address it in the following style sheets.

    document.writeln(
        '<style type="text/css">'                                       +
            '.stw           {padding:1px;vertical-align:middle;}'       +
            'iframe.stw     {position:absolute;z-index:' + stwZindex    +
                            ';top:0px;left:0px;visibility:hidden;'      +
                            'width:1px;height:1px;}'                    +
            'div.stw        {padding:0px;visibility:hidden;'            +
                            'position:absolute;cursor:default;'         +
                            'top:0px;left:0px;'                         +
                            'z-index:' + (stwZindex+1)                  +
                            ';text-align:center;}'                      +
        '</style>'  );

    // This style sheet can be extracted from the script and edited into regular
    // CSS (by removing all occurrences of + and '). That can be used as the
    // basis for themes. Classes are described in comments within the style
    // sheet.

    document.writeln(
        '<style type="text/css">'                                       +
            '/* IMPORTANT:  The stw calendar script requires all '      +
            '               the classes defined here.'                  +
            '*/'                                                        +
            'div.stw      {padding:       1px;'                         +
                            'vertical-align:middle;'                    +
                            'border:        ridge 0px;'                 +
                            'font-size:     10pt;'                      +
                            'font-family:   Arial,Helvetica,Sans-Serif;'+
                            'font-weight:   bold;}'                     +
            'select.stwTime     {margin:        1px;}'                  +
            'option             {margin:            2px 30px 2px 0px;'  +
			                     'padding: 2px;}'                       +
            'option:hover {background-color:        #6666CC;}'          +

        '</style>'
                    );

//******************************************************************************
//------------------------------------------------------------------------------
// End of customisation section
//------------------------------------------------------------------------------
//******************************************************************************

//  Variables required by both stwShow and stwShowMonth

    var stwTargetEle,
        stwTriggerEle,
        stwMonthSum            = 0,
        stwBlnFullInputDate    = false,
        stwPassEnabledDay      = new Array(),
        stwSeedDate            = new Date(),
        stwParmActiveToday     = true,
        stwWeekStart           = stwWeekStart%7,
        stwToday,
        stwDrag,
        stwArrMonthNames,
        stwArrWeekInits,
        stwInvalidDateMsg,
        stwOutOfRangeMsg,
        stwDoesNotExistMsg,
        stwInvalidAlert,
        stwDateDisablingError,
        stwRangeDisablingError;

    // Add a method to format a date into the required pattern

    Date.prototype.stwFormat =
        function(stwFormat)
            {var charCount = 0,
                 codeChar  = '',
                 result    = '';

             for (var i=0;i<=stwFormat.length;i++)
                {if (i<stwFormat.length && stwFormat.charAt(i)==codeChar)
                        {// If we haven't hit the end of the string and
                         // the format string character is the same as
                         // the previous one, just clock up one to the
                         // length of the current element definition
                         charCount++;
                        }
                 else   {switch (codeChar)
                            {case 'y': case 'Y':
                                result += (this.getFullYear()%Math.
                                            pow(10,charCount)).toString().
                                            stwPadLeft(charCount);
                                break;
                             case 'm': case 'M':
                                // If we find an M, check the number of them to
                                // determine whether to get the month number or
                                // the month name.
                                result += (charCount<3)
                                            ?(this.getMonth()+1).
                                                toString().stwPadLeft(charCount)
                                            :stwArrMonthNames[this.getMonth()];
                                break;
                             case 'd': case 'D':
                                // If we find a D, get the date and format it
                                result += this.getDate().toString().
                                            stwPadLeft(charCount);
                                break;
                             default:
                                // Copy any unrecognised characters across
                                while (charCount-- > 0) {result += codeChar;}
                            }

                         if (i<stwFormat.length)
                            {// Store the character we have just worked on
                             codeChar  = stwFormat.charAt(i);
                             charCount = 1;
                            }
                        }
                }
             return result;
            }

    // Add a method to left pad zeroes

    String.prototype.stwPadLeft =
        function(padToLength)
            {var result = '';
             for (var i=0;i<(padToLength - this.length);i++) {result += '0';}
             return (result + this);
            }

    // Set up a closure so that any next function can be triggered
    // after the calendar has been closed AND that function can take
    // arguments.

    Function.prototype.runsAfterstw =
        function()  {var func = this,
                         args = new Array(arguments.length);

                     for (var i=0;i<args.length;++i)
                        {args[i] = arguments[i];}

                     return function()
                        {// concat/join the two argument arrays
                         for (var i=0;i<arguments.length;++i)
                            {args[args.length] = arguments[i];}

                         return (args.shift()==stwTriggerEle)
                                    ?func.apply(this, args):null;
                        }
                    };

    // Set up some shortcuts

    function stwID(id)  {return document.getElementById(id);}

    // Use a global variable for the return value from the next action
    // IE fails to pass the function through if the target element is in
    // a form and stwNextAction is not defined.

    var stwNextActionReturn, stwNextAction;

// ****************************************************************************
// Start of Function Library
//
//  Exposed functions:
//
//      stwShow             Entry point for display of calendar,
//                              called in main page.
//      showCal             Legacy name of stwShow:
//                              Passes only legacy arguments,
//                              not the optional day disabling arguments.
//
//      stwShowMonth        Displays a month on the calendar,
//                              Called when a month is set or changed.
//
//      stwBeginDrag        Controls calendar dragging.
//
//      stwCancel           Called when the calendar background is clicked:
//                              Calls stwStopPropagation and may call stwHide.
//      stwHide             Hides the calendar, called on various events.
//      stwStopPropagation  Stops the propagation of an event.
//
// ****************************************************************************

    function showCal(stwEle,stwSourceEle)    {stwShow(stwEle,stwSourceEle);}
    function stwShow(stwEle,stwSourceEle)
        {stwTriggerEle = stwSourceEle;

         // Take any parameters that there might be from the third onwards as
         // day numbers to be disabled 0 = Sunday through to 6 = Saturday.

         stwParmActiveToday = true;

         for (var i=0;i<7;i++)
            {stwPassEnabledDay[(i+7-stwWeekStart)%7] = true;
             for (var j=2;j<arguments.length;j++)
                {if (arguments[j]==i)
                    {stwPassEnabledDay[(i+7-stwWeekStart)%7] = false;
                     if (stwDateNow.getDay()==i) stwParmActiveToday = false;
                    }
                }
            }

         //   If no value is preset then the seed date is
         //      Today (when today is in range) OR
         //      The middle of the date range.

         stwSeedDate = stwDateNow;

         // Find the date and Strip space characters from start and
         // end of date input.

         if (typeof stwEle.value == 'undefined')
            {var stwChildNodes = stwEle.childNodes;
             for (var i=0;i<stwChildNodes.length;i++)
                if (stwChildNodes[i].nodeType == 3)
                    {var stwDateValue = stwChildNodes[i].nodeValue.replace(/^\s+/,'').replace(/\s+$/,'');
                     if (stwDateValue.length > 0)
                        {stwTriggerEle.stwTextNode = stwChildNodes[i];
                         stwTriggerEle.stwLength   = stwChildNodes[i].nodeValue.length;
                         break;
                        }
                    }
            }
         else
            {var stwDateValue = stwEle.value.replace(/^\s+/,'').replace(/\s+$/,'');}

         // Set the language-dependent elements

         stwSetDefaultLanguage();

         // stwID('stwDragText').innerHTML = stwDrag;

         //stwID('stwMonths').options.length = 0;
         //for (var i=0;i<stwArrMonthNames.length;i++)
         //   stwID('stwMonths').options[i] =
         //        new Option(stwArrMonthNames[i],stwArrMonthNames[i]);

         stwID('stwTime').options.length = 0;
         for (var i=0;i<stwArrHours.length;i++)
            stwID('stwTime').options[i] = new Option(stwArrHours[i],stwArrHours[i]);


         // Set the drop down boxes.

         //stwID('stwYears').options.selectedIndex = Math.floor(stwMonthSum/12);
         //stwID('stwMonths').options.selectedIndex = (stwMonthSum%12);
         stwID('stwTime').options.selectedIndex = (24);

         // Opera has a bug with this method of setting the selected index.
         // It requires the following work-around to force SELECTs to display
         // correctly.

         if (window.opera)
            {stwID('stwTime').style.display = 'none';
             stwID('stwTime').style.display = 'block';
            }

         // The bug is apparently known and "fixed for future versions"
         // but they say they aren't going to put the fix into the 9.x
         // series.

         // Check whether or not dragging is allowed and display drag handle
         // if necessary

         //stwID('stwDrag').style.display=
         //    (stwAllowDrag)
         //       ?((stwID('stwIFrame'))?'block':'table-row')
         //       :'none';

         // Display the month

         //stwShowMonth(0);

         // Position the calendar box

         // The object sniffing for Opera allows for the fact that Opera
         // is the only major browser that correctly reports the position
         // of an element in a scrollable DIV.  This is because IE and
         // Firefox omit the DIV from the offsetParent tree.

         stwTargetEle=stwEle;

         var offsetTop =parseInt(stwEle.offsetTop ,10) +
                        parseInt(stwEle.offsetHeight,10),
             offsetLeft=parseInt(stwEle.offsetLeft,10);

         if (!window.opera)
             {while (stwEle.tagName!='BODY' && stwEle.tagName!='HTML')
                 {offsetTop -=parseInt(stwEle.scrollTop, 10);
                  offsetLeft-=parseInt(stwEle.scrollLeft,10);
                  stwEle=stwEle.parentNode;
                 }
              stwEle=stwTargetEle;
             }

         do {stwEle=stwEle.offsetParent;
             offsetTop +=parseInt(stwEle.offsetTop, 10);
             offsetLeft+=parseInt(stwEle.offsetLeft,10);
            }
         while (stwEle.tagName!='BODY' && stwEle.tagName!='HTML');

         stwID('stw').style.top =offsetTop +'px';
         stwID('stw').style.left=offsetLeft+'px';

         if (stwID('stwIframe'))
            {stwID('stwIframe').style.top=offsetTop +'px';
             stwID('stwIframe').style.left=offsetLeft+'px';
             stwID('stwIframe').style.width=(stwID('stw').offsetWidth-2)+'px';
             stwID('stwIframe').style.height=(stwID('stw').offsetHeight-2)+'px';
             stwID('stwIframe').style.visibility='visible';
            }

         // Show it on the page

         stwID('stw').style.visibility='visible';

         // Ensure that Opera actually displays the value that is selected!

         //stwID('stwYears' ).options.selectedIndex = stwID('stwYears' ).options.selectedIndex;
         //stwID('stwMonths').options.selectedIndex = stwID('stwMonths').options.selectedIndex;

         var el = (stwSourceEle.parentNode)
                    ?stwSourceEle.parentNode
                    :stwSourceEle;

         if (typeof event=='undefined')
                {el.addEventListener('click',
                                     stwStopPropagation,
                                     false);
                }
         else   {if (el.attachEvent)
                        {el.attachEvent('onclick',stwStopPropagation);}
                 else   {event.cancelBubble = true;}
                }
        }

    function stwHide()
        {stwID('stw').style.visibility='hidden';
         if (stwID('stwIframe'))
            {stwID('stwIframe').style.visibility='hidden';}

         if (typeof stwNextAction!='undefined' && stwNextAction!=null)
             {stwNextActionReturn = stwNextAction();
              // Explicit null set to prevent closure causing memory leak
              stwNextAction = null;
             }
        }

    function stwCancel(stwEvt)
        {if (stwClickToHide) stwHide();
         stwStopPropagation(stwEvt);
        }

    function stwOutput(stwTime)
        {
		 stwTargetEle.value = stwID('stwTime').options[stwID('stwTime').selectedIndex].text;
         stwHide();
        }

    function stwStopPropagation(stwEvt)
        {if (stwEvt.stopPropagation)
                stwEvt.stopPropagation();     // Capture phase
         else   stwEvt.cancelBubble = true;   // Bubbling phase
        }



// *************************
//  End of Function Library
// *************************
// ***************************
// Start of Calendar structure
// ***************************

    document.write(
     "<!--[if IE]>" +
        "<iframe class='stw' src='/stwblank.html' " +
                "id='stwIframe' name='stwIframe' " +
                "frameborder='0'>" +
        "</iframe>" +
     "<![endif]-->" +
     "<div id='stw' class='stw'>" +
         "<select size='6' id='stwTime' class='stwTime' onclick='stwOutput();'></select>" +
     "</div>");

    if (document.addEventListener)
            {stwID('stw'         ).addEventListener('click',stwCancel,false);
            }
    else    {stwID('stw'         ).attachEvent('onclick',stwCancel);
            }

// ***************************
//  End of Calendar structure
// ***************************
// ****************************************
// Start of document level event definition
// ****************************************

    if (document.addEventListener)
            {document.addEventListener('click',stwHide, false);}
    else    {document.attachEvent('onclick',stwHide);}

// ****************************************
//  End of document level event definition
// ****************************************
// ************************************
//  End of Simple Calendar Widget Code
// ************************************