2 DatePicker v4b rc1 by frequency-decoder.com
4 Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)
6 Please credit frequency-decoder in any derivative work - thanks.
10 * to copy, distribute, display, and perform the work
11 * to make derivative works
12 * to make commercial use of the work
14 Under the following conditions:
18 You must attribute the work in the manner specified by the author or licensor.
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.
24 * For any reuse or distribution, you must make clear to others the license terms of this work.
25 * Any of these conditions can be waived if you get permission from the copyright holder.
28 var datePickerController = (function datePickerController() {
29 var languageInfo = navigator.language ? navigator.language.toLowerCase().replace(/-[a-z]+$/, "") : navigator.userLanguage ? navigator.userLanguage.toLowerCase().replace(/-[a-z]+$/, "") : "en",
32 weeksInYearCache = {},
34 nbsp = String.fromCharCode(160),
36 splitAppend = ["-dd","-mm"],
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"];
40 var scriptFiles = document.getElementsByTagName('head')[0].getElementsByTagName('script'),
41 scriptInner = scriptFiles[scriptFiles.length - 1].innerHTML.replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""),
42 json = parseJSON(scriptInner);
44 if(typeof json === "object" && !("err" in json)) {
48 if(typeof(fdLocale) != "object" && languageInfo != "en") {
49 var loc = scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/" + languageInfo + ".js",
50 script = document.createElement('script');
52 script.type = "text/javascript";
54 script.setAttribute("charset", "utf-8");
57 var bases = document.getElementsByTagName('base');
58 if (bases.length && bases[0].childNodes.length) {
59 bases[0].appendChild(script);
61 document.getElementsByTagName('head')[0].appendChild(script);
65 document.getElementsByTagName('head')[0].appendChild(script);
73 function affectJSON(json) {
74 if(typeof json !== "object") { return; };
77 switch(key.toLower()) {
79 if(value.search(/^[a-z]{2}$/i) != -1) {
84 if(typeof value === 'object') {
85 if(value.length && value.length == 2) {
90 if(typeof value === 'object') {
102 // Functions shared between the datePickerController object & the datePicker objects
103 function pad(value, length) {
104 length = length || 2;
105 return "0000".substr(0,length - Math.min(String(value).length, length)) + value;
108 function addEvent(obj, type, fn) {
109 if( obj.attachEvent ) {
110 obj["e"+type+fn] = fn;
111 obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
112 obj.attachEvent( "on"+type, obj[type+fn] );
114 obj.addEventListener( type, fn, true );
118 function removeEvent(obj, type, fn) {
120 if( obj.detachEvent ) {
121 obj.detachEvent( "on"+type, obj[type+fn] );
124 obj.removeEventListener( type, fn, true );
129 function stopEvent(e) {
130 e = e || document.parentWindow.event;
131 if(e.stopPropagation) {
137 e.cancelBubble = true;
138 e.returnValue = false;
144 function parseJSON(str) {
145 // Check we have a String
146 if(typeof str !== 'string' || str == "") { return {}; };
148 // Does the Douglas Crockford JSON parser exist in the global scope?
149 if("JSON" in window && "parse" in window.JSON && typeof window.JSON.parse == "function") {
150 return window.JSON.parse(str);
151 // Genious code taken from: http://kentbrewster.com/badges/
152 } else if(/lang|split|formats|nodrag/.test(str.toLower())) {
153 var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
154 'Array,String,Math,RegExp,Image,ActiveXObject;',
155 'return (' , str.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'functionÂ') , ');'].join(''));
159 return {"err":"Trouble parsing JSON object"};
164 // The datePicker object itself
165 function datePicker(options) {
167 this.timerSet = false;
168 this.visible = false;
169 this.fadeTimer = null;
178 this.date = new Date();
180 this.created = false;
181 this.id = options.id;
183 this.firstDayOfWeek = 0;
184 this.buttonWrapper = "buttonWrapper" in options ? options.buttonWrapper : false;
185 this.staticPos = "staticPos" in options ? !!options.staticPos : false;
186 this.disabledDays = "disabledDays" in options && options.disabledDays.length ? options.disabledDays : [0,0,0,0,0,0,0];
187 this.disabledDates = "disabledDates" in options ? options.disabledDates : {};
188 this.enabledDates = "enabledDates" in options ? options.enabledDates : {};
189 this.showWeeks = "showWeeks" in options ? !!options.showWeeks : false;
190 this.low = options.low || "";
191 this.high = options.high || "";
192 this.dragDisabled = nodrag ? true : ("dragDisabled" in options ? !!options.dragDisabled : false);
193 this.positioned = "positioned" in options ? options.positioned : false;
194 this.hideInput = this.staticPos ? false : "hideInput" in options ? !!options.hideInput : false;
195 this.splitDate = "splitDate" in options ? !!options.splitDate : false;
196 this.format = options.format || "d-sl-m-sl-Y";
197 this.statusFormat = options.statusFormat || "";
198 this.highlightDays = options.highlightDays && options.highlightDays.length ? options.highlightDays : [0,0,0,0,0,1,1];
199 this.noFadeEffect = "noFadeEffect" in options ? !!options.noFadeEffect : false;
200 this.opacityTo = this.noFadeEffect || this.staticPos ? 99 : 90;
202 this.fillGrid = !!options.fillGrid;
203 this.noToday = !!options.noToday;
204 this.constrainSelection = this.fillGrid && !!options.constrainSelection;
205 this.finalOpacity = !this.staticPos && "finalOpacity" in options ? +options.finalOpacity : 90;
206 this.dynDisabledDates = {};
207 this.inUpdate = false;
210 this.interval = new Date();
214 for(var thing in options.callbacks) {
215 this.callbacks[thing] = options.callbacks[thing];
218 // Adjust time to stop daylight savings madness on windows
219 this.date.setHours(12);
221 this.startDrag = function(e) {
222 e = e || document.parentWindow.event;
223 o.mx = e.pageX?e.pageX:e.clientX?e.clientX:e.x;
224 o.my = e.pageY?e.pageY:e.clientY?e.clientY:e.Y;
225 o.x = parseInt(o.div.style.left);
226 o.y = parseInt(o.div.style.top);
227 addEvent(document,'mousemove',o.trackDrag, false);
228 addEvent(document,'mouseup',o.stopDrag, false);
229 o.div.style.zIndex = 10000;
232 this.trackDrag = function(e) {
233 e = e || window.event;
234 var diffx = (e.pageX?e.pageX:e.clientX?e.clientX:e.x) - o.mx;
235 var diffy = (e.pageY?e.pageY:e.clientY?e.clientY:e.Y) - o.my;
236 o.div.style.left = Math.round(o.x + diffx) > 0 ? Math.round(o.x + diffx) + 'px' : "0px";
237 o.div.style.top = Math.round(o.y + diffy) > 0 ? Math.round(o.y + diffy) + 'px' : "0px";
239 @if(@_jscript_version <= 5.6)
240 if(o.staticPos) return;
241 o.iePopUp.style.top = o.div.style.top;
242 o.iePopUp.style.left = o.div.style.left;
246 this.stopDrag = function(e) {
247 removeEvent(document,'mousemove',o.trackDrag, false);
248 removeEvent(document,'mouseup',o.stopDrag, false);
249 o.div.style.zIndex = 9999;
251 this.changeHandler = function() {
252 o.setDateFromInput();
253 if(o.created) o.updateTable();
255 this.reposition = function() {
256 if(!o.created || !o.getElem() || o.staticPos) { return; };
258 o.div.style.visibility = "hidden";
259 o.div.style.left = o.div.style.top = "0px";
260 o.div.style.display = "block";
262 var osh = o.div.offsetHeight,
263 osw = o.div.offsetWidth,
264 elem = document.getElementById('fd-but-' + o.id),
265 pos = o.truePosition(elem),
266 trueBody = (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body,
267 scrollTop = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollTop,
268 scrollLeft = window.devicePixelRatio || window.opera ? 0 : trueBody.scrollLeft;
270 o.div.style.visibility = "visible";
272 o.div.style.left = Number(parseInt(trueBody.clientWidth+scrollLeft) < parseInt(osw+pos[0]) ? Math.abs(parseInt((trueBody.clientWidth+scrollLeft) - osw)) : pos[0]) + "px";
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";
276 @if(@_jscript_version <= 5.6)
277 o.iePopUp.style.top = o.div.style.top;
278 o.iePopUp.style.left = o.div.style.left;
279 o.iePopUp.style.width = osw + "px";
280 o.iePopUp.style.height = (osh - 2) + "px";
284 this.updateTable = function(noCallback) {
285 if(o.inUpdate) return;
291 o.date.setDate(Math.min(o.date.getDate()+o.dayInc, daysInMonth(o.date.getMonth()+o.monthInc,o.date.getFullYear()+o.yearInc)) );
292 o.date.setMonth(o.date.getMonth() + o.monthInc);
293 o.date.setFullYear(o.date.getFullYear() + o.yearInc);
297 if(!o.noToday) { o.disableTodayButton(); };
298 o.showHideButtons(o.date);
300 var cd = o.date.getDate(),
301 cm = o.date.getMonth(),
302 cy = o.date.getFullYear(),
303 cursorDate = (String(cy) + pad(cm+1) + pad(cd)),
304 tmpDate = new Date(cy, cm, 1);
308 var dt, cName, td, i, currentDate, cellAdded, col, currentStub, abbr, bespokeRenderClass,
309 weekDayC = ( tmpDate.getDay() + 6 ) % 7,
310 firstColIndex = (((weekDayC - o.firstDayOfWeek) + 7 ) % 7) - 1,
311 dpm = daysInMonth(cm, cy),
313 dateSetD = (o.dateSet != null) ? o.dateSet.getFullYear() + pad(o.dateSet.getMonth()+1) + pad(o.dateSet.getDate()) : false,
314 stub = String(tmpDate.getFullYear()) + pad(tmpDate.getMonth()+1),
315 cellAdded = [4,4,4,4,4,4],
316 lm = new Date(cy, cm-1, 1),
317 nm = new Date(cy, cm+1, 1),
318 daySub = daysInMonth(lm.getMonth(), lm.getFullYear()),
319 stubN = String(nm.getFullYear()) + pad(nm.getMonth()+1),
320 stubP = String(lm.getFullYear()) + pad(lm.getMonth()+1),
321 weekDayN = (nm.getDay() + 6) % 7,
322 weekDayP = (lm.getDay() + 6) % 7,
323 today = today.getFullYear() + pad(today.getMonth()+1) + pad(today.getDate());
325 o.firstDateShown = !o.constrainSelection && o.fillGrid && (0 - firstColIndex < 1) ? String(stubP) + (daySub + (0 - firstColIndex)) : stub + "01";
326 o.lastDateShown = !o.constrainSelection && o.fillGrid ? stubN + pad(41 - firstColIndex - dpm) : stub + String(dpm);
327 o.currentYYYYMM = stub;
329 bespokeRenderClass = o.callback("redraw", {id:o.id, dd:pad(cd), mm:pad(cm+1), yyyy:cy, firstDateDisplayed:o.firstDateShown, lastDateDisplayed:o.lastDateShown}) || {};
330 o.dynDisabledDates = o.getDisabledDates(cy, cm + 1);
332 for(var curr = 0; curr < 42; curr++) {
333 row = Math.floor(curr / 7);
336 while(td.firstChild) td.removeChild(td.firstChild);
337 if((curr > firstColIndex && curr <= (firstColIndex + dpm)) || o.fillGrid) {
340 dt = curr - firstColIndex;
348 selectable = !o.constrainSelection;
349 cName.push("month-out");
350 } else if(dt > dpm) {
354 selectable = !o.constrainSelection;
355 cName.push("month-out");
358 td.appendChild(document.createTextNode(dt));
359 currentDate = currentStub + String(dt < 10 ? "0" : "") + dt;
361 if(o.low && +currentDate < +o.low || o.high && +currentDate > +o.high) {
362 td.className = "out-of-range";
363 if(o.showWeeks) { cellAdded[row] = Math.min(cellAdded[row], 2); };
367 cName.push("cd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));
369 cName.push("not-selectable yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));
372 weekDay = ( weekDay + dt + 6 ) % 7;
374 if(currentDate == today) { cName.push("date-picker-today"); };
376 if(dateSetD == currentDate) { cName.push("date-picker-selected-date"); };
378 if(o.disabledDays[weekDay] || currentDate in o.dynDisabledDates) { cName.push("day-disabled"); }
380 if(currentDate in bespokeRenderClass) { cName.push(bespokeRenderClass[currentDate]); }
382 if(o.highlightDays[weekDay]) { cName.push("date-picker-highlight"); };
384 if(cursorDate == currentDate) { td.id = o.id + "-date-picker-hover"; cName.push("date-picker-hover"); }
385 else { td.id = ""; };
387 td.className = cName.join(" ");
390 cellAdded[row] = Math.min(cName[0] == "month-out" ? 3 : 1, cellAdded[row]);
394 td.className = "date-picker-unused";
396 td.appendChild(document.createTextNode(nbsp));
399 if(o.showWeeks && curr - (row * 7) == 6) {
400 while(o.wkThs[row].firstChild) o.wkThs[row].removeChild(o.wkThs[row].firstChild);
401 o.wkThs[row].appendChild(document.createTextNode(cellAdded[row] == 4 && !o.fillGrid ? nbsp : getWeekNumber(cy, cm, curr - firstColIndex - 6)));
402 o.wkThs[row].className = "date-picker-week-header" + (["",""," out-of-range"," month-out",""][cellAdded[row]]);
407 var span = o.titleBar.getElementsByTagName("span");
408 while(span[0].firstChild) span[0].removeChild(span[0].firstChild);
409 while(span[1].firstChild) span[1].removeChild(span[1].firstChild);
410 span[0].appendChild(document.createTextNode(getMonthTranslation(cm, false) + nbsp));
411 span[1].appendChild(document.createTextNode(cy));
414 o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));
415 o.timer = window.setTimeout(o.updateTable, o.timerInc);
421 this.show = function() {
422 var elem = this.getElem();
423 if(!elem || this.visible || elem && elem.disabled) { return; };
425 if(!document.getElementById('fd-' + this.id)) {
426 this.created = false;
429 this.setDateFromInput();
434 if(!this.staticPos) this.reposition();
436 addEvent(this.staticPos ? this.table : document, "mousedown", this.events.onmousedown);
437 this.opacityTo = this.finalOpacity;
438 this.div.style.display = "block";
439 if(!this.staticPos) {
441 @if(@_jscript_version <= 5.6)
442 this.iePopUp.style.width = this.div.offsetWidth + "px";
443 this.iePopUp.style.height = this.div.offsetHeight + "px";
444 this.iePopUp.style.display = "block";
447 this.addKeyboardEvents();
449 var butt = document.getElementById('fd-but-' + this.id);
450 if(butt) butt.className = butt.className.replace("dp-button-active", "") + " dp-button-active";
453 this.opacity = this.opacityTo;
456 this.hide = function() {
457 if(!this.visible) return;
459 if(this.staticPos) return;
461 var butt = document.getElementById('fd-but-' + this.id);
462 if(butt) butt.className = butt.className.replace("dp-button-active", "");
464 removeEvent(document, "mousedown", this.events.onmousedown);
465 removeEvent(document, "mouseup", this.events.clearTimer);
466 this.removeKeyboardEvents();
469 @if(@_jscript_version <= 5.6)
470 this.iePopUp.style.display = "none";
477 //var elem = this.getElem();
478 //if(!elem.type || elem.type && elem.type != "hidden") { elem.focus(); };
480 this.destroy = function() {
481 // Cleanup for Internet Explorer
482 removeEvent(this.staticPos ? this.table : document, "mousedown", o.events.onmousedown);
483 removeEvent(document, "mouseup", o.events.clearTimer);
484 o.removeKeyboardEvents();
485 clearTimeout(o.fadeTimer);
486 clearTimeout(o.timer);
489 @if(@_jscript_version <= 5.6)
491 o.iePopUp.parentNode.removeChild(o.iePopUp);
497 if(!this.staticPos && document.getElementById(this.id.replace(/^fd-/, 'fd-but-'))) {
498 var butt = document.getElementById(this.id.replace(/^fd-/, 'fd-but-'));
499 butt.onclick = butt.onpress = null;
502 if(this.div && this.div.parentNode) {
503 this.div.parentNode.removeChild(this.div);
508 this.resizeInlineDiv = function() {
509 o.div.style.width = o.table.offsetWidth + "px";
511 this.create = function() {
512 if(this.created) { return; };
514 function createTH(details) {
515 var th = document.createElement('th');
516 if(details.thClassName) th.className = details.thClassName;
517 if(details.colspan) {
520 th.setAttribute('colSpan',details.colspan);
522 th.setAttribute('colspan',details.colspan);
528 th.unselectable = "on";
532 function createThAndButton(tr, obj) {
533 for(var i = 0, details; details = obj[i]; i++) {
534 var th = createTH(details);
536 var but = document.createElement('span');
537 but.className = details.className;
538 but.id = o.id + details.id;
539 but.appendChild(document.createTextNode(details.text || o.nbsp));
540 but.title = details.title || "";
541 if(details.onmousedown) but.onmousedown = details.onmousedown;
542 if(details.onclick) but.onclick = details.onclick;
543 if(details.onmouseout) but.onmouseout = details.onmouseout;
546 th.unselectable = but.unselectable = "on";
552 this.div = document.createElement('div');
553 this.div.id = "fd-" + this.id;
554 this.div.className = "datePicker";
556 var tr, row, col, tableHead, tableBody, tableFoot;
558 this.table = document.createElement('table');
559 this.table.className = "datePickerTable";
560 this.table.onmouseover = this.events.ontablemouseover;
561 this.table.onmouseout = this.events.ontablemouseout;
563 this.div.appendChild(this.table);
565 if(!this.staticPos) {
566 this.div.style.visibility = "hidden";
567 if(!this.dragDisabled) { this.div.className += " drag-enabled"; };
568 document.getElementsByTagName('body')[0].appendChild(this.div);
571 @if(@_jscript_version <= 5.6)
572 this.iePopUp = document.createElement('iframe');
573 this.iePopUp.src = "javascript:'<html></html>';";
574 this.iePopUp.setAttribute('className','iehack');
575 this.iePopUp.scrolling="no";
576 this.iePopUp.frameBorder="0";
577 this.iePopUp.name = this.iePopUp.id = this.id + "-iePopUpHack";
578 document.body.appendChild(this.iePopUp);
582 elem = this.positioned ? document.getElementById(this.positioned) : this.getElem();
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;
588 this.div.className += " static-datepicker";
591 this.div.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");
592 this.div.tabIndex = 0;
594 this.div.onfocus = this.events.onfocus;
595 this.div.onblur = this.events.onblur;
597 if(this.positioned) {
598 elem.appendChild(this.div);
600 elem.parentNode.insertBefore(this.div, elem.nextSibling);
604 var elemList = [elem];
606 elemList[elemList.length] = document.getElementById(this.id + splitAppend[1]);
607 elemList[elemList.length] = document.getElementById(this.id + splitAppend[0]);
609 for(var i = 0; i < elemList.length; i++) {
610 if(elemList[i].tagName) elemList[i].className += " fd-hidden-input";
614 setTimeout(this.resizeInlineDiv, 300);
618 if(this.statusFormat) {
619 tableFoot = document.createElement('tfoot');
620 this.table.appendChild(tableFoot);
621 tr = document.createElement('tr');
622 tr.className = "date-picker-tfoot";
623 tableFoot.appendChild(tr);
624 this.statusBar = createTH({thClassName:"date-picker-statusbar", colspan:this.showWeeks ? 8 : 7});
625 tr.appendChild(this.statusBar);
627 if(!this.dragDisabled) {
628 this.statusBar.className += " drag-enabled";
629 addEvent(this.statusBar,'mousedown',this.startDrag,false);
633 tableHead = document.createElement('thead');
634 this.table.appendChild(tableHead);
636 tr = document.createElement('tr');
637 tableHead.appendChild(tr);
640 this.titleBar = createTH({thClassName:!this.dragDisabled ? "date-picker-title drag-enabled" : "date-picker-title", colspan:this.showWeeks ? 8 : 7});
641 if(!this.dragDisabled) {
642 addEvent(this.titleBar,'mousedown',o.startDrag,false);
645 tr.appendChild(this.titleBar);
648 var span = document.createElement('span');
649 span.appendChild(document.createTextNode(nbsp));
650 span.className = !this.dragDisabled ? "month-display drag-enabled" : "month-display";
651 this.titleBar.appendChild(span);
653 span = document.createElement('span');
654 span.appendChild(document.createTextNode(nbsp));
655 span.className = !this.dragDisabled ? "year-display drag-enabled" : "year-display";
656 this.titleBar.appendChild(span);
660 tr = document.createElement('tr');
661 tableHead.appendChild(tr);
663 createThAndButton(tr, [
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 },
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 },
666 {colspan:this.showWeeks ? 4 : 3, className:"today-but", id:"-today-but", text:getTitleTranslation(4), onclick:this.events.gotoToday},
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 },
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 }]);
670 tableBody = document.createElement('tbody');
671 this.table.appendChild(tableBody);
673 var colspanTotal = this.showWeeks ? 8 : 7,
674 colOffset = this.showWeeks ? 0 : -1,
677 for(var rows = 0; rows < 7; rows++) {
678 row = document.createElement('tr');
681 tableBody.appendChild(row);
683 tableHead.appendChild(row);
686 for(var cols = 0; cols < colspanTotal; cols++) {
688 if(rows === 0 || (this.showWeeks && cols === 0)) {
689 col = document.createElement('th');
691 col = document.createElement('td');
696 col.unselectable = "on";
699 row.appendChild(col);
700 if((this.showWeeks && cols > 0 && rows > 0) || (!this.showWeeks && rows > 0)) {
701 col.onclick = this.events.onclick;
703 if(rows === 0 && cols > colOffset) {
704 col.className = "date-picker-day-header";
707 col.className = "date-picker-week-header";
716 this.ths = this.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
717 for (var y = 0; y < colspanTotal; y++) {
718 if(y == 0 && this.showWeeks) {
719 this.ths[y].appendChild(document.createTextNode(getTitleTranslation(6)));
720 this.ths[y].title = getTitleTranslation(8);
724 if(y > (this.showWeeks ? 0 : -1)) {
725 but = document.createElement("span");
726 but.className = "fd-day-header";
727 but.onclick = this.ths[y].onclick = this.setFirstDayOfWeek;
730 but.unselectable = "on";
732 this.ths[y].appendChild(but);
738 this.trs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
739 this.tds = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('td');
740 this.butPrevYear = document.getElementById(this.id + "-prev-year-but");
741 this.butPrevMonth = document.getElementById(this.id + "-prev-month-but");
742 this.butToday = document.getElementById(this.id + "-today-but");
743 this.butNextYear = document.getElementById(this.id + "-next-year-but");
744 this.butNextMonth = document.getElementById(this.id + "-next-month-but");
747 this.butToday.style.display = "none";
751 this.wkThs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('th');
752 this.div.className += " weeks-displayed";
755 tableBody = tableHead = tr = createThAndButton = createTH = null;
757 if(this.low && this.high && (this.high - this.low < 7)) { this.equaliseDates(); };
759 this.setDateFromInput();
760 this.updateTableHeaders();
762 this.callback("create", {id:this.id});
768 this.div.style.visibility = "visible";
769 this.opacity = this.opacityTo;
772 this.div.style.visibility = "visible";
776 this.setFirstDayOfWeek = function(e) {
777 e = e || document.parentWindow.event;
778 var elem = e.target != null ? e.target : e.srcElement;
779 while(elem.tagName.toLowerCase() != "th") elem = elem.parentNode;
780 var cnt = o.showWeeks ? -1 : 0;
781 while(elem.previousSibling) {
782 elem = elem.previousSibling;
783 if(elem.tagName.toLowerCase() == "th") cnt++;
785 o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;
786 o.updateTableHeaders();
789 this.fade = function() {
790 window.clearTimeout(o.fadeTimer);
792 var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4));
794 if(Math.abs(o.opacityTo - diff) > 3 && !o.noFadeEffect) {
795 o.fadeTimer = window.setTimeout(o.fade, 50);
797 o.setOpacity(o.opacityTo);
798 if(o.opacityTo == 0) {
799 o.div.style.display = "none";
808 o.removeKeyboardEvents();
809 if(o.statusBar) { o.updateStatus(getTitleTranslation(9)); };
811 onfocus:function(e) {
812 o.addKeyboardEvents();
813 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };
815 onkeydown: function (e) {
817 if(!o.visible) return false;
819 if(e == null) e = document.parentWindow.event;
820 var kc = e.keyCode ? e.keyCode : e.charCode;
823 // RETURN/ENTER: close & select the date
824 var td = document.getElementById(o.id + "-date-picker-hover");
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);
826 o.dateSet = new Date(o.date);
827 o.returnFormattedDate();
830 } else if(kc == 27) {
831 // ESC: close, no date selection
834 } else if(kc == 32 || kc == 0) {
835 // SPACE: goto today's date
841 // Internet Explorer fires the keydown event faster than the JavaScript engine can
842 // update the interface. The following attempts to fix this.
845 if(new Date().getTime() - o.interval.getTime() < 50) return stopEvent(e);
846 o.interval = new Date();
850 if ((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {
851 if (kc > 96) kc -= (96-48);
853 o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;
854 o.updateTableHeaders();
858 if ( kc < 33 || kc > 40 ) return true;
860 var d = new Date(o.date), tmp, cursorYYYYMM = o.date.getFullYear() + pad(o.date.getMonth()+1);
862 // HOME: Set date to first day of current month
865 // END: Set date to last day of current month
866 } else if(kc == 35) {
867 d.setDate(daysInMonth(d.getMonth(),d.getFullYear()));
869 } else if ( kc == 33 || kc == 34) {
870 var add = (kc == 34) ? 1 : -1;
871 // CTRL + PAGE UP/DOWN: Moves to the same date in the previous/next year
873 d.setFullYear(d.getFullYear() + add);
874 // PAGE UP/DOWN: Moves to the same date in the previous/next month
876 if(!((kc == 33 && o.currentYYYYMM > cursorYYYYMM) || (kc == 34 && o.currentYYYYMM < cursorYYYYMM))) {
879 tmp.setMonth(d.getMonth() + add);
880 d.setDate(Math.min(d.getDate(), daysInMonth(tmp.getMonth(),tmp.getFullYear())));
881 d.setMonth(d.getMonth() + add);
885 } else if ( kc == 37 ) {
886 d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1);
888 } else if ( kc == 39 || kc == 34) {
889 d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1 );
891 } else if ( kc == 38 ) {
892 d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7);
894 } else if ( kc == 40 ) {
895 d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7);
898 if(o.outOfRange(d)) return stopEvent(e);
901 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };
902 var t = String(o.date.getFullYear()) + pad(o.date.getMonth()+1) + pad(o.date.getDate())
904 if(e.ctrlKey || (kc == 33 || kc == 34) || t < o.firstDateShown || t > o.lastDateShown) {
906 o.interval = new Date();
908 if(!o.noToday) { o.disableTodayButton(); }
911 var dt = "cd-" + o.date.getFullYear() + pad(o.date.getMonth()+1) + pad(o.date.getDate());
913 for(var i = 0, td; td = o.tds[i]; i++) {
914 td.className = td.className.replace(/date-picker-hover/g, "");
915 if(td.className.search(dt) == -1) continue;
916 o.showHideButtons(o.date);
917 td.id = o.id + "-date-picker-hover";
918 td.className = td.className.replace(/date-picker-hover/g, "") + " date-picker-hover";
925 gotoToday: function(e) {
930 onmousedown: function(e) {
931 e = e || document.parentWindow.event;
932 var el = e.target != null ? e.target : e.srcElement;
933 while(el.parentNode) {
934 if(el.id && (el.id == "fd-" + o.id || el.id == "fd-but-" + o.id)) {
937 try { el = el.parentNode; } catch(err) { break; };
942 ontablemouseout:function(e) {
943 e = e || document.parentWindow.event;
944 var p = e.toElement || e.relatedTarget;
945 while (p && p != this) try { p = p.parentNode } catch(e) { p = this; };
946 if (p == this) return false;
948 o.currentTR.className = o.currentTR.className.replace('dp-row-highlight', '');
951 if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };
953 ontablemouseover: function(e) {
954 e = e || document.parentWindow.event;
955 var el = e.target != null ? e.target : e.srcElement;
956 while ( el.nodeType != 1 ) el = el.parentNode;
958 if(!el || ! el.tagName) { return; };
959 var statusText = getTitleTranslation(9);
960 switch (el.tagName.toLowerCase()) {
962 if(el.className.search(/date-picker-unused|out-of-range/) != -1) {
963 statusText = getTitleTranslation(9);
964 } else if(el.className.search(/cd-([0-9]{8})/) == -1) {
968 var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
970 el.id = o.id+"-date-picker-hover";
971 el.className = el.className.replace(/date-picker-hover/g, "") + " date-picker-hover";
972 o.date = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2));
973 if(!o.noToday) { o.disableTodayButton(); };
974 statusText = printFormattedDate(o.date, o.statusFormat, true);
979 if(!o.statusBar) { break; };
980 if(el.className.search(/drag-enabled/) != -1) {
981 statusText = getTitleTranslation(10);
982 } else if(el.className.search(/date-picker-week-header/) != -1) {
983 var txt = el.firstChild ? el.firstChild.nodeValue : "";
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);
988 if(!o.statusBar) { break; };
989 if(el.className.search(/drag-enabled/) != -1) {
990 statusText = getTitleTranslation(10);
991 } else if(el.className.search(/day-([0-6])/) != -1) {
992 var day = el.className.match(/day-([0-6])/)[1];
993 statusText = getTitleTranslation(11, [getDayTranslation(day, false)]);
994 } else if(el.className.search(/prev-year/) != -1) {
995 statusText = getTitleTranslation(2);
996 } else if(el.className.search(/prev-month/) != -1) {
997 statusText = getTitleTranslation(0);
998 } else if(el.className.search(/next-year/) != -1) {
999 statusText = getTitleTranslation(3);
1000 } else if(el.className.search(/next-month/) != -1) {
1001 statusText = getTitleTranslation(1);
1002 } else if(el.className.search(/today-but/) != -1 && el.className.search(/disabled/) == -1) {
1003 statusText = getTitleTranslation(12);
1009 while(el.parentNode) {
1011 if(el.nodeType == 1 && el.tagName.toLowerCase() == "tr") {
1012 if(el == o.currentTR) break;
1014 o.currentTR.className = o.currentTR.className.replace('dp-row-highlight', '');
1016 el.className = el.className + " dp-row-highlight";
1021 if(o.statusBar && statusText) { o.updateStatus(statusText); };
1023 onclick: function(e) {
1024 if(o.opacity != o.opacityTo || this.className.search(/date-picker-unused|out-of-range|day-disabled|no-selection/) != -1) return false;
1025 e = e || document.parentWindow.event;
1026 var el = e.target != null ? e.target : e.srcElement;
1027 while (el.nodeType != 1 || (el.tagName && el.tagName != "TD")) el = el.parentNode;
1028 var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
1029 o.date = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2));
1030 o.dateSet = new Date(o.date);
1031 o.returnFormattedDate();
1032 if(!o.staticPos) { o.hide(); }
1033 else { o.updateTable();};
1035 return stopEvent(e);
1037 incDec: function(e) {
1038 e = e || document.parentWindow.event;
1039 var el = e.target != null ? e.target : e.srcElement;
1040 if(el && el.className && el.className.search('fd-disabled') != -1) { return false; }
1042 o.dayInc = arguments[1];
1043 o.yearInc = arguments[2];
1044 o.monthInc = arguments[3];
1050 clearTimer: function(e) {
1056 removeEvent(document, "mouseup", o.events.clearTimer);
1060 this.setFormElementEvents = function() {
1061 var elem = this.getElem();
1062 if(elem && elem.tagName.search(/select|input/i) != -1) {
1063 addEvent(elem, "change", o.changeHandler);
1064 if(this.splitDate) {
1065 addEvent(document.getElementById(this.id + splitAppend[1]), "change", o.changeHandler);
1066 addEvent(document.getElementById(this.id + splitAppend[0]), "change", o.changeHandler);
1073 o.setFormElementEvents();
1075 if(this.staticPos) { this.create(); this.setDateFromInput(); }
1077 this.createButton();
1078 this.setDateFromInput();
1081 datePicker.prototype.createButton = function() {
1083 if(this.staticPos || document.getElementById("fd-but-" + this.id)) { return; };
1085 var inp = this.getElem(),
1086 span = document.createElement('span'),
1087 but = document.createElement('a');
1090 but.className = "date-picker-control";
1091 but.title = getTitleTranslation(5);
1092 but.id = "fd-but-" + this.id;
1094 span.appendChild(document.createTextNode(nbsp));
1095 but.appendChild(span);
1097 if(this.buttonWrapper && document.getElementById(this.buttonWrapper)) {
1098 document.getElementById(this.buttonWrapper).appendChild(but);
1099 } else if(inp.nextSibling) {
1100 inp.parentNode.insertBefore(but, inp.nextSibling);
1102 inp.parentNode.appendChild(but);
1105 but.onclick = but.onpress = function(e) {
1106 e = e || window.event;
1108 var inpId = this.id.replace('fd-but-',''),
1109 dpVisible = isVisible(inpId);
1111 if(e.type == "press") {
1112 var kc = e.keyCode != null ? e.keyCode : e.charCode;
1113 if(kc != 13) return true;
1115 this.className = this.className.replace("dp-button-active", "");
1116 datePickerController.hideAll();
1121 this.className = this.className.replace("dp-button-active", "");
1124 this.className += " dp-button-active";
1126 showDatePicker(inpId);
1136 datePicker.prototype.setRangeLow = function(range) {
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;
1138 if(this.created) { this.updateTable(); };
1140 datePicker.prototype.setRangeHigh = function(range) {
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;
1142 if(this.created) { this.updateTable(); };
1144 datePicker.prototype.setDisabledDays = function(dayArray) {
1145 this.disabledDays = dayArray;
1146 if(this.created) { this.updateTable(); };
1148 datePicker.prototype.setDisabledDates = function(dateArray) {
1149 this.disabledDates = {};
1150 this.addDisabledDates(dateArray);
1152 datePicker.prototype.addDisabledDates = function(dateArray) {
1153 var disabledDateObj = {};
1154 if(typeof dateArray !== "object") dateArray = [dateArray];
1155 for(var i = dateArray.length; i-- ;) {
1156 if(dateArray[i].match(/^(\d\d\d\d|\*\*\*\*)(0[1-9]|1[012]|\*\*)(0[1-9]|[12][0-9]|3[01])$/) != -1) {
1157 this.disabledDates[dateArray[i]] = 1;
1160 if(this.created) { this.updateTable(); };
1162 datePicker.prototype.addKeyboardEvents = function() {
1163 addEvent(document, "keypress", this.events.onkeydown);
1166 removeEvent(document, "keypress", this.events.onkeydown);
1167 addEvent(document, "keydown", this.events.onkeydown);
1170 if(window.devicePixelRatio) {
1171 removeEvent(document, "keypress", this.events.onkeydown);
1172 addEvent(document, "keydown", this.events.onkeydown);
1175 datePicker.prototype.removeKeyboardEvents = function() {
1176 removeEvent(document, "keypress", this.events.onkeydown);
1177 removeEvent(document, "keydown", this.events.onkeydown);
1179 datePicker.prototype.removeHighlight = function() {
1180 var el = document.getElementById(this.id+"-date-picker-hover");
1182 el.className = el.className.replace("date-picker-hover", "");
1186 datePicker.prototype.stopTimer = function() {
1187 this.timerSet = false;
1188 window.clearTimeout(this.timer);
1190 datePicker.prototype.setOpacity = function(op) {
1191 this.div.style.opacity = op/100;
1192 this.div.style.filter = 'alpha(opacity=' + op + ')';
1195 datePicker.prototype.getElem = function() {
1196 return document.getElementById(this.id.replace(/^fd-/, '')) || false;
1198 datePicker.prototype.getDisabledDates = function(y, m) {
1202 lower = this.firstDateShown,
1203 upper = this.lastDateShown,
1204 dt1, dt2, rngLower, rngUpper;
1206 if(!upper || !lower) {
1207 lower = this.firstDateShown = y + pad(m) + "01";
1208 upper = this.lastDateShown = y + pad(m) + pad(daysInMonth(m, y));
1211 for(var dt in this.disabledDates) {
1212 dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);
1213 dt2 = this.disabledDates[dt];
1216 if(+lower <= +dt1 && +upper >= +dt1) {
1222 // Range of disabled dates
1223 if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {
1225 if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {
1226 for(var i = dt1; i <= dt2; i++) {
1232 // Different months but we only want this month
1233 rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;
1234 rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;
1235 for(var i = +rngLower; i <= +rngUpper; i++) {
1241 for(dt in this.enabledDates) {
1242 dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);
1243 dt2 = this.enabledDates[dt];
1254 if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {
1256 if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {
1257 for(var i = dt1; i <= dt2; i++) {
1266 // Different months but we only want this month
1267 rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;
1268 rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;
1269 for(var i = +rngLower; i <= +rngUpper; i++) {
1279 datePicker.prototype.truePosition = function(element) {
1280 var pos = this.cumulativeOffset(element);
1281 if(window.opera) { return pos; };
1282 var iebody = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body,
1283 dsocleft = document.all ? iebody.scrollLeft : window.pageXOffset,
1284 dsoctop = document.all ? iebody.scrollTop : window.pageYOffset,
1285 posReal = this.realOffset(element);
1286 return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];
1288 datePicker.prototype.realOffset = function(element) {
1291 t += element.scrollTop || 0;
1292 l += element.scrollLeft || 0;
1293 element = element.parentNode;
1297 datePicker.prototype.cumulativeOffset = function(element) {
1300 t += element.offsetTop || 0;
1301 l += element.offsetLeft || 0;
1302 element = element.offsetParent;
1306 datePicker.prototype.equaliseDates = function() {
1307 var clearDayFound = false, tmpDate;
1308 for(var i = this.low; i <= this.high; i++) {
1309 tmpDate = String(i);
1310 if(!this.disabledDays[new Date(tmpDate.substr(0,4), tmpDate.substr(6,2), tmpDate.substr(4,2)).getDay() - 1]) {
1311 clearDayFound = true;
1315 if(!clearDayFound) { this.disabledDays = [0,0,0,0,0,0,0] };
1317 datePicker.prototype.outOfRange = function(tmpDate) {
1318 if(!this.low && !this.high) { return false; };
1323 tmpDate = this.date;
1326 var d = pad(tmpDate.getDate()),
1327 m = pad(tmpDate.getMonth() + 1),
1328 y = tmpDate.getFullYear(),
1329 dt = String(y)+String(m)+String(d);
1331 if(this.low && +dt < +this.low) {
1332 if(!level) return true;
1333 this.date = new Date(this.low.substr(0,4), this.low.substr(4,2)-1, this.low.substr(6,2), 5, 0, 0);
1336 if(this.high && +dt > +this.high) {
1337 if(!level) return true;
1338 this.date = new Date(this.high.substr(0,4), this.high.substr(4,2)-1, this.high.substr(6,2), 5, 0, 0);
1342 datePicker.prototype.updateStatus = function(msg) {
1343 while(this.statusBar.firstChild) { this.statusBar.removeChild(this.statusBar.firstChild); };
1344 if(msg && this.statusFormat.search(/-S|S-/) != -1) {
1345 msg = msg.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1<sup>$2</sup>");
1346 msg = msg.split(/<sup>|<\/sup>/);
1347 var dc = document.createDocumentFragment();
1348 for(var i = 0, nd; nd = msg[i]; i++) {
1349 if(/^(st|nd|rd|th)$/.test(nd)) {
1350 var sup = document.createElement("sup");
1351 sup.appendChild(document.createTextNode(nd));
1352 dc.appendChild(sup);
1354 dc.appendChild(document.createTextNode(nd));
1357 this.statusBar.appendChild(dc);
1359 this.statusBar.appendChild(document.createTextNode(msg ? msg : getTitleTranslation(9)));
1362 datePicker.prototype.setDateFromInput = function() {
1363 this.dateSet = null;
1365 var elem = this.getElem(),
1369 if(!elem || elem.tagName.search(/select|input/i) == -1) return;
1371 if(!this.splitDate && elem.value.replace(/\s/g, "") !== "") {
1372 var dynFormatMasks = formatMasks.concat([this.format]).reverse();
1373 for(var i = 0, fmt; fmt = dynFormatMasks[i]; i++) {
1374 dt = parseDateString(elem.value, fmt);
1380 } else if(this.splitDate) {
1381 var mmN = document.getElementById(this.id + splitAppend[1]),
1382 ddN = document.getElementById(this.id + splitAppend[0]),
1383 tm = parseInt(mmN.tagName.toLowerCase() == "input" ? mmN.value : mmN.options[mmN.selectedIndex || 0].value, 10),
1384 td = parseInt(ddN.tagName.toLowerCase() == "input" ? ddN.value : ddN.options[ddN.selectedIndex || 0].value, 10),
1385 ty = parseInt(elem.tagName.toLowerCase() == "input" ? elem.value : elem.options[elem.selectedIndex || 0].value, 10);
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))) {
1390 if(+td > daysInMonth(+tm - 1, +ty)) {
1392 td = daysInMonth(+tm - 1, +ty);
1393 dt = new Date(ty,tm-1,td);
1395 dt = new Date(ty,tm-1,td);
1400 if(!dt || isNaN(dt)) {
1401 this.date = new Date();
1402 this.date.setHours(5);
1408 this.date = new Date(dt);
1411 var dtYYYYMMDD = dt.getFullYear() + pad(dt.getMonth() + 1) + pad(dt.getDate()),
1412 weekDay = ( dt.getDay() + 6 ) % 7;
1414 if(dt.getTime() == this.date.getTime() && !(dtYYYYMMDD in this.dynDisabledDates || this.disabledDays[weekDay])) {
1415 this.dateSet = new Date(this.date);
1418 if(upd) { this.returnFormattedDate(); };
1420 datePicker.prototype.setSelectIndex = function(elem, indx) {
1421 for(var opt = elem.options.length-1; opt >= 0; opt--) {
1422 if(elem.options[opt].value == +indx) {
1423 elem.selectedIndex = opt;
1428 datePicker.prototype.returnFormattedDate = function() {
1430 var elem = this.getElem();
1433 var d = pad(this.date.getDate()),
1434 m = pad(this.date.getMonth() + 1),
1435 yyyy = this.date.getFullYear(),
1436 disabledDates = this.getDisabledDates(+yyyy, +m),
1437 weekDay = (this.date.getDay() + 6) % 7;
1439 if(!(this.disabledDays[weekDay] || String(yyyy)+m+d in this.disabledDates)) {
1440 if(this.splitDate) {
1441 var ddE = document.getElementById(this.id+splitAppend[0]),
1442 mmE = document.getElementById(this.id+splitAppend[1]);
1444 if(ddE.tagName.toLowerCase() == "input") { ddE.value = d; }
1445 else { this.setSelectIndex(ddE, d); };
1446 if(mmE.tagName.toLowerCase() == "input") { mmE.value = m; }
1447 else { this.setSelectIndex(mmE, m); };
1448 if(elem.tagName.toLowerCase() == "input") elem.value = yyyy;
1449 else { this.setSelectIndex(elem, yyyy); };
1451 } else if(elem.tagName.toLowerCase() == "input") {
1452 elem.value = printFormattedDate(this.date, this.format);
1455 if(elem.type && elem.type != "hidden") { elem.focus(); }
1457 this.callback("dateselect", { "id":this.id, "date":this.dateSet, "dd":d, "mm":m, "yyyy":yyyy });
1459 if(this.staticPos) { this.updateTable(); };
1462 datePicker.prototype.disableTodayButton = function() {
1463 var today = new Date();
1464 this.butToday.className = this.butToday.className.replace("fd-disabled", "");
1465 if(this.outOfRange(today) || (this.date.getDate() == today.getDate() && this.date.getMonth() == today.getMonth() && this.date.getFullYear() == today.getFullYear())) {
1466 this.butToday.className += " fd-disabled";
1467 this.butToday.onclick = null;
1469 this.butToday.onclick = this.events.gotoToday;
1472 datePicker.prototype.updateTableHeaders = function() {
1473 var colspanTotal = this.showWeeks ? 8 : 7,
1474 colOffset = this.showWeeks ? 1 : 0,
1477 for(var col = colOffset; col < colspanTotal; col++ ) {
1478 d = (this.firstDayOfWeek + (col - colOffset)) % 7;
1479 this.ths[col].title = getDayTranslation(d, false);
1481 if(col > colOffset) {
1482 but = this.ths[col].getElementsByTagName("span")[0];
1483 while(but.firstChild) { but.removeChild(but.firstChild); };
1484 but.appendChild(document.createTextNode(getDayTranslation(d, true)));
1485 but.title = this.ths[col].title;
1486 but.className = but.className.replace(/day-([0-6])/, "") + " day-" + d;
1489 while(this.ths[col].firstChild) { this.ths[col].removeChild(this.ths[col].firstChild); };
1490 this.ths[col].appendChild(document.createTextNode(getDayTranslation(d, true)));
1493 this.ths[col].className = this.ths[col].className.replace(/date-picker-highlight/g, "");
1494 if(this.highlightDays[d]) {
1495 this.ths[col].className += " date-picker-highlight";
1499 if(this.created) { this.updateTable(); }
1502 datePicker.prototype.callback = function(type, args) {
1503 if(!type || !(type in this.callbacks)) return false;
1506 for(var func = 0; func < this.callbacks[type].length; func++) {
1507 ret = this.callbacks[type][func](args || this.id);
1508 if(!ret) return false;
1513 datePicker.prototype.showHideButtons = function(tmpDate) {
1514 var tdm = tmpDate.getMonth(),
1515 tdy = tmpDate.getFullYear();
1517 this.butPrevYear.className = this.butPrevYear.className.replace("fd-disabled", "");
1518 if(this.outOfRange(new Date((tdy - 1), tdm, daysInMonth(+tdm, tdy-1)))) {
1519 this.butPrevYear.className += " fd-disabled";
1520 if(this.yearInc == -1) this.stopTimer();
1523 this.butPrevMonth.className = this.butPrevMonth.className.replace("fd-disabled", "");
1524 if(this.outOfRange(new Date(tdy, (+tdm - 1), daysInMonth(+tdm-1, tdy)))) {
1525 this.butPrevMonth.className += " fd-disabled";
1526 if(this.monthInc == -1) this.stopTimer();
1529 this.butNextYear.className = this.butNextYear.className.replace("fd-disabled", "");
1530 if(this.outOfRange(new Date((tdy + 1), +tdm, 1))) {
1531 this.butNextYear.className += " fd-disabled";
1532 if(this.yearInc == 1) this.stopTimer();
1535 this.butNextMonth.className = this.butNextMonth.className.replace("fd-disabled", "");
1536 if(this.outOfRange(new Date(tdy, +tdm + 1, 1))) {
1537 this.butNextMonth.className += " fd-disabled";
1538 if(this.monthInc == 1) this.stopTimer();
1541 var localeDefaults = {
1542 fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"],
1543 monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
1544 fullDays: ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],
1545 dayAbbrs: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],
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"],
1549 var grepRangeLimits = function(sel) {
1551 for(var i = 0; i < sel.options.length; i++) {
1552 if(sel.options[i].value.search(/^\d\d\d\d$/) == -1) { continue; };
1553 if(!range[0] || Number(sel.options[i].value) < range[0]) { range[0] = Number(sel.options[i].value); };
1554 if(!range[1] || Number(sel.options[i].value) > range[1]) { range[1] = Number(sel.options[i].value); };
1558 var joinNodeLists = function() {
1559 if(!arguments.length) { return []; }
1561 for (var i = 0; i < arguments.length; i++) {
1562 for (var j = 0, item; item = arguments[i][j]; j++) {
1563 nodeList[nodeList.length] = item;
1568 var cleanUp = function() {
1570 for(dp in datePickers) {
1571 if(!document.getElementById(datePickers[dp].id)) {
1572 if(datePickers[dp].created) datePickers[dp].destroy();
1573 datePickers[dp] = null;
1574 delete datePickers[dp];
1578 var hideAll = function(exception) {
1580 for(dp in datePickers) {
1581 if(!datePickers[dp].created || datePickers[dp].staticPos || (exception && exception == datePickers[dp].id)) continue;
1582 datePickers[dp].hide();
1585 var showDatePicker = function(inpID) {
1586 if(!(inpID in datePickers)) return false;
1587 datePickers[inpID].show();
1590 var destroy = function() {
1591 for(dp in datePickers) {
1592 if(datePickers[dp].created) datePickers[dp].destroy();
1593 datePickers[dp] = null;
1594 delete datePickers[dp];
1597 removeEvent(window, 'load', datePickerController.create);
1598 removeEvent(window, 'unload', datePickerController.destroy);
1600 var getTitleTranslation = function(num, replacements) {
1601 replacements = replacements || [];
1602 if(localeImport.titles.length > num) {
1603 var txt = localeImport.titles[num];
1604 if(replacements && replacements.length) {
1605 for(var i = 0; i < replacements.length; i++) {
1606 txt = txt.replace("[[%" + i + "%]]", replacements[i]);
1609 return txt.replace(/[[%(\d)%]]/g,"");
1613 var getDayTranslation = function(day, abbreviation) {
1614 var titles = localeImport[abbreviation ? "dayAbbrs" : "fullDays"];
1615 return titles.length && titles.length > day ? titles[day] : "";
1617 var getMonthTranslation = function(month, abbreviation) {
1618 var titles = localeImport[abbreviation ? "monthAbbrs" : "fullMonths"];
1619 return titles.length && titles.length > month ? titles[month] : "";
1621 var daysInMonth = function(nMonth, nYear) {
1622 nMonth = (nMonth + 12) % 12;
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];
1625 var getWeeksInYear = function(Y) {
1626 if(Y in weeksInYearCache) {
1627 return weeksInYearCache[Y];
1630 with (X1 = new Date(Y, 0, 4)) {
1631 setDate(getDate() - (6 + getDay()) % 7);
1633 with (X2 = new Date(Y, 11, 28)) {
1634 setDate(getDate() + (7 - getDay()) % 7);
1636 weeksInYearCache[Y] = Math.round((X2 - X1) / 604800000);
1637 return weeksInYearCache[Y];
1639 var parseRangeFromString = function(str) {
1642 var low = str.search(/^range-low-/) != -1;
1643 str = str.replace(/range-(low|high)-/, "");
1645 if(str.search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) != -1) { return str; };
1647 var tmpDate = new Date();
1649 if(str.search(/^today$/) != -1) { return tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate()); };
1651 var regExp = /^(\d)-(day|week|month|year)$/;
1653 if(str.search(regExp) != -1) {
1654 var parts = str.match(regExp),
1655 acc = { day:0,week:0,month:0,year:0 };
1657 acc[parts[2]] = low ? -(+parts[1]) : +parts[1];
1658 tmpDate.setFullYear(tmpDate.getFullYear() + +acc.year);
1659 tmpDate.setMonth(tmpDate.getMonth() + +acc.month);
1660 tmpDate.setDate(tmpDate.getDate() + +acc.day + (7 * +acc.week));
1661 return !tmpDate || isNaN(tmpDate) ? "" : tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate());
1666 var getWeekNumber = function(y,m,d) {
1667 var d = new Date(y, m, d, 0, 0, 0);
1668 var DoW = d.getDay();
1669 d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
1670 var ms = d.valueOf(); // GMT
1672 d.setDate(4); // Thu in Week 1
1673 return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
1675 var printFormattedDate = function(date, fmt, useImportedLocale) {
1676 if(!date || isNaN(date)) { return ""; };
1678 var parts = fmt.split("-"),
1682 m = date.getMonth(),
1683 y = date.getFullYear(),
1691 "D":useImportedLocale ? localeImport.dayAbbrs[D == 0 ? 6 : D - 1] : localeDefaults.dayAbbrs[D == 0 ? 6 : D - 1],
1692 "l":useImportedLocale ? localeImport.fullDays[D == 0 ? 6 : D - 1] : localeDefaults.fullDays[D == 0 ? 6 : D - 1],
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)],*/
1698 "W":getWeekNumber(date),
1699 "M":useImportedLocale ? localeImport.monthAbbrs[m] : localeDefaults.monthAbbrs[m],
1700 "F":useImportedLocale ? localeImport.fullMonths[m] : localeDefaults.fullMonths[m],
1703 "t":daysInMonth(++m, y),
1706 "y":String(y).substr(2,2),
1707 "S":["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
1710 for(var pt = 0, part; part = parts[pt]; pt++) {
1711 str.push(!(part in flags) ? "" : flags[part]);
1714 return str.join("");
1716 var parseDateString = function(str, fmt) {
1721 parts = fmt.replace(/-sp(-sp)+/g, "-sp").split("-"),
1722 divds = { "dt":".","sl":"/","ds":"-","cc":"," };
1725 for(var pt = 0, part; part = parts[pt]; pt++) {
1726 if(str.length == 0) { return false; };
1730 case "sp": // Space " "
1731 if(str.charAt(0).search(/\s/) != -1) {
1732 // Be easy on multiple spaces...
1733 while(str.charAt(0).search(/\s/) != -1) { str = str.substr(1); };
1740 if(str.charAt(0) == divds[part]) {
1741 str = str.substr(1);
1745 case "d": // Day of the month, 2 digits with leading zeros (01 - 31)
1746 case "j": // Day of the month without leading zeros (1 - 31)
1747 // Accept both when parsing
1748 if(str.search(/^(3[01]|[12][0-9]|0?[1-9])/) != -1) {
1749 d = +str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0];
1750 str = str.substr(str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0].length);
1754 case "D": // A textual representation of a day, three letters (Mon - Sun)
1755 case "l": // A full textual representation of the day of the week (Monday - Sunday)
1756 l = part == "D" ? localeDefaults.dayAbbrs : localeDefaults.fullDays;
1757 for(var i = 0; i < 7; i++) {
1758 if(new RegExp("^" + l[i], "i").test(str)) {
1759 str = str.substr(l[i].length);
1765 case "j": // Day of the month without leading zeros (1 - 31)
1766 if(str.search(/^([1-9]|[12][0-9]|3[01])/) != -1) {
1767 d = +str.match(/^([1-9]|[12][0-9]|3[01])/)[0];
1768 str = str.substr(str.match(/^(\s?[1-9]|[12][0-9]|3[01])/)[0].length);
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)
1773 case "w": // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
1774 if(str.search(part == "N" ? /^([1-7])/ : /^([0-6])/) != -1) {
1775 str = str.substr(1);
1778 case "S": // English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th
1779 if(str.search(/^(st|nd|rd|th)/i) != -1) {
1780 str = str.substr(2);
1783 case "z": // The day of the year (starting from 0): 0 - 365
1784 if(str.search(/^([0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-5])/) != -1) {
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);
1789 case "W": // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0): 1 - 53
1790 if(str.search(/^([1-9]|[1234[0-9]|5[0-3])/) != -1) {
1791 str = str.substr(str.match(/^([1-9]|[1234[0-9]|5[0-3])/)[0].length);
1795 case "M": // A short textual representation of a month, three letters
1796 case "F": // A full textual representation of a month, such as January or March
1797 l = localeDefaults.fullMonths.concat(localeDefaults.monthAbbrs); // : localeDefaults.fullMonths;
1798 for(var i = 0; i < 24; i++) {
1799 if(str.search(new RegExp("^" + l[i],"i")) != -1) {
1800 str = str.substr(l[i].length);
1801 m = ((i + 12) % 12);
1806 case "m": // Numeric representation of a month, with leading zeros
1807 case "n": // Numeric representation of a month, without leading zeros
1808 //l = part == "m" ? /^(0[1-9]|1[012])/ : /^([1-9]|1[012])/;
1809 // Accept either when parsing
1810 l = /^(1[012]|0?[1-9])/;
1811 if(str.search(l) != -1) {
1812 m = +str.match(l)[0] - 1;
1813 str = str.substr(str.match(l)[0].length);
1816 case "t": // Number of days in the given month: 28 through 31
1817 if(str.search(/2[89]|3[01]/) != -1) {
1818 str = str.substr(2);
1822 case "Y": // A full numeric representation of a year, 4 digits
1823 case "o": // ISO-8601 year number. This has the same value as Y
1824 if(str.search(/^(\d{4})/) != -1) {
1825 y = str.substr(0,4);
1826 str = str.substr(4);
1829 case "y": // A two digit representation of a year
1830 if(str.search(/^(0[0-9]|[1-9][0-9])/) != -1) {
1831 y = +str.substr(0,2);
1832 y = +y < 50 ? '20' + y : '19' + y;
1833 str = str.substr(2);
1841 d = d === false ? now.getDate() : d;
1842 m = m === false ? now.getMonth() - 1 : m;
1843 y = y === false ? now.getFullYear() : y;
1845 var tmpDate = new Date(y,m,d);
1846 return isNaN(tmpDate) ? "" : tmpDate;
1848 var repositionDatePickers = function(e) {
1849 for(dp in datePickers) {
1850 if(!datePickers[dp].created || datePickers[dp].staticPos || (!datePickers[dp].staticPos && !datePickers[dp].dragDisabled)) continue;
1851 datePickers[dp].reposition();
1854 var addDatePicker = function(options) {
1855 if(!options.id) { throw "A datePicker requires an associated element with an id attribute"; };
1856 if(options.id in datePickers) { return; };
1857 var elem = document.getElementById(options.id);
1858 if(!elem) throw "Cannot locate a datePicker's associated element with an id of:" + options.id;
1859 if(elem.tagName.search(/select|input/i) == -1) {
1860 if(!("callbacks" in options) || !("dateselect" in options.callbacks)) {
1861 throw "A 'dateselect' callback function is required for datePickers not associated with a form element";
1863 options.staticPos = true;
1864 options.splitDate = false;
1865 options.hideInput = false;
1866 options.noFadeEffect = true;
1867 options.dragDisabled = true;
1868 options.positioned = false;
1869 } else if(!options.staticPos) {
1870 options.hideInput = false;
1872 options.noFadeEffect = true;
1873 options.dragDisabled = true;
1876 datePickers[options.id] = new datePicker(options);
1878 var parseCallbacks = function(cbs) {
1879 if(cbs == null) { return {}; };
1885 for(var i = 0, fn; fn = cbs[i]; i++) {
1886 type = fn.match(/(cb_(dateselect|redraw|create)_)([^\s|$]+)/i)[1].replace(/^cb_/i, "").replace(/_$/, "");
1887 fn = fn.replace(/cb_(dateselect|redraw|create)_/i, "").replace(/-/g, ".");
1890 if(fn.indexOf(".") != -1) {
1891 parts = fn.split('.');
1893 for (var x = 0, part; part = obj[parts[x]]; x++) {
1894 if(part instanceof Function) {
1897 func = function (data) { method.apply(obj, [data]) };
1907 if(!(func instanceof Function)) continue;
1908 if(!(type in cbObj)) { cbObj[type] = []; };
1909 cbObj[type][cbObj[type].length] = func;
1914 // Used by the button to dictate whether to open or close the datePicker
1915 var isVisible = function(id) {
1916 return (!id || !(id in datePickers)) ? false : datePickers[id].visible;
1918 var create = function(inp) {
1919 if(!(typeof document.createElement != "undefined" && typeof document.documentElement != "undefined" && typeof document.documentElement.offsetWidth == "number")) { return; };
1921 // Has the locale file loaded?
1922 if(typeof(fdLocale) == "object" && !localeImport) {
1924 titles : fdLocale.titles,
1925 fullMonths : fdLocale.fullMonths,
1926 monthAbbrs : fdLocale.monthAbbrs,
1927 fullDays : fdLocale.fullDays,
1928 dayAbbrs : fdLocale.dayAbbrs,
1929 firstDayOfWeek : ("firstDayOfWeek" in fdLocale) ? fdLocale.firstDayOfWeek : 0,
1932 } else if(!localeImport) {
1933 localeImport = localeDefaults;
1936 var formElements = (inp && inp.tagName) ? [inp] : joinNodeLists(document.getElementsByTagName('input'), document.getElementsByTagName('select')),
1937 disableDays = /disable-days-([1-7]){1,6}/g,
1938 highlight = /highlight-days-([1-7]{1,7})/,
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))/,
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))/,
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])))+/,
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])))+/,
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,
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,
1945 callbacks = /((cb_(dateselect|redraw|create)_)([^\s|$]+))/ig,
1946 positioned = /display-inline-([^\s|$]+)/i,
1947 bPositioned = /button-([^\s|$]+)/i,
1948 range,tmp,j,t,options,dts,parts;
1950 for(var i = 0, elem; elem = formElements[i]; i++) {
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")) {
1953 if(elem.id && elem.id in datePickers) {
1954 if(!datePickers[elem.id].staticPos) { datePickers[elem.id].createButton(); }
1956 if(!document.getElementById("fd-" + elem.id)) {
1957 datePickers[elem.id].created = false;
1958 datePickers[elem.id].create();
1960 // Only do this if called from an ajax update etc
1961 datePickers[elem.id].setDateFromInput();
1962 datePickers[elem.id].updateTable();
1968 if(!elem.id) { elem.id = "fdDatePickerInput-" + uniqueId++; };
1974 format:"d-sl-m-sl-Y",
1976 highlightDays:[0,0,0,0,0,1,1],
1977 disabledDays:[0,0,0,0,0,0,0],
1980 noFadeEffect:elem.className.search(/no-animation/i) != -1,
1981 staticPos:elem.className.search(/display-inline/i) != -1,
1982 hideInput:elem.className.search(/hide-input/i) != -1,
1983 noToday:elem.className.search(/no-today-button/i) != -1,
1984 showWeeks:elem.className.search(/show-week/i) != -1,
1985 dragDisabled:nodrag ? true : elem.className.search(/disable-drag/i) != -1,
1987 firstDayOfWeek:localeImport.firstDayOfWeek,
1988 fillGrid:elem.className.search(/fill-grid/i) != -1,
1989 constrainSelection:elem.className.search(/fill-grid-no-select/i) != -1,
1990 callbacks:parseCallbacks(elem.className.match(callbacks)),
1994 // Positioning of static dp's
1995 if(options.staticPos && elem.className.search(positioned) != -1) {
1996 options.positioned = elem.className.match(positioned)[1];
1999 // Positioning of non-static dp's button
2000 if(!options.staticPos && elem.className.search(bPositioned) != -1) {
2001 options.buttonWrapper = elem.className.match(bPositioned)[1];
2004 // Opacity of non-static datePickers
2005 if(!options.staticPos) {
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
2010 dts = elem.className.match(disableDates);
2012 for(t = 0; t < dts.length; t++) {
2013 parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("disable-", "").split("-");
2014 options.disabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;
2019 dts = elem.className.match(enableDates);
2021 for(t = 0; t < dts.length; t++) {
2022 parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("enable-", "").split("-");
2023 options.enabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;
2027 // Split the date into three parts ?
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);
2031 if(!options.splitDate && elem.className.search(dateFormat) != -1) {
2032 options.format = elem.className.match(dateFormat)[0].replace('dateformat-','');
2035 // Status bar date format
2036 if(elem.className.search(statusFormat) != -1) {
2037 options.statusFormat = elem.className.match(statusFormat)[0].replace('statusformat-','');
2040 // The days of the week to highlight
2041 if(elem.className.search(highlight) != -1) {
2042 tmp = elem.className.match(highlight)[0].replace(/highlight-days-/, '');
2043 options.highlightDays = [0,0,0,0,0,0,0];
2044 for(j = 0; j < tmp.length; j++) {
2045 options.highlightDays[tmp.charAt(j) - 1] = 1;
2049 // The days of the week to disable
2050 if(elem.className.search(disableDays) != -1) {
2051 tmp = elem.className.match(disableDays)[0].replace(/disable-days-/, '');
2052 options.disabledDays = [0,0,0,0,0,0,0];
2053 for(j = 0; j < tmp.length; j++) {
2054 options.disabledDays[tmp.charAt(j) - 1] = 1;
2059 if(elem.className.search(rangeLow) != -1) {
2060 options.low = parseRangeFromString(elem.className.match(rangeLow)[0]);
2064 if(elem.className.search(rangeHigh) != -1) {
2065 options.high = parseRangeFromString(elem.className.match(rangeHigh)[0]);
2068 // Always round lower & higher limits if a selectList involved
2069 if(elem.tagName.search(/select/i) != -1) {
2070 range = grepRangeLimits(elem);
2071 options.low = options.low ? range[0] + String(options.low).substr(4,4) : range[0] + "0101";
2072 options.high = options.high ? range[1] + String(options.high).substr(4,4) : range[1] + "1231";
2075 addDatePicker(options);
2080 addEvent(window, 'load', create);
2081 addEvent(window, 'unload', destroy);
2082 addEvent(window, 'resize', repositionDatePickers);
2085 addEvent: function(obj, type, fn) { return addEvent(obj, type, fn); },
2086 removeEvent: function(obj, type, fn) { return removeEvent(obj, type, fn); },
2087 stopEvent: function(e) { return stopEvent(e); },
2088 show: function(inpID) { return showDatePicker(inpID); },
2089 create: function(inp) { create(inp); },
2090 repositionDatePickers: function() { repositionDatePickers(); },
2091 newDatePicker: function(opts) { addDatePicker(opts); },
2092 overrideAppendID: function(arr) { splitAppend = (arr && arr.length && arr.length == 2) ? arr : splitAppend },
2093 printFormattedDate: function(dt, fmt, useImportedLocale) { return printFormattedDate(dt, fmt, useImportedLocale); },
2094 setDateFromInput: function(inpID) { if(!inpID || !(inpID in datePickers) || !datePickers[inpID].created) return false; datePickers[inpID].setDateFromInput(); },
2095 setRangeLow: function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeLow(yyyymmdd); },
2096 setRangeHigh: function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeHigh(yyyymmdd); },
2097 parseDateString: function(str, format) { return parseDateString(str, format); },
2098 disableDrag: function() { noDrag = true; },
2099 setGlobalVars: function(json) { affectJSON(json); },
2100 addDisabledDates: function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addDisabledDates(dts); },
2101 setDisabledDates: function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDisabledDates(dts); }
2105 // Change this to use your own month & day id appendages
2106 // It can also be passed using JSON within the script tag
2107 // datePickerController.overrideAppendID(["Day", "Month"]);