1 // *****************************************************************************
2 // Simple Calendar Widget - Cross-Browser Javascript pop-up calendar.
4 // Copyright (C) 2005-2007 Anthony Garrett
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.
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.
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
22 // *****************************************************************************
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.
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.
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:
40 // <A HREF="http://www.tarrget.info/calendar/scw.htm">
41 // Simple Calendar Widget by Anthony Garrett
44 // Features: Easily customised
45 // (output date format, colours, language, year range and
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
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;
58 // <script type='Text/JavaScript' src='scw.js'></script>
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.
65 // How to use the Calendar once it is defined for your page:
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;
72 // <<event>>="scwShow(<<element>>,event);"
74 // e.g. onClick="scwShow(scwID('myElement'),event);"
75 // or onMouseOver="scwShow(this,event);"
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;
81 // <a href="#" onclick="scwShow(<<element>>,event);return false;">
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
89 // e.g. <p onclick="scwShow(this,event);"><<date>></p>
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;
96 // scwShow(<<element>>,event,5,1);
98 // Finally you can use the following technique to run a function
99 // when the calendar closes:
101 // scwNextAction=<<function>>.runsAfterSCW(this,<<arguments>>);
102 // scwShow(<<element>>,event <<,optional arguments above>>);
104 // Where <<function>> is a function defined on the calling page
105 // and <<arguments>> is the list of arguments being passed to that
108 // No event? No problem!
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;
114 // E.G. scwShow(<<target element>>,<<source element>>);
115 // as in: scwShow(this,this);
117 // ------------------------------------------------------------------
118 // Here's an extremely trivial but fully functioning example page
119 // showing two of the ways to trigger the calendar;
122 // <head><title>Basic Example</title></head>
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' />
128 // <a href='#' onclick='scwShow(this,event);return false;'>
134 // *****************************************************************************
136 // See http://www.tarrget.info/calendar/scw.htm for a complete version history
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.
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'
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.
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.
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.
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.
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
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.
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.
203 // *****************************************************************************
205 // ************************************
206 // Start of Simple Calendar Widget Code
207 // ************************************
209 // This date is used throughout to determine today's date.
211 var scwDateNow = new Date(Date.parse(new Date().toDateString()));
213 //******************************************************************************
214 //------------------------------------------------------------------------------
215 // Customisation section
216 //------------------------------------------------------------------------------
217 //******************************************************************************
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;
225 var scwBaseYear = scwDateNow.getFullYear()-10;
227 // How many years do want to be valid and to show in the drop-down list?
229 var scwDropDownYears = 20;
231 // All language-dependent changes can be made here...
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
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.
246 function scwSetDefaultLanguage()
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.'];
266 // Note: Always start the scwArrWeekInits array with your string for
267 // Sunday whatever scwWeekStart (below) is set to.
269 // scwWeekStart determines the start of the week in the display
270 // Set it to: 0 (Zero) for Sunday, 1 (One) for Monday etc..
272 var scwWeekStart = 1;
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.]
279 // If you want to see week numbering on the calendar, set
280 // this to true. If not, false.
282 var scwWeekNumberDisplay = false;
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.
289 // See http://www.cl.cam.ac.uk/~mgk25/iso-time.html for more information
291 var scwWeekNumberBaseDay = 4;
293 // Each of the calendar's alert message types can be disabled
294 // independently here.
296 var scwShowInvalidDateMsg = true,
297 scwShowOutOfRangeMsg = true,
298 scwShowDoesNotExistMsg = true,
299 scwShowInvalidAlert = true,
300 scwShowDateDisablingError = true,
301 scwShowRangeDisablingError = true;
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 = ['/','-','.',',',' '];
308 var scwArrDelimiters = ['/','-','.',',',' '];
310 // Set the format for the displayed 'Today' date and for the output
313 // The format is described using delimiters of your choice (as set
314 // in scwArrDelimiters above) and case insensitive letters D, M and Y.
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
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
331 // Displayed "Today" date format
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
336 // Output date format
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
342 // Note: The delimiters used should be in scwArrDelimiters.
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.
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.
358 var scwBlnStrict = false;
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.
364 // You could put something like the following in your calling page
365 // to disable all weekend days;
367 // for (var i=0;i<scwEnabledDay.length;i++)
368 // {if (i%7%6==0) scwEnabledDay[i] = false;}
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
376 // "How to use the Calendar once it is defined for your page:"
378 // It is possible to use these two approaches in combination.
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];
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
393 var scwDisabledDates = new Array();
395 // e.g. To disable 10-Dec-2005:
396 // scwDisabledDates[0] = new Date(2005,11,10);
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)];
401 // Remember that Javascript months are Zero-based.
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.
407 var scwActiveToday = true;
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
413 // Set scwOutOfMonthDisable to true to disable these dates (or false
414 // to allow their selection).
416 // Set scwOutOfMonthHide to true to hide these dates (or false
417 // to make them visible).
419 var scwOutOfMonthDisable = false;
420 var scwOutOfMonthHide = false;
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).
427 var scwOutOfRangeDisable = true;
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.
433 var scwFormatTodayCell = true;
434 var scwTodayCellBorderColour = 'red';
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.
442 var scwAllowDrag = false;
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.
448 var scwClickToHide = false;
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.
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;}' +
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
474 '<style type="text/css">' +
475 '/* IMPORTANT: The SCW calendar script requires all ' +
476 ' the classes defined here.' +
478 'table.scw {padding: 1px;' +
479 'vertical-align:middle;' +
480 'border: ridge 2px;' +
483 'Verdana,Arial,Helvetica,Sans-Serif;'+
484 'font-weight: bold;}' +
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;' +
493 'vertical-align:middle;' +
494 'text-align: center;' +
496 'font-weight: bold;' +
498 'font-family: fixedSys;}' +
499 'td.scwWeekNumberHead,' +
500 'td.scwWeek {padding: 0px;' +
501 'text-align: center;' +
502 'font-weight: bold;}' +
505 'td.scwFoot:hover,' +
506 'td.scwFootDisabled {padding: 0px;' +
507 'text-align: center;' +
508 'font-weight: normal;}' +
509 'table.scwCells {text-align: right;' +
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,' +
525 'td.scwInputDateHover,' +
526 'td.scwInputDate:hover,' +
527 'td.scwInputDateDisabled,' +
529 'td.scwWeeks {padding: 3px;' +
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;' +
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;' +
549 '/* Enabled Days */' +
551 'td.scwCells {background-color: #CCCCCC;' +
553 '/* Day matching the input date */' +
554 'td.scwInputDate {background-color: #CC9999;' +
556 '/* Weekend Day */' +
557 'td.scwCellsWeekend {background-color: #CCCCCC;' +
559 '/* Day outside the current month */' +
560 'td.scwCellsExMonth {background-color: #CCCCCC;' +
562 '/* Today selector */' +
563 'td.scwFoot {background-color: #6666CC;' +
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' +
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' +
577 '/* Active cells */' +
578 'td.scwCells:hover,' +
579 'td.scwCellsHover {background-color: #FFFF00;' +
582 '/* Day matching the input date */' +
583 'td.scwInputDate:hover,' +
584 'td.scwInputDateHover {background-color: #FFFF00;' +
587 '/* Weekend cells */' +
588 'td.scwCellsWeekend:hover,' +
589 'td.scwCellsWeekendHover {background-color: #FFFF00;' +
592 '/* Day outside the current month */' +
593 'td.scwCellsExMonth:hover,' +
594 'td.scwCellsExMonthHover {background-color: #FFFF00;' +
597 '/* Today selector */' +
598 'td.scwFoot:hover,' +
599 'td.scwFootHover {color: #FFFF00;' +
601 'font-weight: bold;}' +
602 '/* Disabled cells */' +
604 '/* Day matching the input date */' +
605 'td.scwInputDateDisabled {background-color: #999999;' +
607 'td.scwCellsDisabled {background-color: #999999;' +
609 '/* Weekend Day */' +
610 'td.scwCellsWeekendDisabled {background-color: #999999;' +
612 '/* Day outside the current month */' +
613 'td.scwCellsExMonthDisabled {background-color: #999999;' +
615 'td.scwFootDisabled {background-color: #6666CC;' +
620 //******************************************************************************
621 //------------------------------------------------------------------------------
622 // End of customisation section
623 //------------------------------------------------------------------------------
624 //******************************************************************************
626 // Variables required by both scwShow and scwShowMonth
631 scwBlnFullInputDate = false,
632 scwPassEnabledDay = new Array(),
633 scwSeedDate = new Date(),
634 scwParmActiveToday = true,
635 scwWeekStart = scwWeekStart%7,
644 scwDateDisablingError,
645 scwRangeDisablingError;
647 // Add a method to format a date into the required pattern
649 Date.prototype.scwFormat =
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
663 else {switch (codeChar)
665 result += (this.getFullYear()%Math.
666 pow(10,charCount)).toString().
667 scwPadLeft(charCount);
670 // If we find an M, check the number of them to
671 // determine whether to get the month number or
673 result += (charCount<3)
674 ?(this.getMonth()+1).
675 toString().scwPadLeft(charCount)
676 :scwArrMonthNames[this.getMonth()];
679 // If we find a D, get the date and format it
680 result += this.getDate().toString().
681 scwPadLeft(charCount);
684 // Copy any unrecognised characters across
685 while (charCount-- > 0) {result += codeChar;}
688 if (i<scwFormat.length)
689 {// Store the character we have just worked on
690 codeChar = scwFormat.charAt(i);
698 // Add a method to left pad zeroes
700 String.prototype.scwPadLeft =
701 function(padToLength)
703 for (var i=0;i<(padToLength - this.length);i++) {result += '0';}
704 return (result + this);
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
711 Function.prototype.runsAfterSCW =
712 function() {var func = this,
713 args = new Array(arguments.length);
715 for (var i=0;i<args.length;++i) {args[i] = arguments[i];}
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;
724 // Set up some shortcuts
726 function scwID(id) {return document.getElementById(id);};
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.
732 var scwNextActionReturn, scwNextAction;
734 // ****************************************************************************
735 // Start of Function Library
737 // Exposed functions:
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.
745 // scwShowMonth Displays a month on the calendar,
746 // Called when a month is set or changed.
748 // scwBeginDrag Controls calendar dragging.
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.
755 // ****************************************************************************
757 function showCal(scwEle,scwSource) {scwShow(scwEle,scwSource);};
758 function scwShow(scwEle,scwSource)
759 {if (!scwSource) {scwSource = window.event;}
761 if (scwSource.tagName) // Second parameter isn't an event it's an element
762 {var scwSourceEle = scwSource;
764 if (scwID('scwIE')) {window.event.cancelBubble = true;}
765 else {scwSourceEle.parentNode.addEventListener('click',scwStopPropagation,false);}
767 else // Second parameter is an event
768 {var scwSourceEle = (scwSource.target)
770 :scwSource.srcElement;
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;}
778 scwTriggerEle = scwSourceEle;
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.
783 scwParmActiveToday = true;
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;}
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.
799 scwSeedDate = scwDateNow;
801 // Find the date and Strip space characters from start and
802 // end of date input.
804 var scwDateValue = '';
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;
822 // Set the language-dependent elements
824 scwSetDefaultLanguage();
826 scwID('scwDragText').innerHTML = scwDrag;
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]);}
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));}
836 for (var i=0;i<scwArrWeekInits.length;i++)
837 {scwID('scwWeekInit' + i).innerHTML = scwArrWeekInits[(i+scwWeekStart)%scwArrWeekInits.length];}
839 if (scwID('scwFoot'))
840 {scwID('scwFoot').innerHTML = scwToday + ' ' + scwDateNow.scwFormat(scwDateDisplayFormat);}
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.
846 scwBlnFullInputDate=false;
848 if ((new Date(scwBaseYear+scwDropDownYears,0,0))<scwSeedDate ||
849 (new Date(scwBaseYear,0,1)) >scwSeedDate
851 {scwSeedDate = new Date(scwBaseYear + Math.floor(scwDropDownYears / 2), 5, 1);}
854 {function scwInputFormat()
855 {var scwArrSeed = new Array(),
856 scwArrInput = scwDateValue.split(new RegExp('[\\'+scwArrDelimiters.join('\\')+']+','g'));
858 // "Escape" all the user defined date delimiters above -
859 // several delimiters will need it and it does no harm for
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
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);}
872 scwBlnFullInputDate = false;
874 scwDateOutputFormat = scwDateOutputFormat.toUpperCase();
876 // List all the allowed letters in the date format
877 var template = ['D','M','Y'];
879 // Prepare the sequence of date input elements
880 var result = new Array();
882 for (var i=0;i<template.length;i++)
883 {if (scwDateOutputFormat.search(template[i])>-1)
884 {result[scwDateOutputFormat.search(template[i])] = template[i];}
887 var scwDateSequence = result.join('');
889 // Separate the elements of the date input
890 switch (scwArrInput.length)
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);
897 else {scwArrSeed[0] = 0;}
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);
904 else {scwArrSeed[1] = '6';}
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);
911 else {scwArrSeed[2] = 1;}
913 if (scwArrInput[0].length==scwDateOutputFormat.length) {scwBlnFullInputDate = true;}
917 {// Year and Month entry
919 parseInt(scwArrInput[scwDateSequence.
921 search(/Y/i)],10); // Year
922 scwArrSeed[1] = scwArrInput[scwDateSequence.
924 search(/M/i)]; // Month
925 scwArrSeed[2] = 1; // Day
929 {// Day Month and Year entry
932 parseInt(scwArrInput[scwDateSequence.
933 search(/Y/i)],10); // Year
934 scwArrSeed[1] = scwArrInput[scwDateSequence.
935 search(/M/i)]; // Month
937 parseInt(scwArrInput[scwDateSequence.
938 search(/D/i)],10); // Day
940 scwBlnFullInputDate = true;
944 {// A stuff-up has led to more than three elements in
946 scwArrSeed[0] = 0; // Year
947 scwArrSeed[1] = 0; // Month
948 scwArrSeed[2] = 0; // Day
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
959 // Months names are as set in the language-dependent
960 // definitions and delimiters are set just below there
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('|') +
966 scwExpValYear = new RegExp('^([0-9]{1,2}|[0-9]{4})$');
968 // Apply validation and report failures
970 if (scwExpValYear.exec(scwArrSeed[0]) == null ||
971 scwExpValMonth.exec(scwArrSeed[1]) == null ||
972 scwExpValDay.exec(scwArrSeed[2]) == null
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
985 // Return the Year in scwArrSeed[0]
986 // Month in scwArrSeed[1]
987 // Day in scwArrSeed[2]
992 // Parse the string into an array using the allowed delimiters
994 scwArrSeedDate = scwInputFormat();
996 // So now we have the Year, Month and Day in an array.
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.
1002 if (scwArrSeedDate[0]<100) {scwArrSeedDate[0] += (scwArrSeedDate[0]>50)?1900:2000;}
1004 // Check whether the month is in digits or an abbreviation
1006 if (scwArrSeedDate[1].search(/\d+/)!=0)
1007 {month = scwArrMonthNames.join('|').toUpperCase().
1008 search(scwArrSeedDate[1].substr(0,3).
1010 scwArrSeedDate[1] = Math.floor(month/4)+1;
1013 scwSeedDate = new Date(scwArrSeedDate[0],scwArrSeedDate[1]-1,scwArrSeedDate[2]);
1016 // Test that we have arrived at a valid date
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;
1024 {// Test that the date is within range,
1025 // if not then set date to a sensible date in range.
1027 if ((new Date(scwBaseYear,0,1)) > scwSeedDate)
1028 {if (scwBlnStrict && scwShowOutOfRangeMsg) {alert(scwOutOfRangeMsg);}
1029 scwSeedDate = new Date(scwBaseYear,0,1);
1030 scwBlnFullInputDate=false;
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;
1039 {if (scwBlnStrict && scwBlnFullInputDate &&
1040 (scwSeedDate.getDate() != scwArrSeedDate[2] ||
1041 (scwSeedDate.getMonth()+1) != scwArrSeedDate[1] ||
1042 scwSeedDate.getFullYear() != scwArrSeedDate[0]
1045 {if (scwShowDoesNotExistMsg) alert(scwDoesNotExistMsg);
1046 scwSeedDate = new Date(scwSeedDate.getFullYear(),scwSeedDate.getMonth()-1,1);
1047 scwBlnFullInputDate=false;
1053 // Test the disabled dates for validity
1054 // Give error message if not valid.
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;
1061 if (scwDisabledDates[i].length !=2)
1062 {if (scwShowRangeDisablingError)
1063 {alert(scwRangeDisablingError[0] + scwDisabledDates[i] + scwRangeDisablingError[1]);}
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]);}
1076 if (scwPass && (scwDisabledDates[i][0] > scwDisabledDates[i][1])) {scwDisabledDates[i].reverse();}
1079 {if (scwShowRangeDisablingError) {alert(scwDateDisablingError[0] + scwDisabledDates[i] + scwDateDisablingError[1]);}}
1083 // Calculate the number of months that the entered (or
1084 // defaulted) month is after the start of the allowed
1087 scwMonthSum = 12*(scwSeedDate.getFullYear()-scwBaseYear)+scwSeedDate.getMonth();
1089 scwID('scwYears' ).options.selectedIndex = Math.floor(scwMonthSum/12);
1090 scwID('scwMonths').options.selectedIndex = (scwMonthSum%12);
1092 // Check whether or not dragging is allowed and display drag handle if necessary
1094 scwID('scwDrag').style.display=(scwAllowDrag)?'':'none';
1096 // Display the month
1100 // Position the calendar box
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.
1107 scwTargetEle=scwEle;
1109 var offsetTop =parseInt(scwEle.offsetTop ,10) + parseInt(scwEle.offsetHeight,10),
1110 offsetLeft=parseInt(scwEle.offsetLeft,10);
1113 {while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML')
1114 {offsetTop -=parseInt(scwEle.scrollTop, 10);
1115 offsetLeft-=parseInt(scwEle.scrollLeft,10);
1116 scwEle=scwEle.parentNode;
1118 scwEle=scwTargetEle;
1121 do {scwEle=scwEle.offsetParent;
1122 offsetTop +=parseInt(scwEle.offsetTop, 10);
1123 offsetLeft+=parseInt(scwEle.offsetLeft,10);
1125 while (scwEle.tagName!='BODY' && scwEle.tagName!='HTML');
1127 scwID('scw').style.top =offsetTop +'px';
1128 scwID('scw').style.left=offsetLeft+'px';
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';
1136 // Show it on the page
1137 scwID('scw').style.visibility='inherit';
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;
1150 function scwCancel(scwEvt)
1151 {if (scwClickToHide) {scwHide();}
1152 scwStopPropagation(scwEvt);
1155 function scwStopPropagation(scwEvt)
1156 {if (scwEvt.stopPropagation)
1157 {scwEvt.stopPropagation();} // Capture phase
1158 else {scwEvt.cancelBubble = true;} // Bubbling phase
1161 function scwBeginDrag(event)
1162 {var elementToDrag = scwID('scw');
1164 var deltaX = event.clientX,
1165 deltaY = event.clientY,
1166 offsetEle = elementToDrag;
1168 do {deltaX -= parseInt(offsetEle.offsetLeft,10);
1169 deltaY -= parseInt(offsetEle.offsetTop ,10);
1170 offsetEle = offsetEle.offsetParent;
1172 while (offsetEle.tagName!='BODY' &&
1173 offsetEle.tagName!='HTML');
1175 if (document.addEventListener)
1176 {document.addEventListener('mousemove',moveHandler,true); // Capture phase
1177 document.addEventListener('mouseup', upHandler, true); // Capture phase
1179 else {elementToDrag.attachEvent('onmousemove',moveHandler); // Bubbling phase
1180 elementToDrag.attachEvent('onmouseup', upHandler); // Bubbling phase
1181 elementToDrag.setCapture();
1184 scwStopPropagation(event);
1186 function moveHandler(scwEvt)
1187 {if (!scwEvt) scwEvt = window.event;
1189 elementToDrag.style.left = (scwEvt.clientX - deltaX) + 'px';
1190 elementToDrag.style.top = (scwEvt.clientY - deltaY) + 'px';
1192 scwID('scwIframe').style.left = (scwEvt.clientX - deltaX) + 'px';
1193 scwID('scwIframe').style.top = (scwEvt.clientY - deltaY) + 'px';
1195 scwStopPropagation(scwEvt);
1198 function upHandler(scwEvt)
1199 {if (!scwEvt) scwEvt = window.event;
1201 if (document.removeEventListener)
1202 {document.removeEventListener('mousemove',moveHandler,true); // Capture phase
1203 document.removeEventListener('mouseup', upHandler, true); // Capture phase
1205 else {elementToDrag.detachEvent('onmouseup', upHandler); // Bubbling phase
1206 elementToDrag.detachEvent('onmousemove',moveHandler); // Bubbling phase
1207 elementToDrag.releaseCapture();
1210 scwStopPropagation(scwEvt);
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).
1223 var scwShowDate = new Date(Date.parse(new Date().toDateString())),
1224 scwStartDate = new Date();
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.
1231 scwShowDate.setHours(12);
1233 scwSelYears = scwID('scwYears');
1234 scwSelMonths = scwID('scwMonths');
1236 if (scwSelYears.options.selectedIndex>-1)
1237 {scwMonthSum=12*(scwSelYears.options.selectedIndex)+scwBias;
1238 if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;}
1241 {if (scwSelMonths.options.selectedIndex>-1) {scwMonthSum+=scwSelMonths.options.selectedIndex;}}
1243 scwShowDate.setFullYear(scwBaseYear + Math.floor(scwMonthSum/12),(scwMonthSum%12),1);
1245 // If the Week numbers are displayed, shift the week day names to the right.
1246 scwID('scwWeek_').style.display=(scwWeekNumberDisplay)?'':'none';
1248 // Opera has a bug with setting the selected index.
1249 // It requires the following work-around to force SELECTs to display correctly.
1251 {scwID('scwMonths').style.display = 'inherit';
1252 scwID('scwYears' ).style.display = 'inherit';
1255 // Set the drop down boxes.
1256 scwTemp = (12*parseInt((scwShowDate.getFullYear()-scwBaseYear),10)) + parseInt(scwShowDate.getMonth(),10);
1258 if (scwTemp > -1 && scwTemp < (12*scwDropDownYears))
1259 {scwSelYears.options.selectedIndex=Math.floor(scwMonthSum/12);
1260 scwSelMonths.options.selectedIndex=(scwMonthSum%12);
1262 scwCurMonth = scwShowDate.getMonth();
1264 scwShowDate.setDate((((scwShowDate.
1265 getDay()-scwWeekStart)<0)?-6:1)+
1266 scwWeekStart-scwShowDate.getDay());
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();
1273 scwStartDate = new Date(scwShowDate);
1275 if (scwID('scwFoot'))
1276 {var scwFoot = scwID('scwFoot');
1278 function scwFootOutput() {scwSetOutput(scwDateNow);};
1280 if (scwDisabledDates.length==0)
1281 {if (scwActiveToday && scwParmActiveToday)
1282 {scwFoot.onclick = scwFootOutput;
1283 scwFoot.className = 'scwFoot';
1286 {scwFoot.onmouseover = scwChangeClass;
1287 scwFoot.onmouseout = scwChangeClass;
1292 {scwFoot.onclick = null;
1293 scwFoot.className = 'scwFootDisabled';
1296 {scwFoot.onmouseover = null;
1297 scwFoot.onmouseout = null;
1300 if (document.addEventListener)
1301 {scwFoot.addEventListener('click',scwStopPropagation,false);}
1302 else {scwFoot.attachEvent('onclick',scwStopPropagation);}
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()
1312 ((scwDisabledDates[k].constructor == Array) &&
1313 scwDateNow.valueOf() >= scwDisabledDates[k][0].valueOf() &&
1314 scwDateNow.valueOf() <= scwDisabledDates[k][1].valueOf()
1319 {scwFoot.onclick = null;
1320 scwFoot.className = 'scwFootDisabled';
1323 {scwFoot.onmouseover = null;
1324 scwFoot.onmouseout = null;
1327 if (document.addEventListener)
1328 {scwFoot.addEventListener('click',scwStopPropagation,false);}
1329 else {scwFoot.attachEvent('onclick',scwStopPropagation);}
1333 {scwFoot.onclick=scwFootOutput;
1334 scwFoot.className='scwFoot';
1337 {scwFoot.onmouseover = scwChangeClass;
1338 scwFoot.onmouseout = scwChangeClass;
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);}
1352 function scwCellOutput(scwEvt)
1353 {var scwEle = scwEventTrigger(scwEvt),
1354 scwOutputDate = new Date(scwStartDate);
1356 if (scwEle.nodeType==3) scwEle=scwEle.parentNode;
1358 scwOutputDate.setDate(scwStartDate.getDate() + parseInt(scwEle.id.substr(8),10));
1360 scwSetOutput(scwOutputDate);
1363 function scwChangeClass(scwEvt)
1364 {var scwEle = scwEventTrigger(scwEvt);
1366 if (scwEle.nodeType==3) {scwEle=scwEle.parentNode;}
1368 switch (scwEle.className)
1370 scwEle.className = 'scwCellsHover';
1372 case 'scwCellsHover':
1373 scwEle.className = 'scwCells';
1375 case 'scwCellsExMonth':
1376 scwEle.className = 'scwCellsExMonthHover';
1378 case 'scwCellsExMonthHover':
1379 scwEle.className = 'scwCellsExMonth';
1381 case 'scwCellsWeekend':
1382 scwEle.className = 'scwCellsWeekendHover';
1384 case 'scwCellsWeekendHover':
1385 scwEle.className = 'scwCellsWeekend';
1388 scwEle.className = 'scwFootHover';
1390 case 'scwFootHover':
1391 scwEle.className = 'scwFoot';
1393 case 'scwInputDate':
1394 scwEle.className = 'scwInputDateHover';
1396 case 'scwInputDateHover':
1397 scwEle.className = 'scwInputDate';
1403 function scwEventTrigger(scwEvt)
1404 {if (!scwEvt) {scwEvt = event;}
1405 return scwEvt.target||scwEvt.srcElement;
1408 function scwWeekNumber(scwInDate)
1409 {// The base day in the week of the input date
1410 var scwInDateWeekBase = new Date(scwInDate);
1412 scwInDateWeekBase.setDate(scwInDateWeekBase.getDate()
1413 - scwInDateWeekBase.getDay()
1414 + scwWeekNumberBaseDay
1415 + ((scwInDate.getDay()>
1416 scwWeekNumberBaseDay)?7:0));
1418 // The first Base Day in the year
1419 var scwFirstBaseDay = new Date(scwInDateWeekBase.getFullYear(),0,1);
1421 scwFirstBaseDay.setDate(scwFirstBaseDay.getDate()
1422 - scwFirstBaseDay.getDay()
1423 + scwWeekNumberBaseDay
1426 if (scwFirstBaseDay < new Date(scwInDateWeekBase.getFullYear(),0,1))
1427 {scwFirstBaseDay.setDate(scwFirstBaseDay.getDate()+7);}
1430 var scwStartWeekOne = new Date(scwFirstBaseDay
1431 - scwWeekNumberBaseDay
1432 + scwInDate.getDay());
1434 if (scwStartWeekOne > scwFirstBaseDay)
1435 {scwStartWeekOne.setDate(scwStartWeekOne.getDate()-7);}
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.
1445 var scwWeekNo = '0' + (Math.round((scwInDateWeekBase - scwFirstBaseDay)/604800000,0) + 1);
1447 // Return the last two characters in the week number string
1449 return scwWeekNo.substring(scwWeekNo.length-2, scwWeekNo.length);
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
1457 var scwCells = scwID('scwCells');
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')
1472 scwTmpEl.style.display='';
1475 {scwRows.childNodes[0].style.display='none';}
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],
1484 ((scwOutOfRangeDisable &&
1486 (new Date(scwBaseYear,0,1,
1487 scwShowDate.getHours()))
1490 (new Date(scwBaseYear+
1491 scwDropDownYears,0,0,
1492 scwShowDate.getHours()))
1495 (scwOutOfMonthDisable &&
1497 (new Date(scwShowDate.getFullYear(),
1499 scwShowDate.getHours()))
1502 (new Date(scwShowDate.getFullYear(),
1504 scwShowDate.getHours()))
1509 scwCell.style.visibility =
1510 (scwOutOfMonthHide &&
1512 (new Date(scwShowDate.getFullYear(),
1514 scwShowDate.getHours()))
1517 (new Date(scwShowDate.getFullYear(),
1519 scwShowDate.getHours()))
1521 )?'hidden':'inherit';
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()
1528 {scwDisabled = true;}
1530 {if ((typeof scwDisabledDates[k]=='object') &&
1531 (scwDisabledDates[k].constructor == Array) &&
1532 scwCompareDateValue >= scwDisabledDates[k][0].valueOf() &&
1533 scwCompareDateValue <= scwDisabledDates[k][1].valueOf()
1535 {scwDisabled = true;}
1540 !scwEnabledDay[j-1+(7*((i*scwCells.childNodes.length)/6))] ||
1541 !scwPassEnabledDay[(j-1+(7*(i*scwCells.childNodes.length/6)))%7]
1543 {scwRows.childNodes[j].onclick = null;
1546 {scwRows.childNodes[j].onmouseover = null;
1547 scwRows.childNodes[j].onmouseout = null;
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';
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')
1571 {scwRows.childNodes[j].onclick=scwCellOutput;
1574 {scwRows.childNodes[j].onmouseover = scwChangeClass;
1575 scwRows.childNodes[j].onmouseout = scwChangeClass;
1579 (scwShowDate.getMonth()!=scwCurMonth)
1581 :(scwBlnFullInputDate &&
1582 scwShowDate.toDateString()==
1583 scwSeedDate.toDateString())
1585 :(scwShowDate.getDay()%6==0)
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')
1599 scwShowDate.setDate(scwShowDate.getDate()+1);
1600 scwCompareDateValue = new Date(scwShowDate.getFullYear(),scwShowDate.getMonth(),scwShowDate.getDate()).valueOf();
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.
1614 {scwID('scwMonths').style.display = 'inline';
1615 scwID('scwYears' ).style.display = 'inline';
1616 scwID('scw').style.visibility='hidden';
1617 scwID('scw').style.visibility='inherit';
1621 // *************************
1622 // End of Function Library
1623 // *************************
1624 // ***************************
1625 // Start of Calendar structure
1626 // ***************************
1628 document.writeln("<!--[if IE]><div id='scwIE'></div><![endif]-->");
1629 document.writeln("<!--[if lt IE 7]><div id='scwIElt7'></div><![endif]-->");
1631 "<iframe class='scw' " + (scwID('scwIElt7')?"src='/scwblank.html '":'') +
1632 "id='scwIframe' name='scwIframe' frameborder='0'>" +
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>" +
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);'>" +
1654 "<td class='scwHead'>" +
1655 "<select id='scwYears' class='scwHead' " +
1656 "onchange='scwShowMonth(0);'>" +
1659 "<td class='scwHead'>" +
1660 "<input class='scwHead' id='scwHeadRight' type='button' value='>' " +
1661 "onclick='scwShowMonth(1);' /></td>" +
1666 "<tr class='scw'>" +
1667 "<td class='scw'>" +
1668 "<table class='scwCells' align='center'>" +
1670 "<tr><td class='scwWeekNumberHead' id='scwWeek_' ></td>");
1674 "<td class='scwWeek' id='scwWeekInit" + i + "'></td>");
1677 document.write("</tr>" +
1679 "<tbody id='scwCells' " +
1680 "onClick='scwStopPropagation(event);'>");
1685 "<td class='scwWeekNo' id='scwWeek_" + i + "'></td>");
1688 "<td class='scwCells' id='scwCell_" + (j+(i*7)) +
1699 if ((new Date(scwBaseYear + scwDropDownYears, 0, 0)) > scwDateNow &&
1700 (new Date(scwBaseYear, 0, 0)) < scwDateNow)
1702 "<tfoot class='scwFoot'>" +
1703 "<tr class='scwFoot'>" +
1704 "<td class='scwFoot' id='scwFoot' colspan='8'>" +
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);
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);
1734 // ***************************
1735 // End of Calendar structure
1736 // ***************************
1737 // ****************************************
1738 // Start of document level event definition
1739 // ****************************************
1741 if (document.addEventListener)
1742 {document.addEventListener('click',scwHide, false);}
1743 else {document.attachEvent('onclick',scwHide);}
1745 // ****************************************
1746 // End of document level event definition
1747 // ****************************************
1748 // ************************************
1749 // End of Simple Calendar Widget Code
1750 // ************************************