16b099fc56d7166d671a662a14ecdd0db27149e3
[plewww.git] / plekit / datepicker / datepicker.js
1 /*\r
2         DatePicker v4b rc1 by frequency-decoder.com\r
3 \r
4         Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)\r
5 \r
6         Please credit frequency-decoder in any derivative work - thanks.\r
7         \r
8         You are free:\r
9 \r
10         * to copy, distribute, display, and perform the work\r
11         * to make derivative works\r
12         * to make commercial use of the work\r
13 \r
14         Under the following conditions:\r
15 \r
16                 by Attribution.\r
17                 --------------\r
18                 You must attribute the work in the manner specified by the author or licensor.\r
19 \r
20                 sa\r
21                 --\r
22                 Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.\r
23 \r
24         * For any reuse or distribution, you must make clear to others the license terms of this work.\r
25         * Any of these conditions can be waived if you get permission from the copyright holder.\r
26 */\r
27 \r
28 var datePickerController = (function datePickerController() {\r
29         var languageInfo        = navigator.language ? navigator.language.toLowerCase().replace(/-[a-z]+$/, "") : navigator.userLanguage ? navigator.userLanguage.toLowerCase().replace(/-[a-z]+$/, "") : "en",\r
30             datePickers         = {},\r
31             uniqueId            = 0,\r
32             weeksInYearCache    = {},\r
33             localeImport        = false,\r
34             nbsp                = String.fromCharCode(160),\r
35             nodrag              = false,\r
36             splitAppend         = ["-dd","-mm"],\r
37             formatMasks         = ["Y-sl-m-sl-d","m-sl-d-sl-Y","d-sl-m-sl-Y","Y-ds-m-ds-d","m-ds-d-ds-Y","d-ds-m-ds-Y"];                \r
38         \r
39         void function() {\r
40                 var scriptFiles = document.getElementsByTagName('head')[0].getElementsByTagName('script'),                    \r
41                     scriptInner = scriptFiles[scriptFiles.length - 1].innerHTML.replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""),                    \r
42                     json        = parseJSON(scriptInner);                \r
43                \r
44                 if(typeof json === "object" && !("err" in json)) {                          \r
45                         affectJSON(json);\r
46                 };\r
47        \r
48                 if(typeof(fdLocale) != "object" && languageInfo != "en") {\r
49                         var loc    = scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/" + languageInfo + ".js",\r
50                             script = document.createElement('script');\r
51                                                           \r
52                         script.type = "text/javascript";                         \r
53                         script.src  = loc;\r
54                         script.setAttribute("charset", "utf-8");\r
55                         /*@cc_on\r
56                         /*@if(@_win32)\r
57                         var bases = document.getElementsByTagName('base');\r
58                         if (bases.length && bases[0].childNodes.length) {\r
59                                 bases[0].appendChild(script);\r
60                         } else {\r
61                                 document.getElementsByTagName('head')[0].appendChild(script);\r
62                         };\r
63                         bases = null;\r
64                         @else @*/\r
65                         document.getElementsByTagName('head')[0].appendChild(script);\r
66                         /*@end\r
67                         @*/    \r
68                         \r
69                         script = null;                      \r
70                 };                              \r
71         }();\r
72         \r
73         function affectJSON(json) {\r
74                 if(typeof json !== "object") { return; };\r
75                 for(key in json) {\r
76                         value = json[key];                                                                \r
77                         switch(key.toLower()) { \r
78                                 case "lang":\r
79                                         if(value.search(/^[a-z]{2}$/i) != -1) {\r
80                                                 languageInfo = value;\r
81                                         };\r
82                                         break;\r
83                                 case "split":                                                 \r
84                                         if(typeof value === 'object') {\r
85                                                 if(value.length && value.length == 2) {                                                        \r
86                                                         splitAppend = value;\r
87                                                 };\r
88                                         }; \r
89                                 case "formats":                                                 \r
90                                         if(typeof value === 'object') {\r
91                                                 if(value.length) {\r
92                                                         formatMasks = value;\r
93                                                 };\r
94                                         };\r
95                                         break;\r
96                                 case "nodrag":\r
97                                         nodrag = !!value;                                                                                                                                         \r
98                         };          \r
99                 };        \r
100         };\r
101                 \r
102         // Functions shared between the datePickerController object & the datePicker objects    \r
103         function pad(value, length) { \r
104                 length = length || 2; \r
105                 return "0000".substr(0,length - Math.min(String(value).length, length)) + value; \r
106         };\r
107         \r
108         function addEvent(obj, type, fn) {\r
109                 if( obj.attachEvent ) {\r
110                         obj["e"+type+fn] = fn;\r
111                         obj[type+fn] = function(){obj["e"+type+fn]( window.event );};\r
112                         obj.attachEvent( "on"+type, obj[type+fn] );\r
113                 } else {\r
114                         obj.addEventListener( type, fn, true );\r
115                 };\r
116         };\r
117         \r
118         function removeEvent(obj, type, fn) {\r
119                 try {\r
120                         if( obj.detachEvent ) {\r
121                                 obj.detachEvent( "on"+type, obj[type+fn] );\r
122                                 obj[type+fn] = null;\r
123                         } else {\r
124                                 obj.removeEventListener( type, fn, true );\r
125                         };\r
126                 } catch(err) {};\r
127         };   \r
128 \r
129         function stopEvent(e) {\r
130                 e = e || document.parentWindow.event;\r
131                 if(e.stopPropagation) {\r
132                         e.stopPropagation();\r
133                         e.preventDefault();\r
134                 };\r
135                 /*@cc_on\r
136                 @if(@_win32)\r
137                 e.cancelBubble = true;\r
138                 e.returnValue = false;\r
139                 @end\r
140                 @*/\r
141                 return false;\r
142         };\r
143         \r
144         function parseJSON(str) {\r
145                 // Check we have a String\r
146                 if(typeof str !== 'string' || str == "") { return {}; };                 \r
147                 try {\r
148                         // Does the Douglas Crockford JSON parser exist in the global scope?\r
149                         if("JSON" in window && "parse" in window.JSON && typeof window.JSON.parse == "function") {                                               \r
150                                 return window.JSON.parse(str);  \r
151                         // Genious code taken from: http://kentbrewster.com/badges/                                                      \r
152                         } else if(/lang|split|formats|nodrag/.test(str.toLower())) {                                               \r
153                                 var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',\r
154                                         'Array,String,Math,RegExp,Image,ActiveXObject;',\r
155                                         'return (' , str.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function­') , ');'].join(''));\r
156                                 return f();                          \r
157                         };\r
158                 } catch (e) {                                 \r
159                         return {"err":"Trouble parsing JSON object"};\r
160                 };\r
161                 return {};                                            \r
162         };        \r
163 \r
164         // The datePicker object itself \r
165         function datePicker(options) {                                      \r
166                 this.dateSet             = null;                 \r
167                 this.timerSet            = false;\r
168                 this.visible             = false;\r
169                 this.fadeTimer           = null;\r
170                 this.timer               = null;\r
171                 this.yearInc             = 0;\r
172                 this.monthInc            = 0;\r
173                 this.dayInc              = 0;\r
174                 this.mx                  = 0;\r
175                 this.my                  = 0;\r
176                 this.x                   = 0;\r
177                 this.y                   = 0;                  \r
178                 this.date                = new Date();\r
179                 this.defaults            = {};\r
180                 this.created             = false;\r
181                 this.id                  = options.id;\r
182                 this.opacity             = 0;          \r
183                 this.firstDayOfWeek      = 0; \r
184                 this.buttonWrapper       = "buttonWrapper" in options ? options.buttonWrapper : false;                \r
185                 this.staticPos           = "staticPos" in options ? !!options.staticPos : false;\r
186                 this.disabledDays        = "disabledDays" in options && options.disabledDays.length ? options.disabledDays : [0,0,0,0,0,0,0];\r
187                 this.disabledDates       = "disabledDates" in options ? options.disabledDates : {};\r
188                 this.enabledDates        = "enabledDates" in options ? options.enabledDates : {};\r
189                 this.showWeeks           = "showWeeks" in options ? !!options.showWeeks : false;\r
190                 this.low                 = options.low || "";\r
191                 this.high                = options.high || "";\r
192                 this.dragDisabled        = nodrag ? true : ("dragDisabled" in options ? !!options.dragDisabled : false);\r
193                 this.positioned          = "positioned" in options ? options.positioned : false;\r
194                 this.hideInput           = this.staticPos ? false : "hideInput" in options ? !!options.hideInput : false;\r
195                 this.splitDate           = "splitDate" in options ? !!options.splitDate : false;\r
196                 this.format              = options.format || "d-sl-m-sl-Y";\r
197                 this.statusFormat        = options.statusFormat || "";\r
198                 this.highlightDays       = options.highlightDays && options.highlightDays.length ? options.highlightDays : [0,0,0,0,0,1,1];\r
199                 this.noFadeEffect        = "noFadeEffect" in options ? !!options.noFadeEffect : false;\r
200                 this.opacityTo           = this.noFadeEffect || this.staticPos ? 99 : 90;\r
201                 this.callbacks           = {};\r
202                 this.fillGrid            = !!options.fillGrid;\r
203                 this.noToday             = !!options.noToday;\r
204                 this.constrainSelection  = this.fillGrid && !!options.constrainSelection;\r
205                 this.finalOpacity        = !this.staticPos && "finalOpacity" in options ? +options.finalOpacity : 90;                \r
206                 this.dynDisabledDates    = {};\r
207                 this.inUpdate            = false;\r
208                 /*@cc_on\r
209                 /*@if(@_win32)\r
210                 this.interval            = new Date();\r
211                 this.iePopUp             = null;\r
212                 /*@end@*/\r
213                 \r
214                 for(var thing in options.callbacks) {\r
215                         this.callbacks[thing] = options.callbacks[thing];                 \r
216                 };\r
217                 \r
218                 // Adjust time to stop daylight savings madness on windows\r
219                 this.date.setHours(12);              \r
220           \r
221                 this.startDrag = function(e) {\r
222                         e = e || document.parentWindow.event;\r
223                         o.mx = e.pageX?e.pageX:e.clientX?e.clientX:e.x;\r
224                         o.my = e.pageY?e.pageY:e.clientY?e.clientY:e.Y;\r
225                         o.x = parseInt(o.div.style.left);\r
226                         o.y = parseInt(o.div.style.top);\r
227                         addEvent(document,'mousemove',o.trackDrag, false);\r
228                         addEvent(document,'mouseup',o.stopDrag, false);\r
229                         o.div.style.zIndex = 10000;\r
230                         return stopEvent(e);\r
231                 };\r
232                 this.trackDrag = function(e) {\r
233                         e = e || window.event;\r
234                         var diffx = (e.pageX?e.pageX:e.clientX?e.clientX:e.x) - o.mx;\r
235                         var diffy = (e.pageY?e.pageY:e.clientY?e.clientY:e.Y) - o.my;\r
236                         o.div.style.left = Math.round(o.x + diffx) > 0 ? Math.round(o.x + diffx) + 'px' : "0px";\r
237                         o.div.style.top  = Math.round(o.y + diffy) > 0 ? Math.round(o.y + diffy) + 'px' : "0px";\r
238                         /*@cc_on\r
239                         @if(@_jscript_version <= 5.6)\r
240                         if(o.staticPos) return;\r
241                         o.iePopUp.style.top    = o.div.style.top;\r
242                         o.iePopUp.style.left   = o.div.style.left;\r
243                         @end\r
244                         @*/\r
245                 };\r
246                 this.stopDrag = function(e) {\r
247                         removeEvent(document,'mousemove',o.trackDrag, false);\r
248                         removeEvent(document,'mouseup',o.stopDrag, false);\r
249                         o.div.style.zIndex = 9999;\r
250                 };\r
251                 this.changeHandler = function() {                        \r
252                         o.setDateFromInput();                        \r
253                         if(o.created) o.updateTable();\r
254                 };\r
255                 this.reposition = function() {\r
256                         if(!o.created || !o.getElem() || o.staticPos) { return; };\r
257 \r
258                         o.div.style.visibility = "hidden";\r
259                         o.div.style.left = o.div.style.top = "0px";                           \r
260                         o.div.style.display = "block";\r
261 \r
262                         var osh         = o.div.offsetHeight,\r
263                             osw         = o.div.offsetWidth,\r
264                             elem        = document.getElementById('fd-but-' + o.id),\r
265                             pos         = o.truePosition(elem),\r
266                             trueBody    = (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body,\r
267                             scrollTop   = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollTop,\r
268                             scrollLeft  = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollLeft;\r
269 \r
270                         o.div.style.visibility = "visible";\r
271 \r
272                         o.div.style.left  = Number(parseInt(trueBody.clientWidth+scrollLeft) < parseInt(osw+pos[0]) ? Math.abs(parseInt((trueBody.clientWidth+scrollLeft) - osw)) : pos[0]) + "px";\r
273                         o.div.style.top   = Number(parseInt(trueBody.clientHeight+scrollTop) < parseInt(osh+pos[1]+elem.offsetHeight+2) ? Math.abs(parseInt(pos[1] - (osh + 2))) : Math.abs(parseInt(pos[1] + elem.offsetHeight + 2))) + "px";\r
274 \r
275                         /*@cc_on\r
276                         @if(@_jscript_version <= 5.6)\r
277                         o.iePopUp.style.top    = o.div.style.top;\r
278                         o.iePopUp.style.left   = o.div.style.left;\r
279                         o.iePopUp.style.width  = osw + "px";\r
280                         o.iePopUp.style.height = (osh - 2) + "px";\r
281                         @end\r
282                         @*/\r
283                 }; \r
284                 this.updateTable = function(noCallback) {  \r
285                         if(o.inUpdate) return;\r
286                          \r
287                         o.inUpdate = true;\r
288                         o.removeHighlight();\r
289                                              \r
290                         if(o.timerSet) {\r
291                                 o.date.setDate(Math.min(o.date.getDate()+o.dayInc, daysInMonth(o.date.getMonth()+o.monthInc,o.date.getFullYear()+o.yearInc)) );\r
292                                 o.date.setMonth(o.date.getMonth() + o.monthInc);\r
293                                 o.date.setFullYear(o.date.getFullYear() + o.yearInc);\r
294                         }; \r
295         \r
296                         o.outOfRange();\r
297                         if(!o.noToday) { o.disableTodayButton(); };\r
298                         o.showHideButtons(o.date);\r
299                 \r
300                         var cd = o.date.getDate(),\r
301                             cm = o.date.getMonth(),\r
302                             cy = o.date.getFullYear(),\r
303                             cursorDate = (String(cy) + pad(cm+1) + pad(cd)),\r
304                             tmpDate    = new Date(cy, cm, 1);                      \r
305                 \r
306                         tmpDate.setHours(5);\r
307                         \r
308                         var dt, cName, td, i, currentDate, cellAdded, col, currentStub, abbr, bespokeRenderClass,\r
309                         weekDayC            = ( tmpDate.getDay() + 6 ) % 7,                \r
310                         firstColIndex       = (((weekDayC - o.firstDayOfWeek) + 7 ) % 7) - 1,\r
311                         dpm                 = daysInMonth(cm, cy),\r
312                         today               = new Date(),\r
313                         dateSetD            = (o.dateSet != null) ? o.dateSet.getFullYear() + pad(o.dateSet.getMonth()+1) + pad(o.dateSet.getDate()) : false,\r
314                         stub                = String(tmpDate.getFullYear()) + pad(tmpDate.getMonth()+1),\r
315                         cellAdded           = [4,4,4,4,4,4],                                                                   \r
316                         lm                  = new Date(cy, cm-1, 1),\r
317                         nm                  = new Date(cy, cm+1, 1),                          \r
318                         daySub              = daysInMonth(lm.getMonth(), lm.getFullYear()),                \r
319                         stubN               = String(nm.getFullYear()) + pad(nm.getMonth()+1),\r
320                         stubP               = String(lm.getFullYear()) + pad(lm.getMonth()+1),                \r
321                         weekDayN            = (nm.getDay() + 6) % 7,\r
322                         weekDayP            = (lm.getDay() + 6) % 7,                                       \r
323                         today               = today.getFullYear() + pad(today.getMonth()+1) + pad(today.getDate());\r
324                         \r
325                         o.firstDateShown    = !o.constrainSelection && o.fillGrid && (0 - firstColIndex < 1) ? String(stubP) + (daySub + (0 - firstColIndex)) : stub + "01";            \r
326                         o.lastDateShown     = !o.constrainSelection && o.fillGrid ? stubN + pad(41 - firstColIndex - dpm) : stub + String(dpm);\r
327                         o.currentYYYYMM     = stub;                    \r
328                 \r
329                         bespokeRenderClass  = o.callback("redraw", {id:o.id, dd:pad(cd), mm:pad(cm+1), yyyy:cy, firstDateDisplayed:o.firstDateShown, lastDateDisplayed:o.lastDateShown}) || {};                    \r
330                         o.dynDisabledDates  = o.getDisabledDates(cy, cm + 1);\r
331                        \r
332                         for(var curr = 0; curr < 42; curr++) {\r
333                                 row  = Math.floor(curr / 7);                         \r
334                                 td   = o.tds[curr];\r
335                                 \r
336                                 while(td.firstChild) td.removeChild(td.firstChild);\r
337                                 if((curr > firstColIndex && curr <= (firstColIndex + dpm)) || o.fillGrid) {\r
338                                         currentStub     = stub;\r
339                                         weekDay         = weekDayC;                                \r
340                                         dt              = curr - firstColIndex;\r
341                                         cName           = [];                                         \r
342                                         selectable      = true;\r
343                                 \r
344                                         if(dt < 1) {\r
345                                                 dt              = daySub + dt;\r
346                                                 currentStub     = stubP;\r
347                                                 weekDay         = weekDayP;                                        \r
348                                                 selectable      = !o.constrainSelection;\r
349                                                 cName.push("month-out");                                                  \r
350                                         } else if(dt > dpm) {\r
351                                                 dt -= dpm;\r
352                                                 currentStub     = stubN;\r
353                                                 weekDay         = weekDayN;                                        \r
354                                                 selectable      = !o.constrainSelection; \r
355                                                 cName.push("month-out");                                                                                           \r
356                                         }; \r
357                                                                                                    \r
358                                         td.appendChild(document.createTextNode(dt));\r
359                                         currentDate = currentStub + String(dt < 10 ? "0" : "") + dt;                            \r
360                                         \r
361                                         if(o.low && +currentDate < +o.low || o.high && +currentDate > +o.high) {                                          \r
362                                                 td.className = "out-of-range";                                                \r
363                                                 if(o.showWeeks) { cellAdded[row] = Math.min(cellAdded[row], 2); };                                                  \r
364                                         \r
365                                         } else {                               \r
366                                                 if(selectable) {                                                 \r
367                                                         cName.push("cd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));\r
368                                                 } else {                                                    \r
369                                                         cName.push("not-selectable yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));\r
370                                                 };                                                                                                      \r
371                                         \r
372                                                 weekDay = ( weekDay + dt + 6 ) % 7;\r
373 \r
374                                                 if(currentDate == today) { cName.push("date-picker-today"); };\r
375 \r
376                                                 if(dateSetD == currentDate) { cName.push("date-picker-selected-date"); };\r
377 \r
378                                                 if(o.disabledDays[weekDay] || currentDate in o.dynDisabledDates) { cName.push("day-disabled"); }\r
379                                         \r
380                                                 if(currentDate in bespokeRenderClass) { cName.push(bespokeRenderClass[currentDate]); }\r
381                                         \r
382                                                 if(o.highlightDays[weekDay]) { cName.push("date-picker-highlight"); };\r
383 \r
384                                                 if(cursorDate == currentDate) { td.id = o.id + "-date-picker-hover"; cName.push("date-picker-hover"); }\r
385                                                 else { td.id = ""; };\r
386                                         \r
387                                                 td.className = cName.join(" ");\r
388 \r
389                                                 if(o.showWeeks) {                                                         \r
390                                                         cellAdded[row] = Math.min(cName[0] == "month-out" ? 3 : 1, cellAdded[row]);                                                          \r
391                                                 }; \r
392                                         };                       \r
393                                 } else {\r
394                                         td.className = "date-picker-unused";\r
395                                         td.id = "";                                         \r
396                                         td.appendChild(document.createTextNode(nbsp));                                        \r
397                                 };                                 \r
398                                 \r
399                                 if(o.showWeeks && curr - (row * 7) == 6) { \r
400                                         while(o.wkThs[row].firstChild) o.wkThs[row].removeChild(o.wkThs[row].firstChild);                                         \r
401                                         o.wkThs[row].appendChild(document.createTextNode(cellAdded[row] == 4 && !o.fillGrid ? nbsp : getWeekNumber(cy, cm, curr - firstColIndex - 6)));\r
402                                         o.wkThs[row].className = "date-picker-week-header" + (["",""," out-of-range"," month-out",""][cellAdded[row]]);                                          \r
403                                 };                                \r
404                         };\r
405 \r
406                         // Title Bar\r
407                         var span = o.titleBar.getElementsByTagName("span");\r
408                         while(span[0].firstChild) span[0].removeChild(span[0].firstChild);\r
409                         while(span[1].firstChild) span[1].removeChild(span[1].firstChild);\r
410                         span[0].appendChild(document.createTextNode(getMonthTranslation(cm, false) + nbsp));\r
411                         span[1].appendChild(document.createTextNode(cy));\r
412                         \r
413                         if(o.timerSet) {\r
414                                 o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));\r
415                                 o.timer = window.setTimeout(o.updateTable, o.timerInc);\r
416                         };\r
417                         \r
418                         o.inUpdate = false;                          \r
419                 };\r
420                 \r
421                 this.show = function() {\r
422                         var elem = this.getElem();\r
423                         if(!elem || this.visible || elem && elem.disabled) { return; };\r
424 \r
425                         if(!document.getElementById('fd-' + this.id)) {\r
426                                 this.created = false;\r
427                                 this.create();\r
428                         } else {                           \r
429                                 this.setDateFromInput();\r
430                                 this.outOfRange();\r
431                                 this.updateTable();\r
432                         };                         \r
433                         \r
434                         if(!this.staticPos) this.reposition();                        \r
435 \r
436                         addEvent(this.staticPos ? this.table : document, "mousedown", this.events.onmousedown);\r
437                         this.opacityTo = this.finalOpacity;\r
438                         this.div.style.display = "block";\r
439                         if(!this.staticPos) {\r
440                                 /*@cc_on\r
441                                 @if(@_jscript_version <= 5.6)\r
442                                 this.iePopUp.style.width = this.div.offsetWidth + "px";\r
443                                 this.iePopUp.style.height = this.div.offsetHeight + "px";\r
444                                 this.iePopUp.style.display = "block";\r
445                                 @end\r
446                                 @*/\r
447                                 this.addKeyboardEvents();\r
448                                 this.fade();\r
449                                 var butt = document.getElementById('fd-but-' + this.id);\r
450                                 if(butt) butt.className = butt.className.replace("dp-button-active", "") + " dp-button-active";\r
451 \r
452                         } else {\r
453                                 this.opacity = this.opacityTo;\r
454                         };\r
455                 };\r
456                 this.hide = function() {\r
457                         if(!this.visible) return;\r
458                         this.stopTimer();\r
459                         if(this.staticPos) return;\r
460 \r
461                         var butt = document.getElementById('fd-but-' + this.id);\r
462                         if(butt) butt.className = butt.className.replace("dp-button-active", "");\r
463                 \r
464                         removeEvent(document, "mousedown", this.events.onmousedown);\r
465                         removeEvent(document, "mouseup",  this.events.clearTimer);\r
466                         this.removeKeyboardEvents();\r
467 \r
468                         /*@cc_on\r
469                         @if(@_jscript_version <= 5.6)\r
470                         this.iePopUp.style.display = "none";\r
471                         @end\r
472                         @*/\r
473 \r
474                         this.opacityTo = 0;\r
475                         this.fade();\r
476                         \r
477                         //var elem = this.getElem();\r
478                         //if(!elem.type || elem.type && elem.type != "hidden") { elem.focus(); };\r
479                 };\r
480                 this.destroy = function() {\r
481                         // Cleanup for Internet Explorer\r
482                         removeEvent(this.staticPos ? this.table : document, "mousedown", o.events.onmousedown);\r
483                         removeEvent(document, "mouseup",   o.events.clearTimer);\r
484                         o.removeKeyboardEvents();\r
485                         clearTimeout(o.fadeTimer);\r
486                         clearTimeout(o.timer);\r
487 \r
488                         /*@cc_on\r
489                         @if(@_jscript_version <= 5.6)\r
490                         if(!o.staticPos) {\r
491                                 o.iePopUp.parentNode.removeChild(o.iePopUp);\r
492                                 o.iePopUp = null;\r
493                         };\r
494                         @end\r
495                         @*/\r
496 \r
497                         if(!this.staticPos && document.getElementById(this.id.replace(/^fd-/, 'fd-but-'))) {\r
498                                 var butt = document.getElementById(this.id.replace(/^fd-/, 'fd-but-'));\r
499                                 butt.onclick = butt.onpress = null;\r
500                         };\r
501 \r
502                         if(this.div && this.div.parentNode) {\r
503                                 this.div.parentNode.removeChild(this.div);\r
504                         };\r
505 \r
506                         o = null;\r
507                 };\r
508                 this.resizeInlineDiv = function()  {                        \r
509                         o.div.style.width = o.table.offsetWidth + "px";\r
510                 };\r
511                 this.create = function() {\r
512                         if(this.created) { return; };\r
513 \r
514                         function createTH(details) {\r
515                                 var th = document.createElement('th');\r
516                                 if(details.thClassName) th.className = details.thClassName;\r
517                                 if(details.colspan) {\r
518                                         /*@cc_on\r
519                                         /*@if (@_win32)\r
520                                         th.setAttribute('colSpan',details.colspan);\r
521                                         @else @*/\r
522                                         th.setAttribute('colspan',details.colspan);\r
523                                         /*@end\r
524                                         @*/\r
525                                 };\r
526                                 /*@cc_on\r
527                                 /*@if (@_win32)\r
528                                 th.unselectable = "on";\r
529                                 /*@end@*/\r
530                                 return th;\r
531                         };\r
532                         function createThAndButton(tr, obj) {\r
533                                 for(var i = 0, details; details = obj[i]; i++) {\r
534                                         var th = createTH(details);\r
535                                         tr.appendChild(th);\r
536                                         var but = document.createElement('span');\r
537                                         but.className = details.className;\r
538                                         but.id = o.id + details.id;\r
539                                         but.appendChild(document.createTextNode(details.text || o.nbsp));\r
540                                         but.title = details.title || "";\r
541                                         if(details.onmousedown) but.onmousedown = details.onmousedown;\r
542                                         if(details.onclick)     but.onclick     = details.onclick;\r
543                                         if(details.onmouseout)  but.onmouseout  = details.onmouseout;\r
544                                         /*@cc_on\r
545                                         /*@if(@_win32)\r
546                                         th.unselectable = but.unselectable = "on";\r
547                                         /*@end@*/\r
548                                         th.appendChild(but);\r
549                                 };\r
550                         };  \r
551                         \r
552                         this.div                     = document.createElement('div');\r
553                         this.div.id                  = "fd-" + this.id;\r
554                         this.div.className           = "datePicker";                   \r
555                         \r
556                         var tr, row, col, tableHead, tableBody, tableFoot;\r
557 \r
558                         this.table             = document.createElement('table');\r
559                         this.table.className   = "datePickerTable";                         \r
560                         this.table.onmouseover = this.events.ontablemouseover;\r
561                         this.table.onmouseout  = this.events.ontablemouseout;\r
562 \r
563                         this.div.appendChild(this.table);   \r
564                                 \r
565                         if(!this.staticPos) {\r
566                                 this.div.style.visibility = "hidden";\r
567                                 if(!this.dragDisabled) { this.div.className += " drag-enabled"; };\r
568                                 document.getElementsByTagName('body')[0].appendChild(this.div);\r
569                                                                 \r
570                                 /*@cc_on\r
571                                 @if(@_jscript_version <= 5.6)                                            \r
572                                 this.iePopUp = document.createElement('iframe');\r
573                                 this.iePopUp.src = "javascript:'<html></html>';";\r
574                                 this.iePopUp.setAttribute('className','iehack');\r
575                                 this.iePopUp.scrolling="no";\r
576                                 this.iePopUp.frameBorder="0";\r
577                                 this.iePopUp.name = this.iePopUp.id = this.id + "-iePopUpHack";\r
578                                 document.body.appendChild(this.iePopUp);\r
579                                 @end\r
580                                 @*/\r
581                         } else {\r
582                                 elem = this.positioned ? document.getElementById(this.positioned) : this.getElem();\r
583                                 if(!elem) {\r
584                                         this.div = null;\r
585                                         throw this.positioned ? "Could not locate a datePickers associated parent element with an id:" + this.positioned : "Could not locate a datePickers associated input with an id:" + this.id;\r
586                                 };\r
587 \r
588                                 this.div.className += " static-datepicker";                               \r
589                                                                \r
590                                 // tabIndex\r
591                                 this.div.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");\r
592                                 this.div.tabIndex = 0;\r
593 \r
594                                 this.div.onfocus = this.events.onfocus;\r
595                                 this.div.onblur  = this.events.onblur;                                                                                                                         \r
596                                 \r
597                                 if(this.positioned) {\r
598                                         elem.appendChild(this.div);\r
599                                 } else {\r
600                                         elem.parentNode.insertBefore(this.div, elem.nextSibling);\r
601                                 };\r
602                                 \r
603                                 if(this.hideInput) {\r
604                                         var elemList = [elem];                                        \r
605                                         if(this.splitDate) {\r
606                                                 elemList[elemList.length] = document.getElementById(this.id + splitAppend[1]);\r
607                                                 elemList[elemList.length] = document.getElementById(this.id + splitAppend[0]);                                         \r
608                                         };\r
609                                         for(var i = 0; i < elemList.length; i++) {\r
610                                                 if(elemList[i].tagName) elemList[i].className += " fd-hidden-input";        \r
611                                         };\r
612                                 };                                                                  \r
613                                                                           \r
614                                 setTimeout(this.resizeInlineDiv, 300);                               \r
615                         };\r
616 \r
617                         \r
618                         if(this.statusFormat) {\r
619                                 tableFoot = document.createElement('tfoot');\r
620                                 this.table.appendChild(tableFoot);\r
621                                 tr = document.createElement('tr');\r
622                                 tr.className = "date-picker-tfoot";\r
623                                 tableFoot.appendChild(tr);\r
624                                 this.statusBar = createTH({thClassName:"date-picker-statusbar", colspan:this.showWeeks ? 8 : 7});\r
625                                 tr.appendChild(this.statusBar);\r
626                                 this.updateStatus();\r
627                                 if(!this.dragDisabled) {\r
628                                         this.statusBar.className += " drag-enabled";\r
629                                         addEvent(this.statusBar,'mousedown',this.startDrag,false);\r
630                                 };\r
631                         };\r
632 \r
633                         tableHead = document.createElement('thead');\r
634                         this.table.appendChild(tableHead);\r
635 \r
636                         tr  = document.createElement('tr');\r
637                         tableHead.appendChild(tr);\r
638 \r
639                         // Title Bar\r
640                         this.titleBar = createTH({thClassName:!this.dragDisabled ? "date-picker-title drag-enabled" : "date-picker-title", colspan:this.showWeeks ? 8 : 7});\r
641                         if(!this.dragDisabled) {\r
642                                 addEvent(this.titleBar,'mousedown',o.startDrag,false);\r
643                         };\r
644 \r
645                         tr.appendChild(this.titleBar);\r
646                         tr = null;\r
647 \r
648                         var span = document.createElement('span');\r
649                         span.appendChild(document.createTextNode(nbsp));\r
650                         span.className = !this.dragDisabled ? "month-display drag-enabled" : "month-display";\r
651                         this.titleBar.appendChild(span);\r
652 \r
653                         span = document.createElement('span');\r
654                         span.appendChild(document.createTextNode(nbsp));\r
655                         span.className = !this.dragDisabled ? "year-display drag-enabled" : "year-display";\r
656                         this.titleBar.appendChild(span);\r
657 \r
658                         span = null;\r
659 \r
660                         tr  = document.createElement('tr');\r
661                         tableHead.appendChild(tr);\r
662 \r
663                         createThAndButton(tr, [\r
664                         {className:"prev-but prev-year", id:"-prev-year-but", text:"\u00AB", title:getTitleTranslation(2), onmousedown:function(e) { addEvent(document, "mouseup", o.events.clearTimer);  o.events.incDec(e,0,-1,0); }, onmouseout:this.events.clearTimer },\r
665                         {className:"prev-but prev-month", id:"-prev-month-but", text:"\u2039", title:getTitleTranslation(0), onmousedown:function(e) { addEvent(document, "mouseup", o.events.clearTimer); if(o.currentYYYYMM > Number(o.date.getFullYear() + pad(o.date.getMonth()+1))) { o.stopTimer(); o.updateTable(); o.timer = window.setTimeout(function() { o.events.incDec(e,0,0,-1); }, 800); return; }; o.events.incDec(e,0,0,-1); }, onmouseout:this.events.clearTimer },\r
666                         {colspan:this.showWeeks ? 4 : 3, className:"today-but", id:"-today-but", text:getTitleTranslation(4), onclick:this.events.gotoToday},\r
667                         {className:"next-but next-month", id:"-next-month-but", text:"\u203A", title:getTitleTranslation(1), onmousedown:function(e) { addEvent(document, "mouseup", o.events.clearTimer); if(o.currentYYYYMM < Number(o.date.getFullYear() + pad(o.date.getMonth()+1))) { o.stopTimer(); o.updateTable(); o.timer = window.setTimeout(function() { o.events.incDec(e,0,0,1); }, 800);  return; }; o.events.incDec(e,0,0,1); }, onmouseout:this.events.clearTimer },\r
668                         {className:"next-but next-year", id:"-next-year-but", text:"\u00BB", title:getTitleTranslation(3), onmousedown:function(e) { addEvent(document, "mouseup", o.events.clearTimer); o.events.incDec(e,0,1,0); }, onmouseout:this.events.clearTimer }]);\r
669 \r
670                         tableBody = document.createElement('tbody');\r
671                         this.table.appendChild(tableBody);\r
672 \r
673                         var colspanTotal = this.showWeeks ? 8 : 7,\r
674                             colOffset    = this.showWeeks ? 0 : -1,\r
675                             but, abbr;   \r
676                 \r
677                         for(var rows = 0; rows < 7; rows++) {\r
678                                 row = document.createElement('tr');\r
679 \r
680                                 if(rows != 0) {\r
681                                         tableBody.appendChild(row);   \r
682                                 } else {\r
683                                         tableHead.appendChild(row);\r
684                                 };\r
685 \r
686                                 for(var cols = 0; cols < colspanTotal; cols++) {\r
687                                                                                 \r
688                                         if(rows === 0 || (this.showWeeks && cols === 0)) {\r
689                                                 col = document.createElement('th');\r
690                                         } else {\r
691                                                 col = document.createElement('td');\r
692                                         };\r
693                                         \r
694                                         /*@cc_on@*/\r
695                                         /*@if(@_win32)\r
696                                         col.unselectable = "on";\r
697                                         /*@end@*/  \r
698                                         \r
699                                         row.appendChild(col);\r
700                                         if((this.showWeeks && cols > 0 && rows > 0) || (!this.showWeeks && rows > 0)) {\r
701                                                 col.onclick = this.events.onclick;\r
702                                         } else {\r
703                                                 if(rows === 0 && cols > colOffset) {\r
704                                                         col.className = "date-picker-day-header";\r
705                                                         col.scope = "col";                                           \r
706                                                 } else {\r
707                                                         col.className = "date-picker-week-header";\r
708                                                         col.scope = "row";\r
709                                                 };\r
710                                         };\r
711                                 };\r
712                         };\r
713 \r
714                         col = row = null; \r
715                 \r
716                         this.ths = this.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');\r
717                         for (var y = 0; y < colspanTotal; y++) {\r
718                                 if(y == 0 && this.showWeeks) {\r
719                                         this.ths[y].appendChild(document.createTextNode(getTitleTranslation(6)));\r
720                                         this.ths[y].title = getTitleTranslation(8);\r
721                                         continue;\r
722                                 };\r
723 \r
724                                 if(y > (this.showWeeks ? 0 : -1)) {\r
725                                         but = document.createElement("span");\r
726                                         but.className = "fd-day-header";\r
727                                         but.onclick = this.ths[y].onclick = this.setFirstDayOfWeek;\r
728                                         /*@cc_on@*/\r
729                                         /*@if(@_win32)\r
730                                         but.unselectable = "on";\r
731                                         /*@end@*/\r
732                                         this.ths[y].appendChild(but);\r
733                                 };\r
734                         };\r
735                 \r
736                         but = null; \r
737                                         \r
738                         this.trs             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');\r
739                         this.tds             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('td');\r
740                         this.butPrevYear     = document.getElementById(this.id + "-prev-year-but");\r
741                         this.butPrevMonth    = document.getElementById(this.id + "-prev-month-but");\r
742                         this.butToday        = document.getElementById(this.id + "-today-but");\r
743                         this.butNextYear     = document.getElementById(this.id + "-next-year-but"); \r
744                         this.butNextMonth    = document.getElementById(this.id + "-next-month-but");\r
745         \r
746                         if(this.noToday) {\r
747                                 this.butToday.style.display = "none";        \r
748                         };\r
749                         \r
750                         if(this.showWeeks) {\r
751                                 this.wkThs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('th');\r
752                                 this.div.className += " weeks-displayed";\r
753                         };\r
754 \r
755                         tableBody = tableHead = tr = createThAndButton = createTH = null;\r
756 \r
757                         if(this.low && this.high && (this.high - this.low < 7)) { this.equaliseDates(); }; \r
758                         \r
759                         this.setDateFromInput();                                       \r
760                         this.updateTableHeaders();\r
761                         this.created = true;                         \r
762                         this.callback("create", {id:this.id});                        \r
763                         this.updateTable();                         \r
764                         \r
765                         if(this.staticPos) {                                 \r
766                                 this.visible = true;\r
767                                 this.show();\r
768                                 this.div.style.visibility = "visible";\r
769                                 this.opacity = this.opacityTo;\r
770                         } else {                                     \r
771                                 this.reposition();\r
772                                 this.div.style.visibility = "visible";\r
773                                 this.fade();\r
774                         };                                               \r
775                 };\r
776                 this.setFirstDayOfWeek = function(e) {\r
777                         e = e || document.parentWindow.event;\r
778                         var elem = e.target != null ? e.target : e.srcElement;\r
779                         while(elem.tagName.toLowerCase() != "th") elem = elem.parentNode;                         \r
780                         var cnt = o.showWeeks ? -1 : 0;\r
781                         while(elem.previousSibling) {\r
782                                 elem = elem.previousSibling;\r
783                                 if(elem.tagName.toLowerCase() == "th") cnt++;\r
784                         };\r
785                         o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;\r
786                         o.updateTableHeaders();\r
787                         return stopEvent(e);\r
788                 };\r
789                 this.fade = function() {\r
790                         window.clearTimeout(o.fadeTimer);\r
791                         o.fadeTimer = null;   \r
792                         var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4)); \r
793                         o.setOpacity(diff);  \r
794                         if(Math.abs(o.opacityTo - diff) > 3 && !o.noFadeEffect) {\r
795                                 o.fadeTimer = window.setTimeout(o.fade, 50);\r
796                         } else {\r
797                                 o.setOpacity(o.opacityTo);\r
798                                 if(o.opacityTo == 0) {\r
799                                         o.div.style.display = "none";\r
800                                         o.visible = false;\r
801                                 } else {\r
802                                         o.visible = true;\r
803                                 };\r
804                         };\r
805                 };          \r
806                 this.events = {\r
807                         onblur:function(e) {                                    \r
808                                 o.removeKeyboardEvents();                                 \r
809                                 if(o.statusBar) { o.updateStatus(getTitleTranslation(9)); };\r
810                         },\r
811                         onfocus:function(e) {                                 \r
812                                 o.addKeyboardEvents();                                 \r
813                                 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };\r
814                         },\r
815                         onkeydown: function (e) {\r
816                                 o.stopTimer();\r
817                                 if(!o.visible) return false;\r
818 \r
819                                 if(e == null) e = document.parentWindow.event;\r
820                                 var kc = e.keyCode ? e.keyCode : e.charCode;\r
821                                 \r
822                                 if( kc == 13 ) {\r
823                                         // RETURN/ENTER: close & select the date\r
824                                         var td = document.getElementById(o.id + "-date-picker-hover");                                         \r
825                                         if(!td || td.className.search(/cd-([0-9]{8})/) == -1 || td.className.search(/no-selection|out-of-range|day-disabled/) != -1) return stopEvent(e);\r
826                                         o.dateSet = new Date(o.date);\r
827                                         o.returnFormattedDate();\r
828                                         o.hide();\r
829                                         return stopEvent(e);\r
830                                 } else if(kc == 27) {\r
831                                         // ESC: close, no date selection \r
832                                         o.hide();\r
833                                         return stopEvent(e);\r
834                                 } else if(kc == 32 || kc == 0) {\r
835                                         // SPACE: goto today's date \r
836                                         o.date = new Date();\r
837                                         o.updateTable();\r
838                                         return stopEvent(e);\r
839                                 };    \r
840                                  \r
841                                 // Internet Explorer fires the keydown event faster than the JavaScript engine can\r
842                                 // update the interface. The following attempts to fix this.\r
843                                 /*@cc_on\r
844                                 @if(@_win32)\r
845                                 if(new Date().getTime() - o.interval.getTime() < 50) return stopEvent(e);\r
846                                 o.interval = new Date();\r
847                                 @end\r
848                                 @*/\r
849                         \r
850                                 if ((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {\r
851                                         if (kc > 96) kc -= (96-48);\r
852                                         kc -= 49;\r
853                                         o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;\r
854                                         o.updateTableHeaders();\r
855                                         return stopEvent(e);\r
856                                 };\r
857 \r
858                                 if ( kc < 33 || kc > 40 ) return true;\r
859 \r
860                                 var d = new Date(o.date), tmp, cursorYYYYMM = o.date.getFullYear() + pad(o.date.getMonth()+1); \r
861 \r
862                                 // HOME: Set date to first day of current month\r
863                                 if(kc == 36) {\r
864                                         d.setDate(1); \r
865                                 // END: Set date to last day of current month                                 \r
866                                 } else if(kc == 35) {\r
867                                         d.setDate(daysInMonth(d.getMonth(),d.getFullYear())); \r
868                                 // PAGE UP & DOWN                                   \r
869                                 } else if ( kc == 33 || kc == 34) {\r
870                                         var add = (kc == 34) ? 1 : -1; \r
871                                         // CTRL + PAGE UP/DOWN: Moves to the same date in the previous/next year\r
872                                         if(e.ctrlKey) {                                                                                                               \r
873                                                 d.setFullYear(d.getFullYear() + add);\r
874                                         // PAGE UP/DOWN: Moves to the same date in the previous/next month                                            \r
875                                         } else {                                                     \r
876                                                 if(!((kc == 33 && o.currentYYYYMM > cursorYYYYMM) || (kc == 34 && o.currentYYYYMM < cursorYYYYMM))) {                                                    \r
877                                                         tmp = new Date(d);\r
878                                                         tmp.setDate(2);\r
879                                                         tmp.setMonth(d.getMonth() + add);                                         \r
880                                                         d.setDate(Math.min(d.getDate(), daysInMonth(tmp.getMonth(),tmp.getFullYear())));                                        \r
881                                                         d.setMonth(d.getMonth() + add);\r
882                                                 };      \r
883                                         };                                 \r
884                                 // LEFT ARROW                                    \r
885                                 } else if ( kc == 37 ) {                                         \r
886                                         d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1);                                       \r
887                                 // RIGHT ARROW\r
888                                 } else if ( kc == 39 || kc == 34) {                                         \r
889                                         d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1 ); \r
890                                 // UP ARROW                                        \r
891                                 } else if ( kc == 38 ) {                                          \r
892                                         d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7);  \r
893                                 // DOWN ARROW                                        \r
894                                 } else if ( kc == 40 ) {                                          \r
895                                         d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7);                                         \r
896                                 };\r
897 \r
898                                 if(o.outOfRange(d)) return stopEvent(e);\r
899                                 o.date = d;\r
900                         \r
901                                 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };\r
902                                 var t = String(o.date.getFullYear()) + pad(o.date.getMonth()+1) + pad(o.date.getDate())\r
903 \r
904                                 if(e.ctrlKey || (kc == 33 || kc == 34) || t < o.firstDateShown || t > o.lastDateShown) {                                         \r
905                                         o.updateTable(); \r
906                                         o.interval = new Date();                                        \r
907                                 } else {                                    \r
908                                         if(!o.noToday) { o.disableTodayButton(); }\r
909                                         o.removeHighlight();\r
910                                 \r
911                                         var dt = "cd-" + o.date.getFullYear() + pad(o.date.getMonth()+1) + pad(o.date.getDate());\r
912                                             \r
913                                         for(var i = 0, td; td = o.tds[i]; i++) {  \r
914                                                 td.className = td.className.replace(/date-picker-hover/g, "");                                               \r
915                                                 if(td.className.search(dt) == -1) continue;                                                 \r
916                                                 o.showHideButtons(o.date);\r
917                                                 td.id = o.id + "-date-picker-hover";\r
918                                                 td.className = td.className.replace(/date-picker-hover/g, "") + " date-picker-hover";\r
919                                                 break;\r
920                                         };\r
921                                 };\r
922 \r
923                                 return stopEvent(e);\r
924                         },\r
925                         gotoToday: function(e) {\r
926                                 o.date = new Date();\r
927                                 o.updateTable();\r
928                                 return stopEvent(e);\r
929                         },\r
930                         onmousedown: function(e) {\r
931                                 e = e || document.parentWindow.event;\r
932                                 var el = e.target != null ? e.target : e.srcElement;\r
933                                 while(el.parentNode) {\r
934                                         if(el.id && (el.id == "fd-" + o.id || el.id == "fd-but-" + o.id)) {\r
935                                                 return true;\r
936                                         };\r
937                                         try { el = el.parentNode; } catch(err) { break; };\r
938                                 };\r
939                                 o.stopTimer();\r
940                                 hideAll();\r
941                         },\r
942                         ontablemouseout:function(e) {\r
943                                 e = e || document.parentWindow.event;\r
944                                 var p = e.toElement || e.relatedTarget;\r
945                                 while (p && p != this) try { p = p.parentNode } catch(e) { p = this; };\r
946                                 if (p == this) return false;\r
947                                 if(o.currentTR) {\r
948                                         o.currentTR.className = o.currentTR.className.replace('dp-row-highlight', '');\r
949                                         o.currentTR = null;\r
950                                 };\r
951                                 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };\r
952                         },\r
953                         ontablemouseover: function(e) {\r
954                                 e = e || document.parentWindow.event;\r
955                                 var el = e.target != null ? e.target : e.srcElement;\r
956                                 while ( el.nodeType != 1 ) el = el.parentNode;\r
957 \r
958                                 if(!el || ! el.tagName) { return; };\r
959                                 var statusText = getTitleTranslation(9);\r
960                                 switch (el.tagName.toLowerCase()) {\r
961                                 case "td":    \r
962                                         if(el.className.search(/date-picker-unused|out-of-range/) != -1) {\r
963                                                 statusText = getTitleTranslation(9);\r
964                                         } else if(el.className.search(/cd-([0-9]{8})/) == -1) {\r
965                                                 break;\r
966                                         } else {                                                \r
967                                                 o.stopTimer();\r
968                                                 var cellDate = el.className.match(/cd-([0-9]{8})/)[1];                                                                              \r
969                                                 o.removeHighlight();\r
970                                                 el.id = o.id+"-date-picker-hover";\r
971                                                 el.className = el.className.replace(/date-picker-hover/g, "") + " date-picker-hover";                                                 \r
972                                                 o.date = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2));                                                \r
973                                                 if(!o.noToday) { o.disableTodayButton(); };\r
974                                                 statusText = printFormattedDate(o.date, o.statusFormat, true);\r
975 \r
976                                         };\r
977                                         break;\r
978                                 case "th":\r
979                                         if(!o.statusBar) { break; };\r
980                                         if(el.className.search(/drag-enabled/) != -1) {\r
981                                                 statusText = getTitleTranslation(10);\r
982                                         } else if(el.className.search(/date-picker-week-header/) != -1) {\r
983                                                 var txt = el.firstChild ? el.firstChild.nodeValue : "";\r
984                                                 statusText = txt.search(/^(\d+)$/) != -1 ? getTitleTranslation(7, [txt, txt < 3 && o.date.getMonth() == 11 ? getWeeksInYear(o.date.getFullYear()) + 1 : getWeeksInYear(o.date.getFullYear())]) : getTitleTranslation(9);\r
985                                         };\r
986                                         break;\r
987                                 case "span":\r
988                                         if(!o.statusBar) { break; };\r
989                                         if(el.className.search(/drag-enabled/) != -1) {\r
990                                                 statusText = getTitleTranslation(10);\r
991                                         } else if(el.className.search(/day-([0-6])/) != -1) {\r
992                                                 var day = el.className.match(/day-([0-6])/)[1];\r
993                                                 statusText = getTitleTranslation(11, [getDayTranslation(day, false)]);\r
994                                         } else if(el.className.search(/prev-year/) != -1) {\r
995                                                 statusText = getTitleTranslation(2);\r
996                                         } else if(el.className.search(/prev-month/) != -1) {\r
997                                                 statusText = getTitleTranslation(0);\r
998                                         } else if(el.className.search(/next-year/) != -1) {\r
999                                                 statusText = getTitleTranslation(3);\r
1000                                         } else if(el.className.search(/next-month/) != -1) {\r
1001                                                 statusText = getTitleTranslation(1);\r
1002                                         } else if(el.className.search(/today-but/) != -1 && el.className.search(/disabled/) == -1) {\r
1003                                                 statusText = getTitleTranslation(12);\r
1004                                         };\r
1005                                         break;\r
1006                                 default:\r
1007                                         statusText = "";\r
1008                                 };\r
1009                                 while(el.parentNode) {\r
1010                                         el = el.parentNode;\r
1011                                         if(el.nodeType == 1 && el.tagName.toLowerCase() == "tr") {\r
1012                                                 if(el == o.currentTR) break;\r
1013                                                 if(o.currentTR) {\r
1014                                                         o.currentTR.className = o.currentTR.className.replace('dp-row-highlight', '');\r
1015                                                 };\r
1016                                                 el.className = el.className + " dp-row-highlight";\r
1017                                                 o.currentTR = el;\r
1018                                                 break;\r
1019                                         };\r
1020                                 };                                                          \r
1021                                 if(o.statusBar && statusText) { o.updateStatus(statusText); };\r
1022                         },\r
1023                         onclick: function(e) {                                                  \r
1024                                 if(o.opacity != o.opacityTo || this.className.search(/date-picker-unused|out-of-range|day-disabled|no-selection/) != -1) return false;\r
1025                                 e = e || document.parentWindow.event;\r
1026                                 var el = e.target != null ? e.target : e.srcElement;\r
1027                                 while (el.nodeType != 1 || (el.tagName && el.tagName != "TD")) el = el.parentNode;\r
1028                                 var cellDate = el.className.match(/cd-([0-9]{8})/)[1];                                                                                                                                                                           \r
1029                                 o.date = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2));\r
1030                                 o.dateSet = new Date(o.date);                                 \r
1031                                 o.returnFormattedDate();\r
1032                                 if(!o.staticPos) { o.hide(); }\r
1033                                 else { o.updateTable();};\r
1034                                 o.stopTimer();\r
1035                                 return stopEvent(e);\r
1036                         },\r
1037                         incDec: function(e) {                            \r
1038                                 e = e || document.parentWindow.event;\r
1039                                 var el = e.target != null ? e.target : e.srcElement;\r
1040                                 if(el && el.className && el.className.search('fd-disabled') != -1) { return false; }                                \r
1041                                 o.timerInc      = 800;\r
1042                                 o.dayInc        = arguments[1];\r
1043                                 o.yearInc       = arguments[2];\r
1044                                 o.monthInc      = arguments[3];                         \r
1045                                 o.timerSet      = true;                                                \r
1046                        \r
1047                                 o.updateTable();\r
1048                                 return true;\r
1049                         },\r
1050                         clearTimer: function(e) {\r
1051                                 o.stopTimer();\r
1052                                 o.timerInc      = 800;\r
1053                                 o.yearInc       = 0;\r
1054                                 o.monthInc      = 0;\r
1055                                 o.dayInc        = 0;\r
1056                                 removeEvent(document, "mouseup", o.events.clearTimer);\r
1057                         }\r
1058                 };\r
1059         \r
1060                 this.setFormElementEvents = function() {\r
1061                         var elem = this.getElem();\r
1062                         if(elem && elem.tagName.search(/select|input/i) != -1) {                                         \r
1063                                 addEvent(elem, "change", o.changeHandler);\r
1064                                 if(this.splitDate) {                                                                         \r
1065                                         addEvent(document.getElementById(this.id + splitAppend[1]), "change", o.changeHandler);\r
1066                                         addEvent(document.getElementById(this.id + splitAppend[0]), "change", o.changeHandler);\r
1067                                 };\r
1068                         };\r
1069                 };                 \r
1070                 \r
1071                 var o = this;\r
1072                 \r
1073                 o.setFormElementEvents();\r
1074                 \r
1075                 if(this.staticPos) { this.create(); this.setDateFromInput(); }\r
1076                 else { \r
1077                         this.createButton();\r
1078                         this.setDateFromInput();                         \r
1079                 };\r
1080         };\r
1081         datePicker.prototype.createButton = function() {\r
1082                 \r
1083                 if(this.staticPos || document.getElementById("fd-but-" + this.id)) { return; };\r
1084 \r
1085                 var inp         = this.getElem(),\r
1086                     span        = document.createElement('span'),\r
1087                     but         = document.createElement('a');\r
1088 \r
1089                 but.href        = "#";\r
1090                 but.className   = "date-picker-control";\r
1091                 but.title       = getTitleTranslation(5);\r
1092                 but.id          = "fd-but-" + this.id;\r
1093 \r
1094                 span.appendChild(document.createTextNode(nbsp));\r
1095                 but.appendChild(span);\r
1096 \r
1097                 if(this.buttonWrapper && document.getElementById(this.buttonWrapper)) {\r
1098                         document.getElementById(this.buttonWrapper).appendChild(but);\r
1099                 } else if(inp.nextSibling) {\r
1100                         inp.parentNode.insertBefore(but, inp.nextSibling);\r
1101                 } else {\r
1102                         inp.parentNode.appendChild(but);\r
1103                 };                   \r
1104 \r
1105                 but.onclick = but.onpress = function(e) {\r
1106                         e = e || window.event;                      \r
1107                 \r
1108                         var inpId     = this.id.replace('fd-but-',''),\r
1109                             dpVisible = isVisible(inpId);  \r
1110                 \r
1111                         if(e.type == "press") {\r
1112                                 var kc = e.keyCode != null ? e.keyCode : e.charCode;\r
1113                                 if(kc != 13) return true; \r
1114                                 if(dpVisible) {\r
1115                                         this.className = this.className.replace("dp-button-active", "");\r
1116                                         datePickerController.hideAll();\r
1117                                         return false;\r
1118                                 };\r
1119                         };\r
1120 \r
1121                         this.className = this.className.replace("dp-button-active", "");\r
1122                         \r
1123                         if(!dpVisible) {\r
1124                                 this.className += " dp-button-active";\r
1125                                 hideAll(inpId);\r
1126                                 showDatePicker(inpId);\r
1127                         } else {\r
1128                                 hideAll();\r
1129                         };\r
1130                 \r
1131                         return false;\r
1132                 };\r
1133         \r
1134                 but = null;\r
1135         };  \r
1136         datePicker.prototype.setRangeLow = function(range) {\r
1137                 this.low = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;                \r
1138                 if(this.created) { this.updateTable(); };\r
1139         };\r
1140         datePicker.prototype.setRangeHigh = function(range) {\r
1141                 this.high = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;                \r
1142                 if(this.created) { this.updateTable(); };\r
1143         };\r
1144         datePicker.prototype.setDisabledDays = function(dayArray) {\r
1145                 this.disabledDays = dayArray;\r
1146                 if(this.created) { this.updateTable(); };\r
1147         };\r
1148         datePicker.prototype.setDisabledDates = function(dateArray) {                \r
1149                 this.disabledDates = {};\r
1150                 this.addDisabledDates(dateArray);                \r
1151         }; \r
1152         datePicker.prototype.addDisabledDates = function(dateArray) {\r
1153                 var disabledDateObj = {};\r
1154                 if(typeof dateArray !== "object") dateArray = [dateArray];                \r
1155                 for(var i = dateArray.length; i-- ;) {\r
1156                         if(dateArray[i].match(/^(\d\d\d\d|\*\*\*\*)(0[1-9]|1[012]|\*\*)(0[1-9]|[12][0-9]|3[01])$/) != -1) {\r
1157                                 this.disabledDates[dateArray[i]] = 1;\r
1158                         };\r
1159                 }; \r
1160                 if(this.created) { this.updateTable(); };                                  \r
1161         };\r
1162         datePicker.prototype.addKeyboardEvents = function() {\r
1163                 addEvent(document, "keypress", this.events.onkeydown);\r
1164                 /*@cc_on\r
1165                 @if(@_win32)\r
1166                 removeEvent(document, "keypress", this.events.onkeydown);\r
1167                 addEvent(document, "keydown", this.events.onkeydown);\r
1168                 @end\r
1169                 @*/\r
1170                 if(window.devicePixelRatio) {\r
1171                         removeEvent(document, "keypress", this.events.onkeydown);\r
1172                         addEvent(document, "keydown", this.events.onkeydown);\r
1173                 };\r
1174         };  \r
1175         datePicker.prototype.removeKeyboardEvents = function() {\r
1176                 removeEvent(document, "keypress", this.events.onkeydown);\r
1177                 removeEvent(document, "keydown",  this.events.onkeydown);\r
1178         };\r
1179         datePicker.prototype.removeHighlight = function() {\r
1180                 var el = document.getElementById(this.id+"-date-picker-hover");\r
1181                 if(el) {\r
1182                         el.className = el.className.replace("date-picker-hover", "");\r
1183                         el.id = "";                                                                \r
1184                 };\r
1185         };\r
1186         datePicker.prototype.stopTimer = function() {\r
1187                 this.timerSet = false;\r
1188                 window.clearTimeout(this.timer);\r
1189         };\r
1190         datePicker.prototype.setOpacity = function(op) {\r
1191                 this.div.style.opacity = op/100;\r
1192                 this.div.style.filter = 'alpha(opacity=' + op + ')';\r
1193                 this.opacity = op;\r
1194         };         \r
1195         datePicker.prototype.getElem = function() {\r
1196                 return document.getElementById(this.id.replace(/^fd-/, '')) || false;\r
1197         };\r
1198         datePicker.prototype.getDisabledDates = function(y, m) {\r
1199                 m = pad(m);                 \r
1200                 \r
1201                 var obj = {},            \r
1202                     lower  = this.firstDateShown,\r
1203                     upper  = this.lastDateShown,             \r
1204                     dt1, dt2, rngLower, rngUpper;  \r
1205                 \r
1206                 if(!upper || !lower) {\r
1207                         lower = this.firstDateShown = y + pad(m) + "01";\r
1208                         upper = this.lastDateShown  = y + pad(m) + pad(daysInMonth(m, y));                        \r
1209                 };\r
1210                 \r
1211                 for(var dt in this.disabledDates) {                            \r
1212                         dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);\r
1213                         dt2 = this.disabledDates[dt];\r
1214 \r
1215                         if(dt2 == 1) {                                 \r
1216                                 if(+lower <= +dt1 && +upper >= +dt1) {\r
1217                                         obj[dt1] = 1;                                         \r
1218                                 };\r
1219                                 continue;\r
1220                         };\r
1221 \r
1222                         // Range of disabled dates                        \r
1223                         if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {\r
1224                                 // Same month\r
1225                                 if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {\r
1226                                         for(var i = dt1; i <= dt2; i++) {\r
1227                                                 obj[i] = 1;\r
1228                                         };\r
1229                                         continue;\r
1230                                 };\r
1231 \r
1232                                 // Different months but we only want this month\r
1233                                 rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;\r
1234                                 rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;\r
1235                                 for(var i = +rngLower; i <= +rngUpper; i++) {\r
1236                                         obj[i] = 1;                                        \r
1237                                 };\r
1238                         };\r
1239                 };\r
1240                 \r
1241                 for(dt in this.enabledDates) {\r
1242                         dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);\r
1243                         dt2 = this.enabledDates[dt];\r
1244 \r
1245                         if(dt2 == 1) {\r
1246                                 if(dt1 in obj) {                                          \r
1247                                         obj[dt1] = null;\r
1248                                         delete obj[dt1];\r
1249                                 };\r
1250                                 continue;\r
1251                         };\r
1252 \r
1253                         // Range\r
1254                         if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {\r
1255                                 // Same month\r
1256                                 if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {\r
1257                                         for(var i = dt1; i <= dt2; i++) {\r
1258                                                 if(i in obj) {\r
1259                                                         obj[i] = null;\r
1260                                                         delete obj[i];\r
1261                                                 };\r
1262                                         };\r
1263                                         continue;\r
1264                                 };\r
1265 \r
1266                                 // Different months but we only want this month\r
1267                                 rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;\r
1268                                 rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;\r
1269                                 for(var i = +rngLower; i <= +rngUpper; i++) {\r
1270                                         if(i in obj) {\r
1271                                                 obj[i] = null;\r
1272                                                 delete obj[i];\r
1273                                         };\r
1274                                 };\r
1275                         };\r
1276                 };\r
1277                 return obj;\r
1278         };\r
1279         datePicker.prototype.truePosition = function(element) {\r
1280                 var pos = this.cumulativeOffset(element);\r
1281                 if(window.opera) { return pos; };\r
1282                 var iebody      = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body,\r
1283                     dsocleft    = document.all ? iebody.scrollLeft : window.pageXOffset,\r
1284                     dsoctop     = document.all ? iebody.scrollTop  : window.pageYOffset,\r
1285                     posReal     = this.realOffset(element);\r
1286                 return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];\r
1287         };\r
1288         datePicker.prototype.realOffset = function(element) {\r
1289                 var t = 0, l = 0;\r
1290                 do {\r
1291                         t += element.scrollTop  || 0;\r
1292                         l += element.scrollLeft || 0;\r
1293                         element = element.parentNode;\r
1294                 } while(element);\r
1295                 return [l, t];\r
1296         };\r
1297         datePicker.prototype.cumulativeOffset = function(element) {\r
1298                 var t = 0, l = 0;\r
1299                 do {\r
1300                         t += element.offsetTop  || 0;\r
1301                         l += element.offsetLeft || 0;\r
1302                         element = element.offsetParent;\r
1303                 } while(element);\r
1304                 return [l, t];\r
1305         };\r
1306         datePicker.prototype.equaliseDates = function() {\r
1307                 var clearDayFound = false, tmpDate;\r
1308                 for(var i = this.low; i <= this.high; i++) {\r
1309                         tmpDate = String(i);\r
1310                         if(!this.disabledDays[new Date(tmpDate.substr(0,4), tmpDate.substr(6,2), tmpDate.substr(4,2)).getDay() - 1]) {\r
1311                                 clearDayFound = true;\r
1312                                 break;\r
1313                         };\r
1314                 };\r
1315                 if(!clearDayFound) { this.disabledDays = [0,0,0,0,0,0,0] };\r
1316         };\r
1317         datePicker.prototype.outOfRange = function(tmpDate) {\r
1318                 if(!this.low && !this.high) { return false; };\r
1319 \r
1320                 var level = false;\r
1321                 if(!tmpDate) {\r
1322                         level   = true;\r
1323                         tmpDate = this.date;\r
1324                 };\r
1325 \r
1326                 var d  = pad(tmpDate.getDate()),\r
1327                     m  = pad(tmpDate.getMonth() + 1),\r
1328                     y  = tmpDate.getFullYear(),\r
1329                     dt = String(y)+String(m)+String(d);\r
1330 \r
1331                 if(this.low && +dt < +this.low) {\r
1332                         if(!level) return true;\r
1333                         this.date = new Date(this.low.substr(0,4), this.low.substr(4,2)-1, this.low.substr(6,2), 5, 0, 0);\r
1334                         return false;\r
1335                 };\r
1336                 if(this.high && +dt > +this.high) {\r
1337                         if(!level) return true;\r
1338                         this.date = new Date(this.high.substr(0,4), this.high.substr(4,2)-1, this.high.substr(6,2), 5, 0, 0);\r
1339                 };\r
1340                 return false;\r
1341         };         \r
1342         datePicker.prototype.updateStatus = function(msg) {\r
1343                 while(this.statusBar.firstChild) { this.statusBar.removeChild(this.statusBar.firstChild); };\r
1344                 if(msg && this.statusFormat.search(/-S|S-/) != -1) {\r
1345                         msg = msg.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1<sup>$2</sup>");\r
1346                         msg = msg.split(/<sup>|<\/sup>/);\r
1347                         var dc = document.createDocumentFragment();\r
1348                         for(var i = 0, nd; nd = msg[i]; i++) {\r
1349                                 if(/^(st|nd|rd|th)$/.test(nd)) {\r
1350                                         var sup = document.createElement("sup");\r
1351                                         sup.appendChild(document.createTextNode(nd));\r
1352                                         dc.appendChild(sup);\r
1353                                 } else {\r
1354                                         dc.appendChild(document.createTextNode(nd));\r
1355                                 };\r
1356                         };\r
1357                         this.statusBar.appendChild(dc);\r
1358                 } else {\r
1359                         this.statusBar.appendChild(document.createTextNode(msg ? msg : getTitleTranslation(9)));\r
1360                 };\r
1361         };\r
1362         datePicker.prototype.setDateFromInput = function() {\r
1363                 this.dateSet = null;\r
1364 \r
1365                 var elem = this.getElem(), \r
1366                     upd  = false, \r
1367                     dt;\r
1368                     \r
1369                 if(!elem || elem.tagName.search(/select|input/i) == -1) return; \r
1370 \r
1371                 if(!this.splitDate && elem.value.replace(/\s/g, "") !== "") {\r
1372                         var dynFormatMasks = formatMasks.concat([this.format]).reverse();                                                \r
1373                         for(var i = 0, fmt; fmt = dynFormatMasks[i]; i++) {\r
1374                                 dt = parseDateString(elem.value, fmt);                                                              \r
1375                                 if(dt) {                                    \r
1376                                         upd = true;                                       \r
1377                                         break;\r
1378                                 };\r
1379                         };                                                                        \r
1380                 } else if(this.splitDate) {\r
1381                         var mmN  = document.getElementById(this.id + splitAppend[1]),\r
1382                             ddN  = document.getElementById(this.id + splitAppend[0]),\r
1383                             tm   = parseInt(mmN.tagName.toLowerCase() == "input"  ? mmN.value  : mmN.options[mmN.selectedIndex || 0].value, 10),\r
1384                             td   = parseInt(ddN.tagName.toLowerCase() == "input"  ? ddN.value  : ddN.options[ddN.selectedIndex || 0].value, 10),\r
1385                             ty   = parseInt(elem.tagName.toLowerCase() == "input" ? elem.value : elem.options[elem.selectedIndex || 0].value, 10);\r
1386                                              \r
1387                         if(!(/\d\d\d\d/.test(ty)) || !(/^(0?[1-9]|1[012])$/.test(tm)) || !(/^(0?[1-9]|[12][0-9]|3[01])$/.test(td))) {\r
1388                                 dt = false;\r
1389                         } else {\r
1390                                 if(+td > daysInMonth(+tm - 1, +ty)) {                                         \r
1391                                         upd = true;\r
1392                                         td  = daysInMonth(+tm - 1, +ty); \r
1393                                         dt  = new Date(ty,tm-1,td);\r
1394                                 } else {\r
1395                                         dt = new Date(ty,tm-1,td);\r
1396                                 };\r
1397                         };                        \r
1398                 };\r
1399 \r
1400                 if(!dt || isNaN(dt)) {                                                              \r
1401                         this.date = new Date();\r
1402                         this.date.setHours(5);\r
1403                         this.outOfRange();\r
1404                         return;\r
1405                 };\r
1406 \r
1407                 dt.setHours(5);\r
1408                 this.date = new Date(dt);                            \r
1409                 this.outOfRange();                 \r
1410                 \r
1411                 var dtYYYYMMDD = dt.getFullYear() + pad(dt.getMonth() + 1) + pad(dt.getDate()),\r
1412                     weekDay    = ( dt.getDay() + 6 ) % 7;\r
1413                 \r
1414                 if(dt.getTime() == this.date.getTime() && !(dtYYYYMMDD in this.dynDisabledDates || this.disabledDays[weekDay])) {                        \r
1415                         this.dateSet = new Date(this.date);\r
1416                 };\r
1417                 \r
1418                 if(upd) { this.returnFormattedDate(); };\r
1419         };\r
1420         datePicker.prototype.setSelectIndex = function(elem, indx) {\r
1421                 for(var opt = elem.options.length-1; opt >= 0; opt--) {\r
1422                         if(elem.options[opt].value == +indx) {\r
1423                                 elem.selectedIndex = opt;\r
1424                                 return;\r
1425                         };\r
1426                 };\r
1427         };\r
1428         datePicker.prototype.returnFormattedDate = function() {\r
1429                 \r
1430                 var elem = this.getElem();\r
1431                 if(!elem) return;\r
1432 \r
1433                 var d                   = pad(this.date.getDate()),\r
1434                     m                   = pad(this.date.getMonth() + 1),\r
1435                     yyyy                = this.date.getFullYear(),\r
1436                     disabledDates       = this.getDisabledDates(+yyyy, +m),\r
1437                     weekDay             = (this.date.getDay() + 6) % 7;\r
1438                 \r
1439                 if(!(this.disabledDays[weekDay] || String(yyyy)+m+d in this.disabledDates)) {\r
1440                         if(this.splitDate) {\r
1441                                 var ddE = document.getElementById(this.id+splitAppend[0]),\r
1442                                     mmE = document.getElementById(this.id+splitAppend[1]);\r
1443                                     \r
1444                                 if(ddE.tagName.toLowerCase() == "input") { ddE.value = d; }\r
1445                                 else { this.setSelectIndex(ddE, d); };\r
1446                                 if(mmE.tagName.toLowerCase() == "input") { mmE.value = m; }\r
1447                                 else { this.setSelectIndex(mmE, m); };\r
1448                                 if(elem.tagName.toLowerCase() == "input") elem.value = yyyy;\r
1449                                 else { this.setSelectIndex(elem, yyyy); };\r
1450                                 \r
1451                         } else if(elem.tagName.toLowerCase() == "input") {                                                                \r
1452                                 elem.value = printFormattedDate(this.date, this.format);                                \r
1453                         };\r
1454                         \r
1455                         if(elem.type && elem.type != "hidden") { elem.focus(); }                         \r
1456                                                                   \r
1457                         this.callback("dateselect", { "id":this.id, "date":this.dateSet, "dd":d, "mm":m, "yyyy":yyyy });                        \r
1458                         \r
1459                         if(this.staticPos) { this.updateTable(); };                                 \r
1460                 };                        \r
1461         };\r
1462         datePicker.prototype.disableTodayButton = function() {\r
1463                 var today = new Date();                     \r
1464                 this.butToday.className = this.butToday.className.replace("fd-disabled", "");\r
1465                 if(this.outOfRange(today) || (this.date.getDate() == today.getDate() && this.date.getMonth() == today.getMonth() && this.date.getFullYear() == today.getFullYear())) {\r
1466                         this.butToday.className += " fd-disabled";\r
1467                         this.butToday.onclick = null;\r
1468                 } else {\r
1469                         this.butToday.onclick = this.events.gotoToday;\r
1470                 };\r
1471         };\r
1472         datePicker.prototype.updateTableHeaders = function() {\r
1473                 var colspanTotal = this.showWeeks ? 8 : 7,\r
1474                     colOffset    = this.showWeeks ? 1 : 0,\r
1475                     d, but;\r
1476 \r
1477                 for(var col = colOffset; col < colspanTotal; col++ ) {\r
1478                         d = (this.firstDayOfWeek + (col - colOffset)) % 7;\r
1479                         this.ths[col].title = getDayTranslation(d, false);\r
1480 \r
1481                         if(col > colOffset) {\r
1482                                 but = this.ths[col].getElementsByTagName("span")[0];\r
1483                                 while(but.firstChild) { but.removeChild(but.firstChild); };\r
1484                                 but.appendChild(document.createTextNode(getDayTranslation(d, true)));\r
1485                                 but.title = this.ths[col].title;\r
1486                                 but.className = but.className.replace(/day-([0-6])/, "") + " day-" + d;\r
1487                                 but = null;\r
1488                         } else {\r
1489                                 while(this.ths[col].firstChild) { this.ths[col].removeChild(this.ths[col].firstChild); };\r
1490                                 this.ths[col].appendChild(document.createTextNode(getDayTranslation(d, true)));\r
1491                         };\r
1492 \r
1493                         this.ths[col].className = this.ths[col].className.replace(/date-picker-highlight/g, "");\r
1494                         if(this.highlightDays[d]) {\r
1495                                 this.ths[col].className += " date-picker-highlight";\r
1496                         };\r
1497                 };\r
1498                 \r
1499                 if(this.created) { this.updateTable(); }\r
1500         };\r
1501 \r
1502         datePicker.prototype.callback = function(type, args) {                                                     \r
1503                 if(!type || !(type in this.callbacks)) return false;\r
1504                 \r
1505                 var ret = false;                   \r
1506                 for(var func = 0; func < this.callbacks[type].length; func++) {                         \r
1507                         ret = this.callbacks[type][func](args || this.id);\r
1508                         if(!ret) return false;\r
1509                 };                      \r
1510                 return ret;\r
1511         };\r
1512         \r
1513         datePicker.prototype.showHideButtons = function(tmpDate) {\r
1514                 var tdm = tmpDate.getMonth(),\r
1515                     tdy = tmpDate.getFullYear();\r
1516 \r
1517                 this.butPrevYear.className = this.butPrevYear.className.replace("fd-disabled", "");\r
1518                 if(this.outOfRange(new Date((tdy - 1), tdm, daysInMonth(+tdm, tdy-1)))) {\r
1519                         this.butPrevYear.className += " fd-disabled";\r
1520                         if(this.yearInc == -1) this.stopTimer();\r
1521                 };    \r
1522                 \r
1523                 this.butPrevMonth.className = this.butPrevMonth.className.replace("fd-disabled", "");\r
1524                 if(this.outOfRange(new Date(tdy, (+tdm - 1), daysInMonth(+tdm-1, tdy)))) {\r
1525                         this.butPrevMonth.className += " fd-disabled";\r
1526                         if(this.monthInc == -1) this.stopTimer();\r
1527                 };\r
1528          \r
1529                 this.butNextYear.className = this.butNextYear.className.replace("fd-disabled", "");\r
1530                 if(this.outOfRange(new Date((tdy + 1), +tdm, 1))) {\r
1531                         this.butNextYear.className += " fd-disabled";\r
1532                         if(this.yearInc == 1) this.stopTimer();\r
1533                 };\r
1534                 \r
1535                 this.butNextMonth.className = this.butNextMonth.className.replace("fd-disabled", "");\r
1536                 if(this.outOfRange(new Date(tdy, +tdm + 1, 1))) {\r
1537                         this.butNextMonth.className += " fd-disabled";\r
1538                         if(this.monthInc == 1) this.stopTimer();\r
1539                 };\r
1540         };        \r
1541         var localeDefaults = {\r
1542                 fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"],\r
1543                 monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],\r
1544                 fullDays:  ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],\r
1545                 dayAbbrs:  ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],\r
1546                 titles:    ["Previous month","Next month","Previous year","Next year", "Today", "Show Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date"],\r
1547                 imported:  false\r
1548         };        \r
1549         var grepRangeLimits = function(sel) {\r
1550                 var range = [];\r
1551                 for(var i = 0; i < sel.options.length; i++) {\r
1552                         if(sel.options[i].value.search(/^\d\d\d\d$/) == -1) { continue; };\r
1553                         if(!range[0] || Number(sel.options[i].value) < range[0]) { range[0] = Number(sel.options[i].value); };\r
1554                         if(!range[1] || Number(sel.options[i].value) > range[1]) { range[1] = Number(sel.options[i].value); };\r
1555                 };\r
1556                 return range;\r
1557         };\r
1558         var joinNodeLists = function() {\r
1559                 if(!arguments.length) { return []; }\r
1560                 var nodeList = [];\r
1561                 for (var i = 0; i < arguments.length; i++) {\r
1562                         for (var j = 0, item; item = arguments[i][j]; j++) {\r
1563                                 nodeList[nodeList.length] = item;\r
1564                         };\r
1565                 };\r
1566                 return nodeList;\r
1567         };\r
1568         var cleanUp = function() {\r
1569                 var dp;\r
1570                 for(dp in datePickers) {\r
1571                         if(!document.getElementById(datePickers[dp].id)) {\r
1572                                 if(datePickers[dp].created) datePickers[dp].destroy();\r
1573                                 datePickers[dp] = null;\r
1574                                 delete datePickers[dp];\r
1575                         };\r
1576                 };\r
1577         };         \r
1578         var hideAll = function(exception) {\r
1579                 var dp;\r
1580                 for(dp in datePickers) {\r
1581                         if(!datePickers[dp].created || datePickers[dp].staticPos || (exception && exception == datePickers[dp].id)) continue;\r
1582                         datePickers[dp].hide();\r
1583                 };\r
1584         };\r
1585         var showDatePicker = function(inpID) {\r
1586                 if(!(inpID in datePickers)) return false;                 \r
1587                 datePickers[inpID].show();\r
1588                 return true;        \r
1589         };\r
1590         var destroy = function() {\r
1591                 for(dp in datePickers) {\r
1592                         if(datePickers[dp].created) datePickers[dp].destroy();\r
1593                         datePickers[dp] = null;\r
1594                         delete datePickers[dp];\r
1595                 };\r
1596                 datePickers = null;\r
1597                 removeEvent(window, 'load',   datePickerController.create);\r
1598                 removeEvent(window, 'unload', datePickerController.destroy);\r
1599         }; \r
1600         var getTitleTranslation = function(num, replacements) {\r
1601                 replacements = replacements || [];\r
1602                 if(localeImport.titles.length > num) {\r
1603                          var txt = localeImport.titles[num];\r
1604                          if(replacements && replacements.length) {\r
1605                                 for(var i = 0; i < replacements.length; i++) {\r
1606                                         txt = txt.replace("[[%" + i + "%]]", replacements[i]);\r
1607                                 };\r
1608                          };\r
1609                          return txt.replace(/[[%(\d)%]]/g,"");\r
1610                 };\r
1611                 return "";\r
1612         };\r
1613         var getDayTranslation = function(day, abbreviation) {\r
1614                 var titles = localeImport[abbreviation ? "dayAbbrs" : "fullDays"];\r
1615                 return titles.length && titles.length > day ? titles[day] : "";\r
1616         };\r
1617         var getMonthTranslation = function(month, abbreviation) {\r
1618                 var titles = localeImport[abbreviation ? "monthAbbrs" : "fullMonths"];\r
1619                 return titles.length && titles.length > month ? titles[month] : "";\r
1620         };\r
1621         var daysInMonth = function(nMonth, nYear) {\r
1622                 nMonth = (nMonth + 12) % 12;\r
1623                 return (((0 == (nYear%4)) && ((0 != (nYear%100)) || (0 == (nYear%400)))) && nMonth == 1) ? 29: [31,28,31,30,31,30,31,31,30,31,30,31][nMonth];\r
1624         };\r
1625         var getWeeksInYear = function(Y) {\r
1626                 if(Y in weeksInYearCache) {\r
1627                         return weeksInYearCache[Y];\r
1628                 };\r
1629                 var X1, X2, NW;\r
1630                 with (X1 = new Date(Y, 0, 4)) {\r
1631                         setDate(getDate() - (6 + getDay()) % 7);\r
1632                 };\r
1633                 with (X2 = new Date(Y, 11, 28)) {\r
1634                         setDate(getDate() + (7 - getDay()) % 7);\r
1635                 };\r
1636                 weeksInYearCache[Y] = Math.round((X2 - X1) / 604800000);\r
1637                 return weeksInYearCache[Y];\r
1638         };\r
1639         var parseRangeFromString = function(str) {\r
1640                 if(!str) return "";\r
1641                 \r
1642                 var low = str.search(/^range-low-/) != -1;\r
1643                 str = str.replace(/range-(low|high)-/, "");\r
1644 \r
1645                 if(str.search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) != -1) { return str; };\r
1646 \r
1647                 var tmpDate = new Date();\r
1648                 \r
1649                 if(str.search(/^today$/) != -1) { return tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate()); };\r
1650                 \r
1651                 var regExp = /^(\d)-(day|week|month|year)$/;\r
1652                 \r
1653                 if(str.search(regExp) != -1) {\r
1654                         var parts       = str.match(regExp),\r
1655                             acc         = { day:0,week:0,month:0,year:0 };\r
1656                             \r
1657                         acc[parts[2]]   = low ? -(+parts[1]) : +parts[1];\r
1658                         tmpDate.setFullYear(tmpDate.getFullYear() + +acc.year);\r
1659                         tmpDate.setMonth(tmpDate.getMonth() + +acc.month);\r
1660                         tmpDate.setDate(tmpDate.getDate() + +acc.day + (7 * +acc.week));\r
1661                         return !tmpDate || isNaN(tmpDate) ? "" : tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate());\r
1662                 };\r
1663                 \r
1664                 return "";\r
1665         };\r
1666         var getWeekNumber = function(y,m,d) {\r
1667                 var d = new Date(y, m, d, 0, 0, 0);\r
1668                 var DoW = d.getDay();\r
1669                 d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu\r
1670                 var ms = d.valueOf(); // GMT\r
1671                 d.setMonth(0);\r
1672                 d.setDate(4); // Thu in Week 1\r
1673                 return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;\r
1674         };\r
1675         var printFormattedDate = function(date, fmt, useImportedLocale) {\r
1676                 if(!date || isNaN(date)) { return ""; };\r
1677                 \r
1678                 var parts = fmt.split("-"),\r
1679                       str = [],\r
1680                         d = date.getDate(),\r
1681                         D = date.getDay(),\r
1682                         m = date.getMonth(),\r
1683                         y = date.getFullYear(),\r
1684                     flags = {\r
1685                                 "sp":" ",\r
1686                                 "dt":".",\r
1687                                 "sl":"/",\r
1688                                 "ds":"-",\r
1689                                 "cc":",",\r
1690                                 "d":pad(d),\r
1691                                 "D":useImportedLocale ? localeImport.dayAbbrs[D == 0 ? 6 : D - 1] : localeDefaults.dayAbbrs[D == 0 ? 6 : D - 1],\r
1692                                 "l":useImportedLocale ? localeImport.fullDays[D == 0 ? 6 : D - 1] : localeDefaults.fullDays[D == 0 ? 6 : D - 1],\r
1693                                 "j":d,\r
1694                                 "N":D == 0 ? 7 : D,\r
1695                                 "w":D,\r
1696                                 /*"S":String(d).substr(-(Math.min(String(d).length, 2))) > 3 && String(d).substr(-(Math.min(String(d).length, 2))) < 21 ? "th" : ["th", "st", "nd", "rd", "th"][Math.min(+d%10, 4)],*/\r
1697                                 "z":"?",\r
1698                                 "W":getWeekNumber(date),\r
1699                                 "M":useImportedLocale ? localeImport.monthAbbrs[m] : localeDefaults.monthAbbrs[m],\r
1700                                 "F":useImportedLocale ? localeImport.fullMonths[m] : localeDefaults.fullMonths[m],\r
1701                                 "m":pad(++m),\r
1702                                 "n":++m,\r
1703                                 "t":daysInMonth(++m, y),\r
1704                                 "Y":y,\r
1705                                 "o":y,\r
1706                                 "y":String(y).substr(2,2),\r
1707                                 "S":["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]\r
1708                             };\r
1709 \r
1710                 for(var pt = 0, part; part = parts[pt]; pt++) {                        \r
1711                         str.push(!(part in flags) ? "" : flags[part]);\r
1712                 };\r
1713 \r
1714                 return str.join("");\r
1715         };\r
1716         var parseDateString = function(str, fmt) {\r
1717                 var d     = false,\r
1718                     m     = false,\r
1719                     y     = false,\r
1720                     now   = new Date(),\r
1721                     parts = fmt.replace(/-sp(-sp)+/g, "-sp").split("-"),\r
1722                     divds = { "dt":".","sl":"/","ds":"-","cc":"," };                    \r
1723 \r
1724                 loopLabel:\r
1725                 for(var pt = 0, part; part = parts[pt]; pt++) {                        \r
1726                         if(str.length == 0) { return false; };\r
1727                                 \r
1728                         switch(part) {\r
1729                                 // Dividers\r
1730                                 case "sp": // Space " "\r
1731                                                 if(str.charAt(0).search(/\s/) != -1) {\r
1732                                                         // Be easy on multiple spaces...\r
1733                                                         while(str.charAt(0).search(/\s/) != -1) { str = str.substr(1); };\r
1734                                                         break;\r
1735                                                 } else return "";\r
1736                                 case "dt":\r
1737                                 case "sl":\r
1738                                 case "ds":\r
1739                                 case "cc":\r
1740                                                 if(str.charAt(0) == divds[part]) {\r
1741                                                         str = str.substr(1);\r
1742                                                         break;\r
1743                                                 } else return "";\r
1744                                 // DAY\r
1745                                 case "d": // Day of the month, 2 digits with leading zeros (01 - 31)\r
1746                                 case "j": // Day of the month without leading zeros (1 - 31)  \r
1747                                           // Accept both when parsing                                                          \r
1748                                                 if(str.search(/^(3[01]|[12][0-9]|0?[1-9])/) != -1) {\r
1749                                                         d = +str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0];\r
1750                                                         str = str.substr(str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0].length);                                                        \r
1751                                                         break;\r
1752                                                 } else return "";\r
1753                                                 \r
1754                                 case "D": // A textual representation of a day, three letters (Mon - Sun)\r
1755                                 case "l": // A full textual representation of the day of the week (Monday - Sunday)\r
1756                                                 l = part == "D" ? localeDefaults.dayAbbrs : localeDefaults.fullDays;\r
1757                                                 for(var i = 0; i < 7; i++) {\r
1758                                                         if(new RegExp("^" + l[i], "i").test(str)) {\r
1759                                                                 str = str.substr(l[i].length);\r
1760                                                                 continue loopLabel;\r
1761                                                         };\r
1762                                                 };\r
1763                                                 return "";\r
1764                                 /*\r
1765                                 case "j": // Day of the month without leading zeros (1 - 31)\r
1766                                                 if(str.search(/^([1-9]|[12][0-9]|3[01])/) != -1) {\r
1767                                                         d = +str.match(/^([1-9]|[12][0-9]|3[01])/)[0];\r
1768                                                         str = str.substr(str.match(/^(\s?[1-9]|[12][0-9]|3[01])/)[0].length);\r
1769                                                         break;\r
1770                                                 } else return "";\r
1771                                 */\r
1772                                 case "N": // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)\r
1773                                 case "w": // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)\r
1774                                                 if(str.search(part == "N" ? /^([1-7])/ : /^([0-6])/) != -1) {\r
1775                                                         str = str.substr(1);\r
1776                                                         break;\r
1777                                                 } else return "";\r
1778                                 case "S": // English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th\r
1779                                                 if(str.search(/^(st|nd|rd|th)/i) != -1) {\r
1780                                                         str = str.substr(2);\r
1781                                                         break;\r
1782                                                 } else return "";\r
1783                                 case "z": // The day of the year (starting from 0): 0 - 365\r
1784                                                 if(str.search(/^([0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-5])/) != -1) {\r
1785                                                         str = str.substr(str.match(/^([0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-5])/)[0].length);\r
1786                                                         break;\r
1787                                                 } else return "";\r
1788                                 // WEEK\r
1789                                 case "W": // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0): 1 - 53\r
1790                                                 if(str.search(/^([1-9]|[1234[0-9]|5[0-3])/) != -1) {\r
1791                                                         str = str.substr(str.match(/^([1-9]|[1234[0-9]|5[0-3])/)[0].length);\r
1792                                                         break;\r
1793                                                 } else return "";\r
1794                                 // MONTH\r
1795                                 case "M": // A short textual representation of a month, three letters\r
1796                                 case "F": // A full textual representation of a month, such as January or March\r
1797                                                 l = localeDefaults.fullMonths.concat(localeDefaults.monthAbbrs); // : localeDefaults.fullMonths;\r
1798                                                 for(var i = 0; i < 24; i++) {\r
1799                                                         if(str.search(new RegExp("^" + l[i],"i")) != -1) {\r
1800                                                                 str = str.substr(l[i].length);\r
1801                                                                 m = ((i + 12) % 12);                                                                 \r
1802                                                                 continue loopLabel;\r
1803                                                         };\r
1804                                                 };\r
1805                                                 return "";\r
1806                                 case "m": // Numeric representation of a month, with leading zeros\r
1807                                 case "n": // Numeric representation of a month, without leading zeros\r
1808                                                 //l = part == "m" ? /^(0[1-9]|1[012])/ : /^([1-9]|1[012])/;\r
1809                                                 // Accept either when parsing\r
1810                                                 l = /^(1[012]|0?[1-9])/;\r
1811                                                 if(str.search(l) != -1) {\r
1812                                                         m = +str.match(l)[0] - 1;\r
1813                                                         str = str.substr(str.match(l)[0].length);\r
1814                                                         break;\r
1815                                                 } else return "";\r
1816                                 case "t": // Number of days in the given month: 28 through 31\r
1817                                                 if(str.search(/2[89]|3[01]/) != -1) {\r
1818                                                         str = str.substr(2);\r
1819                                                         break;\r
1820                                                 } else return "";\r
1821                                 // YEAR\r
1822                                 case "Y": // A full numeric representation of a year, 4 digits\r
1823                                 case "o": // ISO-8601 year number. This has the same value as Y\r
1824                                                 if(str.search(/^(\d{4})/) != -1) {\r
1825                                                         y = str.substr(0,4);\r
1826                                                         str = str.substr(4);\r
1827                                                         break;\r
1828                                                 } else return "";\r
1829                                 case "y": // A two digit representation of a year\r
1830                                                 if(str.search(/^(0[0-9]|[1-9][0-9])/) != -1) {\r
1831                                                         y = +str.substr(0,2);\r
1832                                                         y = +y < 50 ? '20' + y : '19' + y;\r
1833                                                         str = str.substr(2);\r
1834                                                         break;\r
1835                                                 } else return "";\r
1836                                 default:\r
1837                                                 return "";\r
1838                         };\r
1839                 };   \r
1840                 \r
1841                 d = d === false ? now.getDate() : d;\r
1842                 m = m === false ? now.getMonth() - 1 : m;\r
1843                 y = y === false ? now.getFullYear() : y;\r
1844                    \r
1845                 var tmpDate = new Date(y,m,d);\r
1846                 return isNaN(tmpDate) ? "" : tmpDate;\r
1847         };\r
1848         var repositionDatePickers = function(e) {\r
1849                 for(dp in datePickers) {\r
1850                         if(!datePickers[dp].created || datePickers[dp].staticPos || (!datePickers[dp].staticPos && !datePickers[dp].dragDisabled)) continue;\r
1851                         datePickers[dp].reposition();\r
1852                 };\r
1853         };\r
1854         var addDatePicker = function(options) {\r
1855                 if(!options.id) { throw "A datePicker requires an associated element with an id attribute"; };\r
1856                 if(options.id in datePickers) { return; };\r
1857                 var elem = document.getElementById(options.id);\r
1858                 if(!elem) throw "Cannot locate a datePicker's associated element with an id of:" + options.id;\r
1859                 if(elem.tagName.search(/select|input/i) == -1) {\r
1860                         if(!("callbacks" in options) || !("dateselect" in options.callbacks)) {\r
1861                                 throw "A 'dateselect' callback function is required for datePickers not associated with a form element";\r
1862                         };\r
1863                         options.staticPos       = true;\r
1864                         options.splitDate       = false;\r
1865                         options.hideInput       = false;\r
1866                         options.noFadeEffect    = true;\r
1867                         options.dragDisabled    = true;\r
1868                         options.positioned      = false;\r
1869                 } else if(!options.staticPos) {\r
1870                         options.hideInput       = false;                                                 \r
1871                 } else {\r
1872                         options.noFadeEffect    = true;\r
1873                         options.dragDisabled    = true;\r
1874                 };\r
1875 \r
1876                 datePickers[options.id] = new datePicker(options);\r
1877         };\r
1878         var parseCallbacks = function(cbs) {\r
1879                 if(cbs == null) { return {}; };\r
1880                 var func,\r
1881                     type,\r
1882                     cbObj = {},\r
1883                     parts,\r
1884                     obj;\r
1885                 for(var i = 0, fn; fn = cbs[i]; i++) {\r
1886                         type = fn.match(/(cb_(dateselect|redraw|create)_)([^\s|$]+)/i)[1].replace(/^cb_/i, "").replace(/_$/, "");\r
1887                         fn   = fn.replace(/cb_(dateselect|redraw|create)_/i, "").replace(/-/g, ".");\r
1888                         \r
1889                         try {\r
1890                                 if(fn.indexOf(".") != -1) {\r
1891                                         parts = fn.split('.');\r
1892                                         obj   = window;\r
1893                                         for (var x = 0, part; part = obj[parts[x]]; x++) {\r
1894                                                 if(part instanceof Function) {\r
1895                                                         (function() {\r
1896                                                                 var method = part;\r
1897                                                                 func = function (data) { method.apply(obj, [data]) };\r
1898                                                         })();\r
1899                                                 } else {\r
1900                                                 obj = part;\r
1901                                                 };\r
1902                                         };\r
1903                                 } else {\r
1904                                         func = window[fn];\r
1905                                 };\r
1906 \r
1907                                 if(!(func instanceof Function)) continue;\r
1908                                 if(!(type in cbObj)) { cbObj[type] = []; };\r
1909                                 cbObj[type][cbObj[type].length] = func;\r
1910                         } catch (err) {};\r
1911                 };\r
1912                 return cbObj;\r
1913         };\r
1914         // Used by the button to dictate whether to open or close the datePicker\r
1915         var isVisible = function(id) {\r
1916                 return (!id || !(id in datePickers)) ? false : datePickers[id].visible;\r
1917         };                \r
1918         var create = function(inp) {\r
1919                 if(!(typeof document.createElement != "undefined" && typeof document.documentElement != "undefined" && typeof document.documentElement.offsetWidth == "number")) { return; };\r
1920 \r
1921                 // Has the locale file loaded?\r
1922                 if(typeof(fdLocale) == "object" && !localeImport) {\r
1923                         localeImport = {\r
1924                                 titles          : fdLocale.titles,\r
1925                                 fullMonths      : fdLocale.fullMonths,\r
1926                                 monthAbbrs      : fdLocale.monthAbbrs,\r
1927                                 fullDays        : fdLocale.fullDays,\r
1928                                 dayAbbrs        : fdLocale.dayAbbrs,\r
1929                                 firstDayOfWeek  : ("firstDayOfWeek" in fdLocale) ? fdLocale.firstDayOfWeek : 0,\r
1930                                 imported        : true\r
1931                         };\r
1932                 } else if(!localeImport) {\r
1933                         localeImport = localeDefaults;\r
1934                 };  \r
1935                 \r
1936                 var formElements = (inp && inp.tagName) ? [inp] : joinNodeLists(document.getElementsByTagName('input'), document.getElementsByTagName('select')),\r
1937                     disableDays  = /disable-days-([1-7]){1,6}/g,\r
1938                     highlight    = /highlight-days-([1-7]{1,7})/,\r
1939                     rangeLow     = /range-low-(((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))|((\d)-(day|week|month|year))|(today))/,\r
1940                     rangeHigh    = /range-high-(((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))|((\d)-(day|week|month|year))|(today))/,\r
1941                     dateFormat   = /dateformat(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|z|W|M|F|m|n|t|Y|o|y|O|p])))+/,\r
1942                     statusFormat = /statusformat(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|z|W|M|F|m|n|t|Y|o|y|O|p])))+/,                    \r
1943                     disableDates = /disable((-(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])){2}|(-((\d\d\d\d)|(xxxx))((0[1-9]|1[012])|(xx))(0[1-9]|[12][0-9]|3[01])))/g,\r
1944                     enableDates  = /enable((-(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])){2}|(-((\d\d\d\d)|(xxxx))((0[1-9]|1[012])|(xx))(0[1-9]|[12][0-9]|3[01])))/g,\r
1945                     callbacks    = /((cb_(dateselect|redraw|create)_)([^\s|$]+))/ig,\r
1946                     positioned   = /display-inline-([^\s|$]+)/i,\r
1947                     bPositioned  = /button-([^\s|$]+)/i,\r
1948                     range,tmp,j,t,options,dts,parts;                  \r
1949                     \r
1950                 for(var i = 0, elem; elem = formElements[i]; i++) {  \r
1951                         if(elem.className && (elem.className.search(dateFormat) != -1 || elem.className.search(/split-date/) != -1) && ((elem.tagName.toLowerCase() == "input" && (elem.type == "text" || elem.type == "hidden")) || elem.tagName.toLowerCase() == "select")) {\r
1952                                 \r
1953                                 if(elem.id && elem.id in datePickers) {                                                                                                                        \r
1954                                         if(!datePickers[elem.id].staticPos) { datePickers[elem.id].createButton(); }\r
1955                                         else { \r
1956                                                 if(!document.getElementById("fd-" + elem.id)) {\r
1957                                                         datePickers[elem.id].created = false;                                                         \r
1958                                                         datePickers[elem.id].create();                                                     \r
1959                                                 } else if(inp) {    \r
1960                                                         // Only do this if called from an ajax update etc                                                    \r
1961                                                         datePickers[elem.id].setDateFromInput();  \r
1962                                                         datePickers[elem.id].updateTable();      \r
1963                                                 };                                        \r
1964                                         };                                          \r
1965                                         continue;\r
1966                                 };\r
1967                                 \r
1968                                 if(!elem.id) { elem.id = "fdDatePickerInput-" + uniqueId++; };\r
1969                                 \r
1970                                 options = {\r
1971                                         id:elem.id,\r
1972                                         low:"",\r
1973                                         high:"",\r
1974                                         format:"d-sl-m-sl-Y",\r
1975                                         statusFormat:"",\r
1976                                         highlightDays:[0,0,0,0,0,1,1],\r
1977                                         disabledDays:[0,0,0,0,0,0,0],\r
1978                                         disabledDates:{},\r
1979                                         enabledDates:{},\r
1980                                         noFadeEffect:elem.className.search(/no-animation/i) != -1,\r
1981                                         staticPos:elem.className.search(/display-inline/i) != -1,\r
1982                                         hideInput:elem.className.search(/hide-input/i) != -1,\r
1983                                         noToday:elem.className.search(/no-today-button/i) != -1,\r
1984                                         showWeeks:elem.className.search(/show-week/i) != -1,\r
1985                                         dragDisabled:nodrag ? true : elem.className.search(/disable-drag/i) != -1,\r
1986                                         positioned:false,\r
1987                                         firstDayOfWeek:localeImport.firstDayOfWeek,\r
1988                                         fillGrid:elem.className.search(/fill-grid/i) != -1,\r
1989                                         constrainSelection:elem.className.search(/fill-grid-no-select/i) != -1,\r
1990                                         callbacks:parseCallbacks(elem.className.match(callbacks)),\r
1991                                         buttonWrapper:""\r
1992                                 };                            \r
1993                                 \r
1994                                 // Positioning of static dp's\r
1995                                 if(options.staticPos && elem.className.search(positioned) != -1) {\r
1996                                         options.positioned = elem.className.match(positioned)[1];                                        \r
1997                                 };\r
1998                                 \r
1999                                 // Positioning of non-static dp's button\r
2000                                 if(!options.staticPos && elem.className.search(bPositioned) != -1) {\r
2001                                         options.buttonWrapper = elem.className.match(bPositioned)[1];                                        \r
2002                                 };\r
2003                                 \r
2004                                 // Opacity of non-static datePickers\r
2005                                 if(!options.staticPos) {\r
2006                                         options.finalOpacity = elem.className.search(/opacity-([1-9]{1}[0-9]{1})/i) != -1 ? elem.className.match(/opacity-([1-9]{1}[0-9]{1})/i)[1] : 90                              \r
2007                                 };\r
2008                                 \r
2009                                 // Dates to disable\r
2010                                 dts = elem.className.match(disableDates);\r
2011                                 if(dts) {\r
2012                                         for(t = 0; t < dts.length; t++) {\r
2013                                                 parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("disable-", "").split("-");\r
2014                                                 options.disabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;                                                \r
2015                                         };\r
2016                                 };\r
2017 \r
2018                                 // Dates to enable\r
2019                                 dts = elem.className.match(enableDates);\r
2020                                 if(dts) {\r
2021                                         for(t = 0; t < dts.length; t++) {\r
2022                                                 parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("enable-", "").split("-");\r
2023                                                 options.enabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;                                                \r
2024                                         };\r
2025                                 };\r
2026                                              \r
2027                                 // Split the date into three parts ?                                \r
2028                                 options.splitDate = (elem.className.search(/split-date/) != -1 && document.getElementById(elem.id+splitAppend[0]) && document.getElementById(elem.id+splitAppend[1]) && document.getElementById(elem.id+splitAppend[0]).tagName.search(/input|select/i) != -1 && document.getElementById(elem.id+splitAppend[1]).tagName.search(/input|select/i) != -1);                              \r
2029                                 \r
2030                                 // Date format\r
2031                                 if(!options.splitDate && elem.className.search(dateFormat) != -1) {\r
2032                                         options.format = elem.className.match(dateFormat)[0].replace('dateformat-','');\r
2033                                 };\r
2034 \r
2035                                 // Status bar date format\r
2036                                 if(elem.className.search(statusFormat) != -1) {\r
2037                                         options.statusFormat = elem.className.match(statusFormat)[0].replace('statusformat-','');\r
2038                                 };\r
2039                                 \r
2040                                 // The days of the week to highlight\r
2041                                 if(elem.className.search(highlight) != -1) {\r
2042                                         tmp = elem.className.match(highlight)[0].replace(/highlight-days-/, '');\r
2043                                         options.highlightDays = [0,0,0,0,0,0,0];\r
2044                                         for(j = 0; j < tmp.length; j++) {\r
2045                                                 options.highlightDays[tmp.charAt(j) - 1] = 1;\r
2046                                         };\r
2047                                 };\r
2048                                 \r
2049                                 // The days of the week to disable\r
2050                                 if(elem.className.search(disableDays) != -1) {\r
2051                                         tmp = elem.className.match(disableDays)[0].replace(/disable-days-/, '');\r
2052                                         options.disabledDays = [0,0,0,0,0,0,0];                                         \r
2053                                         for(j = 0; j < tmp.length; j++) {\r
2054                                                 options.disabledDays[tmp.charAt(j) - 1] = 1;\r
2055                                         };\r
2056                                 };\r
2057 \r
2058                                 // The lower limit\r
2059                                 if(elem.className.search(rangeLow) != -1) {\r
2060                                         options.low = parseRangeFromString(elem.className.match(rangeLow)[0]);\r
2061                                 };\r
2062 \r
2063                                 // The higher limit\r
2064                                 if(elem.className.search(rangeHigh) != -1) {\r
2065                                         options.high = parseRangeFromString(elem.className.match(rangeHigh)[0]);\r
2066                                 };\r
2067 \r
2068                                 // Always round lower & higher limits if a selectList involved\r
2069                                 if(elem.tagName.search(/select/i) != -1) {\r
2070                                         range        = grepRangeLimits(elem);\r
2071                                         options.low  = options.low  ? range[0] + String(options.low).substr(4,4)  : range[0] + "0101";\r
2072                                         options.high = options.high ? range[1] + String(options.high).substr(4,4) : range[1] + "1231";\r
2073                                 };\r
2074 \r
2075                                 addDatePicker(options);\r
2076                         };\r
2077                 };\r
2078         };\r
2079 \r
2080         addEvent(window, 'load',   create);\r
2081         addEvent(window, 'unload', destroy);\r
2082         addEvent(window, 'resize', repositionDatePickers);\r
2083 \r
2084         return {\r
2085                 addEvent:               function(obj, type, fn) { return addEvent(obj, type, fn); },\r
2086                 removeEvent:            function(obj, type, fn) { return removeEvent(obj, type, fn); },\r
2087                 stopEvent:              function(e) { return stopEvent(e); },\r
2088                 show:                   function(inpID) { return showDatePicker(inpID); },\r
2089                 create:                 function(inp) { create(inp); },                 \r
2090                 repositionDatePickers:  function() { repositionDatePickers(); },\r
2091                 newDatePicker:          function(opts) { addDatePicker(opts); },\r
2092                 overrideAppendID:       function(arr) { splitAppend = (arr && arr.length && arr.length == 2) ? arr : splitAppend },\r
2093                 printFormattedDate:     function(dt, fmt, useImportedLocale) { return printFormattedDate(dt, fmt, useImportedLocale); },\r
2094                 setDateFromInput:       function(inpID) { if(!inpID || !(inpID in datePickers) || !datePickers[inpID].created) return false; datePickers[inpID].setDateFromInput(); },\r
2095                 setRangeLow:            function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeLow(yyyymmdd); },\r
2096                 setRangeHigh:           function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeHigh(yyyymmdd); },\r
2097                 parseDateString:        function(str, format) { return parseDateString(str, format); },\r
2098                 disableDrag:            function() { noDrag = true; },\r
2099                 setGlobalVars:          function(json) { affectJSON(json); },\r
2100                 addDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addDisabledDates(dts); },\r
2101                 setDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDisabledDates(dts); }                                              \r
2102         }; \r
2103 })();\r
2104 \r
2105 // Change this to use your own month & day id appendages\r
2106 // It can also be passed using JSON within the script tag \r
2107 // datePickerController.overrideAppendID(["Day", "Month"]);\r