remove old readme
[atutor.git] / jscripts / lib / calendar.js
1 // *****************************************************************************
2 //      Simple Calendar Widget - Cross-Browser Javascript pop-up calendar.
3 //
4 //   Copyright (C) 2005-2007  Anthony Garrett
5 //
6 //   This library is free software; you can redistribute it and/or
7 //   modify it under the terms of the GNU Lesser General Public
8 //   License as published by the Free Software Foundation; either
9 //   version 2.1 of the License, or (at your option) any later version.
10 //
11 //   This library is distributed in the hope that it will be useful,
12 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //   Lesser General Public License for more details.
15 //
16 //   You should have received a copy of the GNU Lesser General Public
17 //   License along with this library; if not, it is available at
18 //   the GNU web site (http://www.gnu.org/) or by writing to the
19 //   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 //   Boston, MA  02110-1301  USA
21 //
22 // *****************************************************************************
23 //
24 // Contact:   Sorry, I can't offer support for this but if you find a problem
25 //            (or just want to tell me how useful you find it), please send
26 //            me an email at scwfeedback@tarrget.info (Note the two Rs in
27 //            tarrget).  I will try to fix problems quickly but this is a
28 //            spare time thing for me.
29 //
30 // Credits:   I wrote this from scratch myself but I couldn't have done it
31 //            without the superb "JavaScript The Definitive Guide" by David
32 //            Flanagan (Pub. O'Reilly ISBN 0-596-00048-0).  I also recognise
33 //            a contribution from my experience with PopCalendar 4.1 by
34 //            Liming(Victor) Weng.
35 //
36 // Link back: Please give me credit and link back to my page.  To ensure that
37 //            search engines give my page a higher ranking you can add the
38 //            following HTML to any indexed page on your web site:
39 //
40 //            <A HREF="http://www.tarrget.info/calendar/scw.htm">
41 //              Simple Calendar Widget by Anthony Garrett
42 //            </A>
43 //
44 // Features:  Easily customised
45 //                  (output date format, colours, language, year range and
46 //                   week start day)
47 //            Accepts a date as input
48 //                  (see comments below for formats).
49 //            Cross-browser code tested against;
50 //                  Internet Explorer 6.0.28     Mozilla  1.7.1
51 //                  Opera             7.52+      Firefox  0.9.1+
52 //                  Konqueror         3.4.0      Flock    0.4.9
53 //
54 // How to add the Calendar to your page:
55 //            This script needs to be defined for your page so, immediately
56 //            after the BODY tag add the following line;
57 //
58 //                  <script type='Text/JavaScript' src='scw.js'></script>
59 //
60 //            Your root directory of the web site should also contain an empty
61 //            file called "scwblank.html". See
62 //                  http://www.tarrget.info/calendar/IEnightmare.html
63 //            for a full explanation.
64 //
65 // How to use the Calendar once it is defined for your page:
66 //
67 //            Simply choose an event to trigger the calendar (like an onClick
68 //            or an onMouseOver) and an element to work on (for the calendar
69 //            to take its initial date from and write its output date to) then
70 //            write it like this;
71 //
72 //                  <<event>>="scwShow(<<element>>,event);"
73 //
74 //            e.g. onClick="scwShow(scwID('myElement'),event);"
75 //            or   onMouseOver="scwShow(this,event);"
76 //
77 //            NOTE: If you wish to use the calendar with an Anchor tag, do
78 //                  not use the syntax:   href="javascript:scwShow(...)"
79 //                  Instead you should use the following;
80 //
81 //                  <a href="#" onclick="scwShow(<<element>>,event);return false;">
82 //                      <<your text>>
83 //                  </a>
84 //
85 //            If you are using a text node then specify the text's parent node
86 //            in the function call. The date should be the only text under that
87 //            node;
88 //
89 //            e.g.  <p onclick="scwShow(this,event);"><<date>></p>
90 //
91 //            You can also disable days of the week by adding arguments to the
92 //            call to scwShow.  The values should be Sunday = 0 through to
93 //            Saturday = 6.  A call to scwShow with Friday and Monday disabled
94 //            would look something like this;
95 //
96 //                  scwShow(<<element>>,event,5,1);
97 //
98 //            Finally you can use the following technique to run a function
99 //            when the calendar closes:
100 //
101 //                  scwNextAction=<<function>>.runsAfterSCW(this,<<arguments>>);
102 //                  scwShow(<<element>>,event <<,optional arguments above>>);
103 //
104 //            Where <<function>> is a function defined on the calling page
105 //            and <<arguments>> is the list of arguments being passed to that
106 //            function.
107 //
108 //      No event? No problem!
109 //
110 //            Normally the calendar will be triggered by an event but if you wish to
111 //            control it in code and the event is not available to you, simply pass
112 //            an element as the second parameter;
113 //
114 //            E.G.  scwShow(<<target element>>,<<source element>>);
115 //                  as in: scwShow(this,this);
116 //
117 //            ------------------------------------------------------------------
118 //            Here's an extremely trivial but fully functioning example page
119 //            showing two of the ways to trigger the calendar;
120 //
121 //            <html>
122 //                <head><title>Basic Example</title></head>
123 //                <body>
124 //                    <script type='text/JavaScript' src='scw.js'></script>
125 //                    <p onclick='scwShow(this,event);'>06-Dec-2006</p>
126 //                    <input onclick='scwShow(this,event);' value='07-Dec-2006' />
127 //                    <br/><br/>
128 //                    <a href='#' onclick='scwShow(this,event);return false;'>
129 //                        08-Dec-2006
130 //                    </a>
131 //                </body>
132 //            </html>
133 //
134 // *****************************************************************************
135 //
136 // See http://www.tarrget.info/calendar/scw.htm for a complete version history
137 //
138 // Version   Date        By               Description
139 // =======   ====        ===============  ===========
140 //   3.58    2007-04-04  Anthony Garrett  Resolved an error caused when the date
141 //                                         range does not include the current year.
142 //                                         Thanks to Steve Davis for letting me know.
143 //
144 //                                        Fixed "Today" selector display which
145 //                                         was incorrectly visible when year range
146 //                                         ended last year. (Also the result of
147 //                                         investigations based on Steve Davis'
148 //                                         feedback).
149 //
150 //   3.59    2007-06-13  Anthony Garrett  Added Verdana to font list of
151 //                                         calendar's CSS.  Resolves rendering
152 //                                         bug in Safari Beta 3 for Windows.
153 //
154 //   3.60    2007-07-31  Anthony Garrett  Fixed javascript error that occurred
155 //                                         when the target element had no value
156 //                                         attribute.  The error had no impact
157 //                                         on the behaviour of the script.  Thanks
158 //                                         to John Phelps for reporting this bug.
159 //
160 //   3.70    2007-09-21  Anthony Garrett  Updated the event trapping to make it
161 //                                         less intrusive on the page body.
162 //                                         NOTE: This requires that a calendar's
163 //                                         second parameter should be the calling
164 //                                         event (not the calling object as in
165 //                                         previous versions).
166 //                                         Thanks to Steve Davis for the bug report
167 //                                         that led to this change.
168 //
169 //                                        Fixed a bug that caused undelimited
170 //                                         dates to be handled incorrectly. They
171 //                                         are now parsed against the full date
172 //                                         output format then checked for validity.
173 //                                         Thanks to Dan Wood for raising this bug.
174 //
175 //                                        Replaced the date input sequence user
176 //                                         configuration setting with parsing the
177 //                                         sequence from the full format. New users
178 //                                         are often confused by the sequence and
179 //                                         in practice (to allow the calendar's date
180 //                                         output to be used for input) the sequence
181 //                                         must always match the full format element
182 //                                         order.
183 //
184 //                                        Extended IFRAME backing to all calendar objects
185 //                                         in order to improve calendar display over
186 //                                         some embedded applets and objects.  Thanks to
187 //                                         Stanko Kupcevic for his feedback on this.
188 //                                         NOTE: It is not possible to protect any
189 //                                         JavaScript object displayed over an
190 //                                         embedded DYNAMIC (and, therefore refreshed)
191 //                                         object because browsers usually do not
192 //                                         directly control the screen handling within
193 //                                         the object.  The best advice therefore remains
194 //                                         to design pages in such a way that the calendar
195 //                                         does not overlap embedded objects.
196 //
197 //  3.71     2008-12-14  Anthony Garrett  Restored the ability to use an element
198 //                                         as the second parameter when opening a
199 //                                         calendar while retaining the option
200 //                                         of passing an event. Thanks to Thierry Blind
201 //                                         and Sergey Snovsky for the feedback.
202 //
203 // *****************************************************************************
204
205 // ************************************
206 // Start of Simple Calendar Widget Code
207 // ************************************
208
209 // This date is used throughout to determine today's date.
210
211     var scwDateNow = new Date(Date.parse(new Date().toDateString()));
212
213 //******************************************************************************
214 //------------------------------------------------------------------------------
215 // Customisation section
216 //------------------------------------------------------------------------------
217 //******************************************************************************
218
219     // Set the bounds for the calendar here...
220     // If you want the year to roll forward you can use something like this...
221     //      var scwBaseYear = scwDateNow.getFullYear()-5;
222     // alternatively, hard code a date like this...
223     //      var scwBaseYear = 1990;
224
225     var scwBaseYear        = scwDateNow.getFullYear()-10;
226
227     // How many years do want to be valid and to show in the drop-down list?
228
229     var scwDropDownYears   = 20;
230
231     // All language-dependent changes can be made here...
232
233     // If you wish to work in a single language (other than English) then
234     // just replace the English (in the function scwSetLanguage below) with
235     // your own text.
236
237     // Using multiple languages:
238     // In order to keep this script to a resonable size I have not included
239     // languages here.  You can set language fields in a function that you
240     // should call  scwSetLanguage  the script will use your languages.
241     // I have included all the translations that have been sent to me in
242     // such a function on the demonstration page.
243
244     var scwLanguage;
245
246     function scwSetDefaultLanguage()
247         {try
248             {scwSetLanguage();}
249          catch (exception)
250             {// English
251              scwToday               = 'Today:';
252              scwDrag                = 'click here to drag';
253              scwArrMonthNames       = ['Jan','Feb','Mar','Apr','May','Jun',
254                                        'Jul','Aug','Sep','Oct','Nov','Dec'];
255              scwArrWeekInits        = ['S','M','T','W','T','F','S'];
256              scwInvalidDateMsg      = 'The entered date is invalid.\n';
257              scwOutOfRangeMsg       = 'The entered date is out of range.';
258              scwDoesNotExistMsg     = 'The entered date does not exist.';
259              scwInvalidAlert        = ['Invalid date (',') ignored.'];
260              scwDateDisablingError  = ['Error ',' is not a Date object.'];
261              scwRangeDisablingError = ['Error ',
262                                        ' should consist of two elements.'];
263             }
264         };
265
266     // Note:  Always start the scwArrWeekInits array with your string for
267     //        Sunday whatever scwWeekStart (below) is set to.
268
269     // scwWeekStart determines the start of the week in the display
270     // Set it to: 0 (Zero) for Sunday, 1 (One) for Monday etc..
271
272     var scwWeekStart       =    1;
273
274     // The week start day for the display is taken as the week start
275     // for week numbering.  This ensures that only one week number
276     // applies to one line of the calendar table.
277     // [ISO 8601 begins the week with Day 1 = Monday.]
278
279     // If you want to see week numbering on the calendar, set
280     // this to true.  If not, false.
281
282     var scwWeekNumberDisplay    = false;
283
284     // Week numbering rules are generally based on a day in the week
285     // that determines the first week of the year.  ISO 8601 uses
286     // Thursday (day four when Sunday is day zero).  You can alter
287     // the base day here.
288
289     // See http://www.cl.cam.ac.uk/~mgk25/iso-time.html for more information
290
291     var scwWeekNumberBaseDay    = 4;
292
293     // Each of the calendar's alert message types can be disabled
294     // independently here.
295
296     var scwShowInvalidDateMsg       = true,
297         scwShowOutOfRangeMsg        = true,
298         scwShowDoesNotExistMsg      = true,
299         scwShowInvalidAlert         = true,
300         scwShowDateDisablingError   = true,
301         scwShowRangeDisablingError  = true;
302
303     // Set the allowed input date delimiters here...
304     // E.g. To set the rising slash, hyphen, full-stop (aka stop or point),
305     //      comma and space as delimiters use
306     //              var scwArrDelimiters   = ['/','-','.',',',' '];
307
308     var scwArrDelimiters   = ['/','-','.',',',' '];
309
310     // Set the format for the displayed 'Today' date and for the output
311     // date here.
312     //
313     // The format is described using delimiters of your choice (as set
314     // in scwArrDelimiters above) and case insensitive letters D, M and Y.
315     //
316     // NOTE: If no delimiters are input then the date output format is used
317     //       to parse the value.  This allows less flexiblility in the input
318     //       value than using delimiters but an accurately entered date
319     //       remains parsable.
320     //
321     // Definition               Returns
322     // ----------               -------
323     // D            date in the month without zero filling
324     // DD           date in the month left zero filled
325     // M            month number without zero filling
326     // MM           month number left zero filled
327     // MMM          month string from scwArrMonthNames
328     // YY           year number in two digits
329     // YYYY         year number in four digits
330
331     // Displayed "Today" date format
332
333     //var scwDateDisplayFormat = 'dd-mm-yy';     // e.g. 'MMM-DD-YYYY' for the US
334     var scwDateDisplayFormat = 'dd/mmm/yyyy';     // e.g. 'MMM-DD-YYYY' for the US
335
336     // Output date format
337
338     //var scwDateOutputFormat  = 'DD MMM, YYYY'; // e.g. 'MMM-DD-YYYY' for the US
339     //var scwDateOutputFormat  = 'DD-MMM-YYYY'; // e.g. 'MMM-DD-YYYY' for the US
340     var scwDateOutputFormat  = 'YYYY-MM-DD'; // e.g. 'MMM-DD-YYYY' for the US
341
342     // Note: The delimiters used should be in scwArrDelimiters.
343
344     // scwZindex controls how the pop-up calendar interacts with the rest
345     // of the page.  It is usually adequate to leave it as 1 (One) but I
346     // have made it available here to help anyone who needs to alter the
347     // level in order to ensure that the calendar displays correctly in
348     // relation to all other elements on the page.
349
350     var scwZindex          = 1;
351
352     // Personally I like the fact that entering 31-Sep-2005 displays
353     // 1-Oct-2005, however you may want that to be an error.  If so,
354     // set scwBlnStrict = true.  That will cause an error message to
355     // display and the selected month is displayed without a selected
356     // day. Thanks to Brad Allan for his feedback prompting this feature.
357
358     var scwBlnStrict       = false;
359
360     // If you wish to disable any displayed day, e.g. Every Monday,
361     // you can do it by setting the following array.  The array elements
362     // match the displayed cells.
363     //
364     // You could put something like the following in your calling page
365     // to disable all weekend days;
366     //
367     //  for (var i=0;i<scwEnabledDay.length;i++)
368     //      {if (i%7%6==0) scwEnabledDay[i] = false;}
369     //
370     // The above approach will allow you to disable days of the week
371     // for the whole of your page easily.  If you need to set different
372     // disabled days for a number of date input fields on your page
373     // there is an easier way: You can pass additional arguments to
374     // scwShow. The syntax is described at the top of this script in
375     // the section:
376     //    "How to use the Calendar once it is defined for your page:"
377     //
378     // It is possible to use these two approaches in combination.
379
380     var scwEnabledDay      = [true, true, true, true, true, true, true,
381                               true, true, true, true, true, true, true,
382                               true, true, true, true, true, true, true,
383                               true, true, true, true, true, true, true,
384                               true, true, true, true, true, true, true,
385                               true, true, true, true, true, true, true];
386
387     // You can disable any specific date (e.g. 24-Jan-2006 or Today) by
388     // creating an element of the array scwDisabledDates as a date object
389     // with the value you want to disable.  Date ranges can be disabled
390     // by placing an array of two values (Start and End) into an element
391     // of this array.
392
393     var scwDisabledDates   = new Array();
394
395     // e.g. To disable 10-Dec-2005:
396     //          scwDisabledDates[0] = new Date(2005,11,10);
397     //
398     //      or a range from 2004-Dec-25 to 2005-Jan-01:
399     //          scwDisabledDates[1] = [new Date(2004,11,25),new Date(2005,0,1)];
400     //
401     // Remember that Javascript months are Zero-based.
402
403     // The disabling by date and date range does prevent the current day
404     // from being selected.  Disabling days of the week does not so you can set
405     // the scwActiveToday value to false to prevent selection.
406
407     var scwActiveToday = true;
408
409     // Dates that are out of the displayed month are shown at the start
410     // (unless the month starts on the first day of the week) and end of each
411     // month.
412     //
413     // Set scwOutOfMonthDisable to  true  to disable these dates (or  false
414     // to allow their selection).
415     //
416     // Set scwOutOfMonthHide    to  true  to hide    these dates (or  false
417     // to make them visible).
418
419     var scwOutOfMonthDisable = false;
420     var scwOutOfMonthHide    = false;
421
422     // Dates that are out of the specified range can be displayed at the start
423     // of the very first month and end of the very last.  Set
424     // scwOutOfRangeDisable to  true  to disable these dates (or  false  to
425     // allow their selection).
426
427     var scwOutOfRangeDisable = true;
428
429     // If you want a special format for the cell that contains the current day
430     // set this to true.  This sets a thin border around the cell in the colour
431     // set by scwTodayCellBorderColour.
432
433     var scwFormatTodayCell = true;
434     var scwTodayCellBorderColour = 'red';
435
436     // You can allow the calendar to be dragged around the screen by
437     // using the setting scwAllowDrag to true.
438     // I can't say I recommend it because of the danger of the user
439     // forgetting which date field the calendar will update when there
440     // are multiple date fields on a page.
441
442     var scwAllowDrag = false;
443
444     // Closing the calendar by clicking on it (rather than elsewhere on the
445     // main page) can be inconvenient.  The scwClickToHide boolean value
446     // controls this feature.
447
448     var scwClickToHide = false;
449
450     // I have made every effort to isolate the pop-up script from any
451     // CSS defined on the main page but if you have anything set that
452     // affects the pop-up (or you may want to change the way it looks)
453     // then you can address it in the following style sheets.
454
455     document.writeln(
456         '<style type="text/css">'                                       +
457             '.scw           {padding:1px;vertical-align:middle;}'       +
458             'iframe.scw     {position:absolute;z-index:' + scwZindex    +
459                             ';top:0px;left:0px;visibility:hidden;'      +
460                             'width:1px;height:1px;}'                    +
461             'table.scw      {padding:0px;visibility:hidden;'            +
462                             'position:absolute;cursor:default;'         +
463                             'width:200px;top:0px;left:0px;'             +
464                             'z-index:' + (scwZindex+1)                  +
465                             ';text-align:center;}'                      +
466         '</style>'  );
467
468     // This style sheet can be extracted from the script and edited into regular
469     // CSS (by removing all occurrences of + and '). That can be used as the
470     // basis for themes. Classes are described in comments within the style
471     // sheet.
472
473     document.writeln(
474         '<style type="text/css">'                                       +
475             '/* IMPORTANT:  The SCW calendar script requires all '      +
476             '               the classes defined here.'                  +
477             '*/'                                                        +
478             'table.scw      {padding:       1px;'                       +
479                             'vertical-align:middle;'                    +
480                             'border:        ridge 2px;'                 +
481                             'font-size:     10pt;'                      +
482                             'font-family:   ' +
483                                    'Verdana,Arial,Helvetica,Sans-Serif;'+
484                             'font-weight:   bold;}'                     +
485             'td.scwDrag,'                                               +
486             'td.scwHead                 {padding:       0px 0px;'       +
487                                         'text-align:    center;}'       +
488             'td.scwDrag                 {font-size:     8pt;}'          +
489             'select.scwHead             {margin:        3px 1px;'       +
490                                         'text-align:    center;}'       +
491             'input.scwHead              {height:        22px;'          +
492                                         'width:         22px;'          +
493                                         'vertical-align:middle;'        +
494                                         'text-align:    center;'        +
495                                         'margin:        2px 1px;'       +
496                                         'font-weight:   bold;'          +
497                                         'font-size:     10pt;'          +
498                                         'font-family:   fixedSys;}'     +
499             'td.scwWeekNumberHead,'                                     +
500             'td.scwWeek                 {padding:       0px;'           +
501                                         'text-align:    center;'        +
502                                         'font-weight:   bold;}'         +
503             'td.scwFoot,'                                               +
504             'td.scwFootHover,'                                          +
505             'td.scwFoot:hover,'                                         +
506             'td.scwFootDisabled         {padding:       0px;'           +
507                                         'text-align:    center;'        +
508                                         'font-weight:   normal;}'       +
509             'table.scwCells             {text-align:    right;'         +
510                                         'font-size:     8pt;'           +
511                                         'width:         96%;}'          +
512             'td.scwCells,'                  +
513             'td.scwCellsHover,'             +
514             'td.scwCells:hover,'            +
515             'td.scwCellsDisabled,'          +
516             'td.scwCellsExMonth,'           +
517             'td.scwCellsExMonthHover,'      +
518             'td.scwCellsExMonth:hover,'     +
519             'td.scwCellsExMonthDisabled,'   +
520             'td.scwCellsWeekend,'           +
521             'td.scwCellsWeekendHover,'      +
522             'td.scwCellsWeekend:hover,'     +
523             'td.scwCellsWeekendDisabled,'   +
524             'td.scwInputDate,'              +
525             'td.scwInputDateHover,'         +
526             'td.scwInputDate:hover,'        +
527             'td.scwInputDateDisabled,'      +
528             'td.scwWeekNo,'                 +
529             'td.scwWeeks                {padding:           3px;'       +
530                                         'width:             16px;'      +
531                                         'height:            16px;'      +
532                                         'border-width:      1px;'       +
533                                         'border-style:      solid;'     +
534                                         'font-weight:       bold;'      +
535                                         'vertical-align:    middle;}'   +
536             '/* Blend the colours into your page here...    */'         +
537             '/* Calendar background */'                                 +
538             'table.scw                  {background-color:  #CCCCCC;}'  +
539             '/* Drag Handle */'                                         +
540             'td.scwDrag                 {background-color:  #9999CC;'   +
541                                         'color:             #CCCCFF;}'  +
542             '/* Week number heading */'                                 +
543             'td.scwWeekNumberHead       {color:             #6666CC;}'  +
544             '/* Week day headings */'                                   +
545             'td.scwWeek                 {color:             #CCCCCC;}'  +
546             '/* Week numbers */'                                        +
547             'td.scwWeekNo               {background-color:  #776677;'   +
548                                         'color:             #CCCCCC;}'  +
549             '/* Enabled Days */'                                        +
550             '/* Week Day */'                                            +
551             'td.scwCells                {background-color:  #CCCCCC;'   +
552                                         'color:             #000000;}'  +
553             '/* Day matching the input date */'                         +
554             'td.scwInputDate            {background-color:  #CC9999;'   +
555                                         'color:             #FF0000;}'  +
556             '/* Weekend Day */'                                         +
557             'td.scwCellsWeekend         {background-color:  #CCCCCC;'   +
558                                         'color:             #CC6666;}'  +
559             '/* Day outside the current month */'                       +
560             'td.scwCellsExMonth         {background-color:  #CCCCCC;'   +
561                                         'color:             #666666;}'  +
562             '/* Today selector */'                                      +
563             'td.scwFoot                 {background-color:  #6666CC;'   +
564                                         'color:             #FFFFFF;}'  +
565             '/* MouseOver/Hover formatting '                            +
566             '       If you want to "turn off" any of the formatting '   +
567             '       then just set to the same as the standard format'   +
568             '       above.'                                             +
569             ' '                                                         +
570             '       Note: The reason that the following are'            +
571             '       implemented using both a class and a :hover'        +
572             '       pseudoclass is because Opera handles the rendering' +
573             '       involved in the class swap very poorly and IE6 '    +
574             '       (and below) only implements pseudoclasses on the'   +
575             '       anchor tag.'                                        +
576             '*/'                                                        +
577             '/* Active cells */'                                        +
578             'td.scwCells:hover,'                                        +
579             'td.scwCellsHover           {background-color:  #FFFF00;'   +
580                                         'cursor:            pointer;'   +
581                                         'color:             #000000;}'  +
582             '/* Day matching the input date */'                         +
583             'td.scwInputDate:hover,'                                    +
584             'td.scwInputDateHover       {background-color:  #FFFF00;'   +
585                                         'cursor:            pointer;'   +
586                                         'color:             #000000;}'  +
587             '/* Weekend cells */'                                       +
588             'td.scwCellsWeekend:hover,'                                 +
589             'td.scwCellsWeekendHover    {background-color:  #FFFF00;'   +
590                                         'cursor:            pointer;'   +
591                                         'color:             #000000;}'  +
592             '/* Day outside the current month */'                       +
593             'td.scwCellsExMonth:hover,'                                 +
594             'td.scwCellsExMonthHover    {background-color:  #FFFF00;'   +
595                                         'cursor:            pointer;'   +
596                                         'color:             #000000;}'  +
597             '/* Today selector */'                                      +
598             'td.scwFoot:hover,'                                         +
599             'td.scwFootHover            {color:             #FFFF00;'   +
600                                         'cursor:            pointer;'   +
601                                         'font-weight:       bold;}'     +
602             '/* Disabled cells */'                                      +
603             '/* Week Day */'                                            +
604             '/* Day matching the input date */'                         +
605             'td.scwInputDateDisabled    {background-color:  #999999;'   +
606                                         'color:             #000000;}'  +
607             'td.scwCellsDisabled        {background-color:  #999999;'   +
608                                         'color:             #000000;}'  +
609             '/* Weekend Day */'                                         +
610             'td.scwCellsWeekendDisabled {background-color:  #999999;'   +
611                                         'color:             #CC6666;}'  +
612             '/* Day outside the current month */'                       +
613             'td.scwCellsExMonthDisabled {background-color:  #999999;'   +
614                                         'color:             #666666;}'  +
615             'td.scwFootDisabled         {background-color:  #6666CC;'   +
616                                         'color:             #FFFFFF;}'  +
617         '</style>'
618                     );
619
620 //******************************************************************************
621 //------------------------------------------------------------------------------
622 // End of customisation section
623 //------------------------------------------------------------------------------
624 //******************************************************************************
625
626 //  Variables required by both scwShow and scwShowMonth
627
628     var scwTargetEle,
629         scwTriggerEle,
630         scwMonthSum            = 0,
631         scwBlnFullInputDate    = false,
632         scwPassEnabledDay      = new Array(),
633         scwSeedDate            = new Date(),
634         scwParmActiveToday     = true,
635         scwWeekStart           = scwWeekStart%7,
636         scwToday,
637         scwDrag,
638         scwArrMonthNames,
639         scwArrWeekInits,
640         scwInvalidDateMsg,
641         scwOutOfRangeMsg,
642         scwDoesNotExistMsg,
643         scwInvalidAlert,
644         scwDateDisablingError,
645         scwRangeDisablingError;
646
647     // Add a method to format a date into the required pattern
648
649     Date.prototype.scwFormat =
650         function(scwFormat)
651             {var charCount = 0,
652                  codeChar  = '',
653                  result    = '';
654
655              for (var i=0;i<=scwFormat.length;i++)
656                 {if (i<scwFormat.length && scwFormat.charAt(i)==codeChar)
657                         {// If we haven't hit the end of the string and
658                          // the format string character is the same as
659                          // the previous one, just clock up one to the
660                          // length of the current element definition
661                          charCount++;
662                         }
663                  else   {switch (codeChar)
664                             {case 'y': case 'Y':
665                                 result += (this.getFullYear()%Math.
666                                             pow(10,charCount)).toString().
667                                             scwPadLeft(charCount);
668                                 break;
669                              case 'm': case 'M':
670                                 // If we find an M, check the number of them to
671                                 // determine whether to get the month number or
672                                 // the month name.
673                                 result += (charCount<3)
674                                             ?(this.getMonth()+1).
675                                                 toString().scwPadLeft(charCount)
676                                             :scwArrMonthNames[this.getMonth()];
677                                 break;
678                              case 'd': case 'D':
679                                 // If we find a D, get the date and format it
680                                 result += this.getDate().toString().
681                                             scwPadLeft(charCount);
682                                 break;
683                              default:
684                                 // Copy any unrecognised characters across
685                                 while (charCount-- > 0) {result += codeChar;}
686                             }
687
688                          if (i<scwFormat.length)
689                             {// Store the character we have just worked on
690                              codeChar  = scwFormat.charAt(i);
691                              charCount = 1;
692                             }
693                         }
694                 }
695              return result;
696             };
697
698     // Add a method to left pad zeroes
699
700     String.prototype.scwPadLeft =
701         function(padToLength)
702             {var result = '';
703              for (var i=0;i<(padToLength - this.length);i++) {result += '0';}
704              return (result + this);
705             };
706
707     // Set up a closure so that any next function can be triggered
708     // after the calendar has been closed AND that function can take
709     // arguments.
710
711     Function.prototype.runsAfterSCW =
712         function()  {var func = this,
713                          args = new Array(arguments.length);
714
715                      for (var i=0;i<args.length;++i) {args[i] = arguments[i];}
716
717                      return function()
718                         {// concat/join the two argument arrays
719                          for (var i=0;i<arguments.length;++i) {args[args.length] = arguments[i];}
720                          return (args.shift()==scwTriggerEle)?func.apply(this, args):null;
721                         };
722                     };
723
724     // Set up some shortcuts
725
726     function scwID(id)  {return document.getElementById(id);};
727
728     // Use a global variable for the return value from the next action
729     // IE fails to pass the function through if the target element is in
730     // a form and scwNextAction is not defined.
731
732     var scwNextActionReturn, scwNextAction;
733
734 // ****************************************************************************
735 // Start of Function Library
736 //
737 //  Exposed functions:
738 //
739 //      scwShow             Entry point for display of calendar,
740 //                              called in main page.
741 //      showCal             Legacy name of scwShow:
742 //                              Passes only legacy arguments,
743 //                              not the optional day disabling arguments.
744 //
745 //      scwShowMonth        Displays a month on the calendar,
746 //                              Called when a month is set or changed.
747 //
748 //      scwBeginDrag        Controls calendar dragging.
749 //
750 //      scwCancel           Called when the calendar background is clicked:
751 //                              Calls scwStopPropagation and may call scwHide.
752 //      scwHide             Hides the calendar, called on various events.
753 //      scwStopPropagation  Stops the propagation of an event.
754 //
755 // ****************************************************************************
756
757     function showCal(scwEle,scwSource) {scwShow(scwEle,scwSource);};
758     function scwShow(scwEle,scwSource)
759         {if (!scwSource) {scwSource = window.event;}
760
761          if (scwSource.tagName) // Second parameter isn't an event it's an element
762             {var scwSourceEle = scwSource;
763
764              if (scwID('scwIE'))  {window.event.cancelBubble = true;}
765              else {scwSourceEle.parentNode.addEventListener('click',scwStopPropagation,false);}
766             }
767          else   // Second parameter is an event
768             {var scwSourceEle = (scwSource.target)
769                                     ?scwSource.target
770                                     :scwSource.srcElement;
771
772              // Stop the click event that opens the calendar from bubbling up to
773              // the document-level event handler that hides it!
774              if (scwSource.stopPropagation) {scwSource.stopPropagation();}
775              else                           {scwSource.cancelBubble = true;}
776             }
777
778          scwTriggerEle = scwSourceEle;
779
780          // Take any parameters that there might be from the third onwards as
781          // day numbers to be disabled 0 = Sunday through to 6 = Saturday.
782
783          scwParmActiveToday = true;
784
785          for (var i=0;i<7;i++)
786             {scwPassEnabledDay[(i+7-scwWeekStart)%7] = true;
787              for (var j=2;j<arguments.length;j++)
788                 {if (arguments[j]==i)
789                     {scwPassEnabledDay[(i+7-scwWeekStart)%7] = false;
790                      if (scwDateNow.getDay()==i) {scwParmActiveToday = false;}
791                     }
792                 }
793             }
794
795          //   If no value is preset then the seed date is
796          //      Today (when today is in range) OR
797          //      The middle of the date range.
798
799          scwSeedDate = scwDateNow;
800
801          // Find the date and Strip space characters from start and
802          // end of date input.
803
804          var scwDateValue = '';
805
806          if (scwEle.value) {scwDateValue = scwEle.value.replace(/^\s+/,'').replace(/\s+$/,'');}
807          else   {if (typeof scwEle.value == 'undefined')
808                     {var scwChildNodes = scwEle.childNodes;
809                      for (var i=0;i<scwChildNodes.length;i++)
810                         {if (scwChildNodes[i].nodeType == 3)
811                             {scwDateValue = scwChildNodes[i].nodeValue.replace(/^\s+/,'').replace(/\s+$/,'');
812                              if (scwDateValue.length > 0)
813                                 {scwTriggerEle.scwTextNode = scwChildNodes[i];
814                                  scwTriggerEle.scwLength   = scwChildNodes[i].nodeValue.length;
815                                  break;
816                                 }
817                             }
818                         }
819                     }
820                 }
821
822          // Set the language-dependent elements
823
824          scwSetDefaultLanguage();
825
826          scwID('scwDragText').innerHTML = scwDrag;
827
828          scwID('scwMonths').options.length = 0;
829          for (var i=0;i<scwArrMonthNames.length;i++)
830             {scwID('scwMonths').options[i] = new Option(scwArrMonthNames[i],scwArrMonthNames[i]);}
831
832          scwID('scwYears').options.length = 0;
833          for (var i=0;i<scwDropDownYears;i++)
834             {scwID('scwYears').options[i] =  new Option((scwBaseYear+i),(scwBaseYear+i));}
835
836          for (var i=0;i<scwArrWeekInits.length;i++)
837             {scwID('scwWeekInit' + i).innerHTML = scwArrWeekInits[(i+scwWeekStart)%scwArrWeekInits.length];}
838
839          if (scwID('scwFoot'))
840             {scwID('scwFoot').innerHTML = scwToday + ' ' + scwDateNow.scwFormat(scwDateDisplayFormat);}
841
842          if (scwDateValue.length==0)
843             {// If no value is entered and today is within the range,
844              // use today's date, otherwise use the middle of the valid range.
845
846              scwBlnFullInputDate=false;
847
848              if ((new Date(scwBaseYear+scwDropDownYears,0,0))<scwSeedDate ||
849                  (new Date(scwBaseYear,0,1))                 >scwSeedDate
850                 )
851                 {scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears / 2), 5, 1);}
852             }
853          else
854             {function scwInputFormat()
855                 {var scwArrSeed = new Array(),
856                      scwArrInput = scwDateValue.split(new RegExp('[\\'+scwArrDelimiters.join('\\')+']+','g'));
857
858                  // "Escape" all the user defined date delimiters above -
859                  // several delimiters will need it and it does no harm for
860                  // the others.
861
862                  // Strip any empty array elements (caused by delimiters)
863                  // from the beginning or end of the array. They will
864                  // still appear in the output string if in the output
865                  // format.
866
867                  if (scwArrInput[0]!=null)
868                     {if (scwArrInput[0].length==0)                      {scwArrInput.splice(0,1);}
869                      if (scwArrInput[scwArrInput.length-1].length==0)   {scwArrInput.splice(scwArrInput.length-1,1);}
870                     }
871
872                  scwBlnFullInputDate = false;
873
874                  scwDateOutputFormat = scwDateOutputFormat.toUpperCase();
875
876                  // List all the allowed letters in the date format
877                  var template = ['D','M','Y'];
878
879                  // Prepare the sequence of date input elements
880                  var result = new Array();
881
882                  for (var i=0;i<template.length;i++)
883                     {if (scwDateOutputFormat.search(template[i])>-1)
884                         {result[scwDateOutputFormat.search(template[i])] = template[i];}
885                     }
886
887                  var scwDateSequence = result.join('');
888
889                  // Separate the elements of the date input
890                  switch (scwArrInput.length)
891                     {case 1:
892                         {if (scwDateOutputFormat.indexOf('Y')>-1 &&
893                              scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('Y'))
894                             {scwArrSeed[0] = parseInt(scwArrInput[0].substring(scwDateOutputFormat.indexOf('Y'),
895                                                                                scwDateOutputFormat.lastIndexOf('Y')+1),10);
896                             }
897                          else   {scwArrSeed[0] = 0;}
898
899                          if (scwDateOutputFormat.indexOf('M')>-1 &&
900                              scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('M'))
901                             {scwArrSeed[1] = scwArrInput[0].substring(scwDateOutputFormat.indexOf('M'),
902                                                                       scwDateOutputFormat.lastIndexOf('M')+1);
903                             }
904                          else   {scwArrSeed[1] = '6';}
905
906                          if (scwDateOutputFormat.indexOf('D')>-1 &&
907                              scwArrInput[0].length>scwDateOutputFormat.lastIndexOf('D'))
908                             {scwArrSeed[2] = parseInt(scwArrInput[0].substring(scwDateOutputFormat.indexOf('D'),
909                                                                                scwDateOutputFormat.lastIndexOf('D')+1),10);
910                             }
911                          else   {scwArrSeed[2] = 1;}
912
913                          if (scwArrInput[0].length==scwDateOutputFormat.length) {scwBlnFullInputDate = true;}
914                          break;
915                         }
916                      case 2:
917                         {// Year and Month entry
918                          scwArrSeed[0] =
919                              parseInt(scwArrInput[scwDateSequence.
920                                                     replace(/D/i,'').
921                                                     search(/Y/i)],10);  // Year
922                          scwArrSeed[1] = scwArrInput[scwDateSequence.
923                                                     replace(/D/i,'').
924                                                     search(/M/i)];      // Month
925                          scwArrSeed[2] = 1;                             // Day
926                          break;
927                         }
928                      case 3:
929                         {// Day Month and Year entry
930
931                          scwArrSeed[0] =
932                              parseInt(scwArrInput[scwDateSequence.
933                                                     search(/Y/i)],10);  // Year
934                          scwArrSeed[1] = scwArrInput[scwDateSequence.
935                                                     search(/M/i)];      // Month
936                          scwArrSeed[2] =
937                              parseInt(scwArrInput[scwDateSequence.
938                                                     search(/D/i)],10);  // Day
939
940                          scwBlnFullInputDate = true;
941                          break;
942                         }
943                      default:
944                         {// A stuff-up has led to more than three elements in
945                          // the date.
946                          scwArrSeed[0] = 0;     // Year
947                          scwArrSeed[1] = 0;     // Month
948                          scwArrSeed[2] = 0;     // Day
949                         }
950                     }
951
952                  // These regular expressions validate the input date format
953                  // to the following rules;
954                  //         Day   1-31 (optional zero on single digits)
955                  //         Month 1-12 (optional zero on single digits)
956                  //                     or case insensitive name
957                  //         Year  One, Two or four digits
958
959                  // Months names are as set in the language-dependent
960                  // definitions and delimiters are set just below there
961
962                  var scwExpValDay    = new RegExp('^(0?[1-9]|[1-2][0-9]|3[0-1])$'),
963                      scwExpValMonth  = new RegExp('^(0?[1-9]|1[0-2]|'        +
964                                                   scwArrMonthNames.join('|') +
965                                                   ')$','i'),
966                      scwExpValYear   = new RegExp('^([0-9]{1,2}|[0-9]{4})$');
967
968                  // Apply validation and report failures
969
970                  if (scwExpValYear.exec(scwArrSeed[0])  == null ||
971                      scwExpValMonth.exec(scwArrSeed[1]) == null ||
972                      scwExpValDay.exec(scwArrSeed[2])   == null
973                     )
974                     {if (scwShowInvalidDateMsg)
975                         {alert(scwInvalidDateMsg  +
976                                scwInvalidAlert[0] + scwDateValue +
977                                scwInvalidAlert[1]);}
978                      scwBlnFullInputDate = false;
979                      scwArrSeed[0] = scwBaseYear +
980                                      Math.floor(scwDropDownYears/2); // Year
981                      scwArrSeed[1] = '6';                            // Month
982                      scwArrSeed[2] = 1;                              // Day
983                     }
984
985                  // Return the  Year    in scwArrSeed[0]
986                  //             Month   in scwArrSeed[1]
987                  //             Day     in scwArrSeed[2]
988
989                  return scwArrSeed;
990                 };
991
992              // Parse the string into an array using the allowed delimiters
993
994              scwArrSeedDate = scwInputFormat();
995
996              // So now we have the Year, Month and Day in an array.
997
998              //   If the year is one or two digits then the routine assumes a
999              //   year belongs in the 21st Century unless it is less than 50
1000              //   in which case it assumes the 20th Century is intended.
1001
1002              if (scwArrSeedDate[0]<100) {scwArrSeedDate[0] += (scwArrSeedDate[0]>50)?1900:2000;}
1003
1004              // Check whether the month is in digits or an abbreviation
1005
1006              if (scwArrSeedDate[1].search(/\d+/)!=0)
1007                 {month = scwArrMonthNames.join('|').toUpperCase().
1008                             search(scwArrSeedDate[1].substr(0,3).
1009                                                     toUpperCase());
1010                  scwArrSeedDate[1] = Math.floor(month/4)+1;
1011                 }
1012
1013              scwSeedDate = new Date(scwArrSeedDate[0],scwArrSeedDate[1]-1,scwArrSeedDate[2]);
1014             }
1015
1016          // Test that we have arrived at a valid date
1017
1018          if (isNaN(scwSeedDate))
1019             {if (scwShowInvalidDateMsg) {alert(scwInvalidDateMsg + scwInvalidAlert[0] + scwDateValue + scwInvalidAlert[1]);}
1020              scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears/2),5,1);
1021              scwBlnFullInputDate=false;
1022             }
1023          else
1024             {// Test that the date is within range,
1025              // if not then set date to a sensible date in range.
1026
1027              if ((new Date(scwBaseYear,0,1)) > scwSeedDate)
1028                 {if (scwBlnStrict && scwShowOutOfRangeMsg) {alert(scwOutOfRangeMsg);}
1029                  scwSeedDate = new Date(scwBaseYear,0,1);
1030                  scwBlnFullInputDate=false;
1031                 }
1032              else
1033                 {if ((new Date(scwBaseYear+scwDropDownYears,0,0))<scwSeedDate)
1034                     {if (scwBlnStrict && scwShowOutOfRangeMsg) {alert(scwOutOfRangeMsg);}
1035                      scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears)-1,11,1);
1036                      scwBlnFullInputDate=false;
1037                     }
1038                  else
1039                     {if (scwBlnStrict && scwBlnFullInputDate &&
1040                           (scwSeedDate.getDate()      != scwArrSeedDate[2] ||
1041                            (scwSeedDate.getMonth()+1) != scwArrSeedDate[1] ||
1042                            scwSeedDate.getFullYear()  != scwArrSeedDate[0]
1043                           )
1044                         )
1045                         {if (scwShowDoesNotExistMsg) alert(scwDoesNotExistMsg);
1046                          scwSeedDate = new Date(scwSeedDate.getFullYear(),scwSeedDate.getMonth()-1,1);
1047                          scwBlnFullInputDate=false;
1048                         }
1049                     }
1050                 }
1051             }
1052
1053          // Test the disabled dates for validity
1054          // Give error message if not valid.
1055
1056          for (var i=0;i<scwDisabledDates.length;i++)
1057             {if (!((typeof scwDisabledDates[i] == 'object') && (scwDisabledDates[i].constructor == Date)))
1058                 {if ((typeof scwDisabledDates[i] == 'object') && (scwDisabledDates[i].constructor == Array))
1059                     {var scwPass = true;
1060
1061                      if (scwDisabledDates[i].length !=2)
1062                         {if (scwShowRangeDisablingError)
1063                             {alert(scwRangeDisablingError[0] + scwDisabledDates[i] + scwRangeDisablingError[1]);}
1064                          scwPass = false;
1065                         }
1066                      else
1067                         {for (var j=0;j<scwDisabledDates[i].length;j++)
1068                             {if (!((typeof scwDisabledDates[i][j] == 'object') && (scwDisabledDates[i][j].constructor == Date)))
1069                                 {if (scwShowRangeDisablingError)
1070                                     {alert(  scwDateDisablingError[0] + scwDisabledDates[i][j] + scwDateDisablingError[1]);}
1071                                  scwPass = false;
1072                                 }
1073                             }
1074                         }
1075
1076                      if (scwPass && (scwDisabledDates[i][0] > scwDisabledDates[i][1])) {scwDisabledDates[i].reverse();}
1077                     }
1078                  else
1079                     {if (scwShowRangeDisablingError) {alert(scwDateDisablingError[0] + scwDisabledDates[i] + scwDateDisablingError[1]);}}
1080                 }
1081             }
1082
1083          // Calculate the number of months that the entered (or
1084          // defaulted) month is after the start of the allowed
1085          // date range.
1086
1087          scwMonthSum =  12*(scwSeedDate.getFullYear()-scwBaseYear)+scwSeedDate.getMonth();
1088
1089          scwID('scwYears' ).options.selectedIndex = Math.floor(scwMonthSum/12);
1090          scwID('scwMonths').options.selectedIndex = (scwMonthSum%12);
1091
1092          // Check whether or not dragging is allowed and display drag handle if necessary
1093
1094          scwID('scwDrag').style.display=(scwAllowDrag)?'':'none';
1095
1096          // Display the month
1097
1098          scwShowMonth(0);
1099
1100          // Position the calendar box
1101
1102          // The object sniffing for Opera allows for the fact that Opera
1103          // is the only major browser that correctly reports the position
1104          // of an element in a scrollable DIV.  This is because IE and
1105          // Firefox omit the DIV from the offsetParent tree.
1106
1107          scwTargetEle=scwEle;
1108
1109          var offsetTop =parseInt(scwEle.offsetTop ,10) + parseInt(scwEle.offsetHeight,10),
1110              offsetLeft=parseInt(scwEle.offsetLeft,10);
1111
1112          if (!window.opera)
1113              {while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML')
1114                  {offsetTop -=parseInt(scwEle.scrollTop, 10);
1115                   offsetLeft-=parseInt(scwEle.scrollLeft,10);
1116                   scwEle=scwEle.parentNode;
1117                  }
1118               scwEle=scwTargetEle;
1119              }
1120
1121          do {scwEle=scwEle.offsetParent;
1122              offsetTop +=parseInt(scwEle.offsetTop, 10);
1123              offsetLeft+=parseInt(scwEle.offsetLeft,10);
1124             }
1125          while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML');
1126
1127          scwID('scw').style.top =offsetTop +'px';
1128          scwID('scw').style.left=offsetLeft+'px';
1129
1130          scwID('scwIframe').style.top=offsetTop +'px';
1131          scwID('scwIframe').style.left=offsetLeft+'px';
1132          scwID('scwIframe').style.width=(scwID('scw').offsetWidth-(scwID('scwIE')?2:4))+'px';
1133          scwID('scwIframe').style.height=(scwID('scw').offsetHeight-(scwID('scwIE')?2:4))+'px';
1134          scwID('scwIframe').style.visibility='inherit';
1135
1136          // Show it on the page
1137          scwID('scw').style.visibility='inherit';
1138         };
1139
1140     function scwHide()
1141         {scwID('scw').style.visibility='hidden';
1142          scwID('scwIframe').style.visibility='hidden';
1143          if (typeof scwNextAction!='undefined' && scwNextAction!=null)
1144              {scwNextActionReturn = scwNextAction();
1145               // Explicit null set to prevent closure causing memory leak
1146               scwNextAction = null;
1147              }
1148         };
1149
1150     function scwCancel(scwEvt)
1151         {if (scwClickToHide) {scwHide();}
1152          scwStopPropagation(scwEvt);
1153         };
1154
1155     function scwStopPropagation(scwEvt)
1156         {if (scwEvt.stopPropagation)
1157                 {scwEvt.stopPropagation();}     // Capture phase
1158          else   {scwEvt.cancelBubble = true;}   // Bubbling phase
1159         };
1160
1161     function scwBeginDrag(event)
1162         {var elementToDrag = scwID('scw');
1163
1164          var deltaX    = event.clientX,
1165              deltaY    = event.clientY,
1166              offsetEle = elementToDrag;
1167
1168          do {deltaX   -= parseInt(offsetEle.offsetLeft,10);
1169              deltaY   -= parseInt(offsetEle.offsetTop ,10);
1170              offsetEle = offsetEle.offsetParent;
1171             }
1172          while (offsetEle.tagName!='BODY' &&
1173                 offsetEle.tagName!='HTML');
1174
1175          if (document.addEventListener)
1176                 {document.addEventListener('mousemove',moveHandler,true);        // Capture phase
1177                  document.addEventListener('mouseup',  upHandler,  true);        // Capture phase
1178                 }
1179          else   {elementToDrag.attachEvent('onmousemove',moveHandler); // Bubbling phase
1180                  elementToDrag.attachEvent('onmouseup',  upHandler);   // Bubbling phase
1181                  elementToDrag.setCapture();
1182                 }
1183
1184          scwStopPropagation(event);
1185
1186          function moveHandler(scwEvt)
1187             {if (!scwEvt) scwEvt = window.event;
1188
1189              elementToDrag.style.left = (scwEvt.clientX - deltaX) + 'px';
1190              elementToDrag.style.top  = (scwEvt.clientY - deltaY) + 'px';
1191
1192              scwID('scwIframe').style.left = (scwEvt.clientX - deltaX) + 'px';
1193              scwID('scwIframe').style.top  = (scwEvt.clientY - deltaY) + 'px';
1194
1195              scwStopPropagation(scwEvt);
1196             };
1197
1198          function upHandler(scwEvt)
1199             {if (!scwEvt) scwEvt = window.event;
1200
1201              if (document.removeEventListener)
1202                     {document.removeEventListener('mousemove',moveHandler,true);     // Capture phase
1203                      document.removeEventListener('mouseup',  upHandler,  true);     // Capture phase
1204                     }
1205              else   {elementToDrag.detachEvent('onmouseup',  upHandler);   // Bubbling phase
1206                      elementToDrag.detachEvent('onmousemove',moveHandler); // Bubbling phase
1207                      elementToDrag.releaseCapture();
1208                     }
1209
1210              scwStopPropagation(scwEvt);
1211             };
1212         };
1213
1214     function scwShowMonth(scwBias)
1215         {// Set the selectable Month and Year
1216          // May be called: from the left and right arrows
1217          //                  (shift month -1 and +1 respectively)
1218          //                from the month selection list
1219          //                from the year selection list
1220          //                from the showCal routine
1221          //                  (which initiates the display).
1222
1223          var scwShowDate  = new Date(Date.parse(new Date().toDateString())),
1224              scwStartDate = new Date();
1225
1226          // Set the time to the middle of the day so that the handful of
1227          // regions that have daylight saving shifts that change the day
1228          // of the month (i.e. turn the clock back at midnight or forward
1229          // at 23:00) do not mess up the date display in the calendar.
1230
1231          scwShowDate.setHours(12);
1232
1233          scwSelYears  = scwID('scwYears');
1234          scwSelMonths = scwID('scwMonths');
1235
1236          if (scwSelYears.options.selectedIndex>-1)
1237             {scwMonthSum=12*(scwSelYears.options.selectedIndex)+scwBias;
1238              if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;}
1239             }
1240          else
1241             {if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;}}
1242
1243          scwShowDate.setFullYear(scwBaseYear + Math.floor(scwMonthSum/12),(scwMonthSum%12),1);
1244
1245          // If the Week numbers are displayed, shift the week day names to the right.
1246          scwID('scwWeek_').style.display=(scwWeekNumberDisplay)?'':'none';
1247
1248          // Opera has a bug with setting the selected index.
1249          // It requires the following work-around to force SELECTs to display correctly.
1250          if (window.opera)
1251             {scwID('scwMonths').style.display = 'inherit';
1252              scwID('scwYears' ).style.display = 'inherit';
1253            }
1254
1255          // Set the drop down boxes.
1256          scwTemp = (12*parseInt((scwShowDate.getFullYear()-scwBaseYear),10)) + parseInt(scwShowDate.getMonth(),10);
1257
1258          if (scwTemp > -1 && scwTemp < (12*scwDropDownYears))
1259             {scwSelYears.options.selectedIndex=Math.floor(scwMonthSum/12);
1260              scwSelMonths.options.selectedIndex=(scwMonthSum%12);
1261
1262              scwCurMonth = scwShowDate.getMonth();
1263
1264              scwShowDate.setDate((((scwShowDate.
1265                                     getDay()-scwWeekStart)<0)?-6:1)+
1266                                  scwWeekStart-scwShowDate.getDay());
1267
1268              // This statement moved by Michael Cerveny to make version 3.55
1269              var scwCompareDateValue = new Date(scwShowDate.getFullYear(),
1270                                                 scwShowDate.getMonth(),
1271                                                 scwShowDate.getDate()).valueOf();
1272
1273              scwStartDate = new Date(scwShowDate);
1274
1275              if (scwID('scwFoot'))
1276                 {var scwFoot = scwID('scwFoot');
1277
1278                  function scwFootOutput() {scwSetOutput(scwDateNow);};
1279
1280                  if (scwDisabledDates.length==0)
1281                     {if (scwActiveToday && scwParmActiveToday)
1282                         {scwFoot.onclick     = scwFootOutput;
1283                          scwFoot.className   = 'scwFoot';
1284
1285                          if (scwID('scwIE'))
1286                             {scwFoot.onmouseover  = scwChangeClass;
1287                              scwFoot.onmouseout   = scwChangeClass;
1288                             }
1289
1290                         }
1291                      else
1292                         {scwFoot.onclick     = null;
1293                          scwFoot.className   = 'scwFootDisabled';
1294
1295                          if (scwID('scwIE'))
1296                             {scwFoot.onmouseover  = null;
1297                              scwFoot.onmouseout   = null;
1298                             }
1299
1300                          if (document.addEventListener)
1301                                 {scwFoot.addEventListener('click',scwStopPropagation,false);}
1302                          else   {scwFoot.attachEvent('onclick',scwStopPropagation);}
1303                         }
1304                     }
1305                  else
1306                     {for (var k=0;k<scwDisabledDates.length;k++)
1307                         {if (!scwActiveToday || !scwParmActiveToday ||
1308                              ((typeof scwDisabledDates[k] == 'object')                   &&
1309                                  (((scwDisabledDates[k].constructor == Date)             &&
1310                                    scwDateNow.valueOf() == scwDisabledDates[k].valueOf()
1311                                   ) ||
1312                                   ((scwDisabledDates[k].constructor == Array)               &&
1313                                    scwDateNow.valueOf() >= scwDisabledDates[k][0].valueOf() &&
1314                                    scwDateNow.valueOf() <= scwDisabledDates[k][1].valueOf()
1315                                   )
1316                                  )
1317                              )
1318                             )
1319                             {scwFoot.onclick     = null;
1320                              scwFoot.className   = 'scwFootDisabled';
1321
1322                              if (scwID('scwIE'))
1323                                 {scwFoot.onmouseover  = null;
1324                                  scwFoot.onmouseout   = null;
1325                                 }
1326
1327                              if (document.addEventListener)
1328                                     {scwFoot.addEventListener('click',scwStopPropagation,false);}
1329                              else   {scwFoot.attachEvent('onclick',scwStopPropagation);}
1330                              break;
1331                             }
1332                          else
1333                             {scwFoot.onclick=scwFootOutput;
1334                              scwFoot.className='scwFoot';
1335
1336                              if (scwID('scwIE'))
1337                                 {scwFoot.onmouseover  = scwChangeClass;
1338                                  scwFoot.onmouseout   = scwChangeClass;
1339                                 }
1340                             }
1341                         }
1342                     }
1343                 }
1344
1345              function scwSetOutput(scwOutputDate)
1346                 {if (typeof scwTargetEle.value == 'undefined')
1347                       {scwTriggerEle.scwTextNode.replaceData(0,scwTriggerEle.scwLength,scwOutputDate.scwFormat(scwDateOutputFormat));}
1348                  else {scwTargetEle.value = scwOutputDate.scwFormat(scwDateOutputFormat);}
1349                  scwHide();
1350                 };
1351
1352              function scwCellOutput(scwEvt)
1353                 {var scwEle = scwEventTrigger(scwEvt),
1354                      scwOutputDate = new Date(scwStartDate);
1355
1356                  if (scwEle.nodeType==3) scwEle=scwEle.parentNode;
1357
1358                  scwOutputDate.setDate(scwStartDate.getDate() + parseInt(scwEle.id.substr(8),10));
1359
1360                  scwSetOutput(scwOutputDate);
1361                 };
1362
1363              function scwChangeClass(scwEvt)
1364                 {var scwEle = scwEventTrigger(scwEvt);
1365
1366                  if (scwEle.nodeType==3) {scwEle=scwEle.parentNode;}
1367
1368                  switch (scwEle.className)
1369                     {case 'scwCells':
1370                         scwEle.className = 'scwCellsHover';
1371                         break;
1372                      case 'scwCellsHover':
1373                         scwEle.className = 'scwCells';
1374                         break;
1375                      case 'scwCellsExMonth':
1376                         scwEle.className = 'scwCellsExMonthHover';
1377                         break;
1378                      case 'scwCellsExMonthHover':
1379                         scwEle.className = 'scwCellsExMonth';
1380                         break;
1381                      case 'scwCellsWeekend':
1382                         scwEle.className = 'scwCellsWeekendHover';
1383                         break;
1384                      case 'scwCellsWeekendHover':
1385                         scwEle.className = 'scwCellsWeekend';
1386                         break;
1387                      case 'scwFoot':
1388                         scwEle.className = 'scwFootHover';
1389                         break;
1390                      case 'scwFootHover':
1391                         scwEle.className = 'scwFoot';
1392                         break;
1393                      case 'scwInputDate':
1394                         scwEle.className = 'scwInputDateHover';
1395                         break;
1396                      case 'scwInputDateHover':
1397                         scwEle.className = 'scwInputDate';
1398                     }
1399
1400                  return true;
1401                 }
1402
1403              function scwEventTrigger(scwEvt)
1404                 {if (!scwEvt) {scwEvt = event;}
1405                  return scwEvt.target||scwEvt.srcElement;
1406                 };
1407
1408              function scwWeekNumber(scwInDate)
1409                 {// The base day in the week of the input date
1410                  var scwInDateWeekBase = new Date(scwInDate);
1411
1412                  scwInDateWeekBase.setDate(scwInDateWeekBase.getDate()
1413                                             - scwInDateWeekBase.getDay()
1414                                             + scwWeekNumberBaseDay
1415                                             + ((scwInDate.getDay()>
1416                                                 scwWeekNumberBaseDay)?7:0));
1417
1418                  // The first Base Day in the year
1419                  var scwFirstBaseDay = new Date(scwInDateWeekBase.getFullYear(),0,1);
1420
1421                  scwFirstBaseDay.setDate(scwFirstBaseDay.getDate()
1422                                             - scwFirstBaseDay.getDay()
1423                                             + scwWeekNumberBaseDay
1424                                         );
1425
1426                  if (scwFirstBaseDay < new Date(scwInDateWeekBase.getFullYear(),0,1))
1427                     {scwFirstBaseDay.setDate(scwFirstBaseDay.getDate()+7);}
1428
1429                  // Start of Week 01
1430                  var scwStartWeekOne = new Date(scwFirstBaseDay
1431                                                 - scwWeekNumberBaseDay
1432                                                 + scwInDate.getDay());
1433
1434                  if (scwStartWeekOne > scwFirstBaseDay)
1435                     {scwStartWeekOne.setDate(scwStartWeekOne.getDate()-7);}
1436
1437                  // Subtract the date of the current week from the date of the
1438                  // first week of the year to get the number of weeks in
1439                  // milliseconds.  Divide by the number of milliseconds
1440                  // in a week then round to no decimals in order to remove
1441                  // the effect of daylight saving.  Add one to make the first
1442                  // week, week 1.  Place a string zero on the front so that
1443                  // week numbers are zero filled.
1444
1445                  var scwWeekNo = '0' + (Math.round((scwInDateWeekBase - scwFirstBaseDay)/604800000,0) + 1);
1446
1447                  // Return the last two characters in the week number string
1448
1449                  return scwWeekNo.substring(scwWeekNo.length-2, scwWeekNo.length);
1450                 };
1451
1452              // Treewalk to display the dates.
1453              // I tried to use getElementsByName but IE refused to cooperate
1454              // so I resorted to this method which works for all tested
1455              // browsers.
1456
1457              var scwCells = scwID('scwCells');
1458
1459              for (i=0;i<scwCells.childNodes.length;i++)
1460                 {var scwRows = scwCells.childNodes[i];
1461                  if (scwRows.nodeType==1 && scwRows.tagName=='TR')
1462                     {if (scwWeekNumberDisplay)
1463                         {//Calculate the week number using scwShowDate
1464                          scwTmpEl = scwRows.childNodes[0];
1465                          scwTmpEl.innerHTML = scwWeekNumber(scwShowDate);
1466                          scwTmpEl.style.borderColor =
1467                              (scwTmpEl.currentStyle)
1468                                 ?scwTmpEl.currentStyle['backgroundColor']
1469                                 :(window.getComputedStyle)
1470                                     ?document.defaultView.getComputedStyle(scwTmpEl,null).getPropertyValue('background-color')
1471                                     :'';
1472                          scwTmpEl.style.display='';
1473                         }
1474                      else
1475                         {scwRows.childNodes[0].style.display='none';}
1476
1477                      for (j=1;j<scwRows.childNodes.length;j++)
1478                         {var scwCols = scwRows.childNodes[j];
1479                          if (scwCols.nodeType==1 && scwCols.tagName=='TD')
1480                             {scwRows.childNodes[j].innerHTML=
1481                                 scwShowDate.getDate();
1482                              var scwCell=scwRows.childNodes[j],
1483                                  scwDisabled =
1484                                     ((scwOutOfRangeDisable &&
1485                                         (scwShowDate <
1486                                             (new Date(scwBaseYear,0,1,
1487                                                       scwShowDate.getHours()))
1488                                          ||
1489                                          scwShowDate >
1490                                             (new Date(scwBaseYear+
1491                                                       scwDropDownYears,0,0,
1492                                                       scwShowDate.getHours()))
1493                                         )
1494                                      ) ||
1495                                      (scwOutOfMonthDisable &&
1496                                         (scwShowDate <
1497                                             (new Date(scwShowDate.getFullYear(),
1498                                                       scwCurMonth,1,
1499                                                       scwShowDate.getHours()))
1500                                          ||
1501                                          scwShowDate >
1502                                             (new Date(scwShowDate.getFullYear(),
1503                                                       scwCurMonth+1,0,
1504                                                       scwShowDate.getHours()))
1505                                         )
1506                                      )
1507                                     )?true:false;
1508
1509                              scwCell.style.visibility =
1510                                 (scwOutOfMonthHide &&
1511                                     (scwShowDate <
1512                                         (new Date(scwShowDate.getFullYear(),
1513                                                   scwCurMonth,1,
1514                                                   scwShowDate.getHours()))
1515                                      ||
1516                                      scwShowDate >
1517                                         (new Date(scwShowDate.getFullYear(),
1518                                                   scwCurMonth+1,0,
1519                                                   scwShowDate.getHours()))
1520                                     )
1521                                 )?'hidden':'inherit';
1522
1523                              for (var k=0;k<scwDisabledDates.length;k++)
1524                                 {if ((typeof scwDisabledDates[k]=='object') &&
1525                                      (scwDisabledDates[k].constructor == Date) &&
1526                                      scwCompareDateValue == scwDisabledDates[k].valueOf()
1527                                     )
1528                                     {scwDisabled = true;}
1529                                  else
1530                                     {if ((typeof scwDisabledDates[k]=='object') &&
1531                                          (scwDisabledDates[k].constructor == Array) &&
1532                                          scwCompareDateValue >= scwDisabledDates[k][0].valueOf() &&
1533                                          scwCompareDateValue <= scwDisabledDates[k][1].valueOf()
1534                                         )
1535                                         {scwDisabled = true;}
1536                                     }
1537                                 }
1538
1539                              if (scwDisabled ||
1540                                  !scwEnabledDay[j-1+(7*((i*scwCells.childNodes.length)/6))] ||
1541                                  !scwPassEnabledDay[(j-1+(7*(i*scwCells.childNodes.length/6)))%7]
1542                                 )
1543                                 {scwRows.childNodes[j].onclick = null;
1544
1545                                  if (scwID('scwIE'))
1546                                     {scwRows.childNodes[j].onmouseover  = null;
1547                                      scwRows.childNodes[j].onmouseout   = null;
1548                                     }
1549
1550                                  scwCell.className=
1551                                     (scwShowDate.getMonth()!=scwCurMonth)
1552                                         ?'scwCellsExMonthDisabled'
1553                                         :(scwBlnFullInputDate &&
1554                                           scwShowDate.toDateString()==
1555                                           scwSeedDate.toDateString())
1556                                             ?'scwInputDateDisabled'
1557                                             :(scwShowDate.getDay()%6==0)
1558                                                 ?'scwCellsWeekendDisabled'
1559                                                 :'scwCellsDisabled';
1560
1561                                  scwCell.style.borderColor =
1562                                      (scwFormatTodayCell && scwShowDate.toDateString()==scwDateNow.toDateString())
1563                                         ?scwTodayCellBorderColour
1564                                         :(scwCell.currentStyle)
1565                                             ?scwCell.currentStyle['backgroundColor']
1566                                             :(window.getComputedStyle)
1567                                                 ?document.defaultView.getComputedStyle(scwCell,null).getPropertyValue('background-color')
1568                                                 :'';
1569                                 }
1570                              else
1571                                 {scwRows.childNodes[j].onclick=scwCellOutput;
1572
1573                                  if (scwID('scwIE'))
1574                                     {scwRows.childNodes[j].onmouseover  = scwChangeClass;
1575                                      scwRows.childNodes[j].onmouseout   = scwChangeClass;
1576                                     }
1577
1578                                  scwCell.className=
1579                                      (scwShowDate.getMonth()!=scwCurMonth)
1580                                         ?'scwCellsExMonth'
1581                                         :(scwBlnFullInputDate &&
1582                                           scwShowDate.toDateString()==
1583                                           scwSeedDate.toDateString())
1584                                             ?'scwInputDate'
1585                                             :(scwShowDate.getDay()%6==0)
1586                                                 ?'scwCellsWeekend'
1587                                                 :'scwCells';
1588
1589                                  scwCell.style.borderColor =
1590                                      (scwFormatTodayCell && scwShowDate.toDateString() == scwDateNow.toDateString())
1591                                         ?scwTodayCellBorderColour
1592                                         :(scwCell.currentStyle)
1593                                             ?scwCell.currentStyle['backgroundColor']
1594                                             :(window.getComputedStyle)
1595                                                 ?document.defaultView.getComputedStyle(scwCell,null).getPropertyValue('background-color')
1596                                                 :'';
1597                                }
1598
1599                              scwShowDate.setDate(scwShowDate.getDate()+1);
1600                              scwCompareDateValue = new Date(scwShowDate.getFullYear(),scwShowDate.getMonth(),scwShowDate.getDate()).valueOf();
1601                             }
1602                         }
1603                     }
1604                 }
1605             }
1606
1607          // Opera has a bug with setting the selected index.
1608          // It requires the following work-around to force SELECTs to display correctly.
1609          // Also Opera's poor dynamic rendering prior to 9.5 requires
1610          // the visibility to be reset to prevent garbage in the calendar
1611          // when the displayed month is changed.
1612
1613          if (window.opera)
1614             {scwID('scwMonths').style.display = 'inline';
1615              scwID('scwYears' ).style.display = 'inline';
1616              scwID('scw').style.visibility='hidden';
1617              scwID('scw').style.visibility='inherit';
1618            }
1619         };
1620
1621 // *************************
1622 //  End of Function Library
1623 // *************************
1624 // ***************************
1625 // Start of Calendar structure
1626 // ***************************
1627
1628     document.writeln("<!--[if IE]><div id='scwIE'></div><![endif]-->");
1629     document.writeln("<!--[if lt IE 7]><div id='scwIElt7'></div><![endif]-->");
1630     document.write(
1631      "<iframe class='scw' " + (scwID('scwIElt7')?"src='/scwblank.html '":'') +
1632              "id='scwIframe' name='scwIframe' frameborder='0'>" +
1633      "</iframe>" +
1634      "<table id='scw' class='scw'>" +
1635        "<tr class='scw'>" +
1636          "<td class='scw'>" +
1637            "<table class='scwHead' id='scwHead' width='100%' " +
1638                     "cellspacing='0' cellpadding='0'>" +
1639             "<tr id='scwDrag' style='display:none;'>" +
1640                 "<td colspan='4' class='scwDrag' " +
1641                     "onmousedown='scwBeginDrag(event);'>" +
1642                     "<div id='scwDragText'></div>" +
1643                 "</td>" +
1644             "</tr>" +
1645             "<tr class='scwHead' >" +
1646                  "<td class='scwHead'>" +
1647                     "<input class='scwHead' id='scwHeadLeft' type='button' value='<' " +
1648                             "onclick='scwShowMonth(-1);'  /></td>" +
1649                  "<td class='scwHead'>" +
1650                     "<select id='scwMonths' class='scwHead' " +
1651                             "onchange='scwShowMonth(0);'>" +
1652                     "</select>" +
1653                  "</td>" +
1654                  "<td class='scwHead'>" +
1655                     "<select id='scwYears' class='scwHead' " +
1656                             "onchange='scwShowMonth(0);'>" +
1657                     "</select>" +
1658                  "</td>" +
1659                  "<td class='scwHead'>" +
1660                     "<input class='scwHead' id='scwHeadRight' type='button' value='>' " +
1661                             "onclick='scwShowMonth(1);' /></td>" +
1662                 "</tr>" +
1663               "</table>" +
1664             "</td>" +
1665           "</tr>" +
1666           "<tr class='scw'>" +
1667             "<td class='scw'>" +
1668               "<table class='scwCells' align='center'>" +
1669                 "<thead>" +
1670                   "<tr><td class='scwWeekNumberHead' id='scwWeek_' ></td>");
1671
1672     for (i=0;i<7;i++)
1673         {document.write(
1674                       "<td class='scwWeek' id='scwWeekInit" + i + "'></td>");
1675         }
1676
1677     document.write("</tr>" +
1678                 "</thead>" +
1679                 "<tbody id='scwCells' " +
1680                         "onClick='scwStopPropagation(event);'>");
1681
1682     for (i=0;i<6;i++)
1683         {document.write(
1684                     "<tr>" +
1685                       "<td class='scwWeekNo' id='scwWeek_" + i + "'></td>");
1686          for (j=0;j<7;j++)
1687             {document.write(
1688                         "<td class='scwCells' id='scwCell_" + (j+(i*7)) +
1689                         "'></td>");
1690             }
1691
1692          document.write(
1693                     "</tr>");
1694         }
1695
1696     document.write(
1697                 "</tbody>");
1698
1699     if ((new Date(scwBaseYear + scwDropDownYears, 0, 0)) > scwDateNow &&
1700         (new Date(scwBaseYear, 0, 0))                    < scwDateNow)
1701         {document.write(
1702                   "<tfoot class='scwFoot'>" +
1703                     "<tr class='scwFoot'>" +
1704                       "<td class='scwFoot' id='scwFoot' colspan='8'>" +
1705                       "</td>" +
1706                     "</tr>" +
1707                   "</tfoot>");
1708         }
1709
1710     document.write(
1711               "</table>" +
1712             "</td>" +
1713           "</tr>" +
1714         "</table>");
1715
1716     if (document.addEventListener)
1717             {scwID('scw'         ).addEventListener('click',scwCancel,false);
1718              scwID('scwHeadLeft' ).addEventListener('click',scwStopPropagation,false);
1719              scwID('scwMonths'   ).addEventListener('click',scwStopPropagation,false);
1720              scwID('scwMonths'   ).addEventListener('change',scwStopPropagation,false);
1721              scwID('scwYears'    ).addEventListener('click',scwStopPropagation,false);
1722              scwID('scwYears'    ).addEventListener('change',scwStopPropagation,false);
1723              scwID('scwHeadRight').addEventListener('click',scwStopPropagation,false);
1724             }
1725     else    {scwID('scw'         ).attachEvent('onclick',scwCancel);
1726              scwID('scwHeadLeft' ).attachEvent('onclick',scwStopPropagation);
1727              scwID('scwMonths'   ).attachEvent('onclick',scwStopPropagation);
1728              scwID('scwMonths'   ).attachEvent('onchange',scwStopPropagation);
1729              scwID('scwYears'    ).attachEvent('onclick',scwStopPropagation);
1730              scwID('scwYears'    ).attachEvent('onchange',scwStopPropagation);
1731              scwID('scwHeadRight').attachEvent('onclick',scwStopPropagation);
1732             }
1733
1734 // ***************************
1735 //  End of Calendar structure
1736 // ***************************
1737 // ****************************************
1738 // Start of document level event definition
1739 // ****************************************
1740
1741     if (document.addEventListener)
1742             {document.addEventListener('click',scwHide, false);}
1743     else    {document.attachEvent('onclick',scwHide);}
1744
1745 // ****************************************
1746 //  End of document level event definition
1747 // ****************************************
1748 // ************************************
1749 //  End of Simple Calendar Widget Code
1750 // ************************************