PDA

View Full Version : DatePicker.js (misc)



corey.gilmore
4 Jan 2007, 9:11 AM
Some bugs, some features. This is more just a FYI for anyone looking to implement it (I've got an example posted at http://www.yui-ext.com/forum/viewtopic.php?p=6327).

I'm hoping to submit a revised version of DatePicker.js in two weeks when I've had a chance to sit down and work on it.

Right now the DatePicker constructer accepts a parentElement, but doesen't actually use it. Since it's a pop-up this isn't a big deal, but if you want to use it as an inline calendar (not a pop-up) this won't work. Also constrainXY wasn't working properly if you were positioning the calendar relative to a link at the bottom of a long scrolled page, where it would be offscreen.

Purely for reference here is the code I use in my apps to instantiate a pop-up calendar, and optionally an inline calendar. It's really hacky, hence my desire to sit down and contribute a solution :)

This is based on the 0.33 release, there are minor changes to the DatePicker code in 0.40, mostly CSS related.


var CalObj = function(opts) {
this.textfield = null;
this.datepicker = null; // YAHOO.ext.DatePicker object
this.calID = YAHOO.util.Dom.generateId(0, 'dpobj-dp-');
this.showDatePicker = null;

this.onSelectEvent = new YAHOO.util.CustomEvent("onSelect", this);

this.onSelectEvent.subscribe(this.setTextfield);

this.config = {
textfield: null, // id/HTML Element
showPickerOnFocus: false, // show the picker when the textfield is focused
outputDateFmt: 'm/d/Y',
showPicker: null,
parent:false,
inlinePicker:false,
twoDigitYearCutoff: 1980 // null to ignore, otherwise dates before this have 100 years added to them (1904 becomes 2004).
};

// initialize our config
if( typeof opts != 'undefined' ) {
for( var c in this.config) {
if( typeof opts[c] != "undefined" ) {
this.config[c] = opts[c];
}
}
}

}
CalObj.prototype.init = function() {
if( this.config.showPicker ) {
this.showDatePicker = getEl(this.config.showPicker);
// add our click handler to show the link
this.showDatePicker.on('click', this.show, this, true);
}
if( this.config.textfield ) {
this.textfield = getEl(this.config.textfield);
if( this.config.showPickerOnFocus ) {
this.textfield.set({autocomplete:'off'}); // work around firefox errors
this.textfield.on('focus', this.show, this, true);
}
}
if( this.config.inlinePicker ) {
var _this = this; // scope our callback appropriately
this.datepicker = new YAHOO.ext.InlineDatePicker(this.calID, this.config.parent, function() { _this.onSelectEventFire(_this); } );
} else {
this.datepicker = new YAHOO.ext.DatePicker(this.calID, this.config.parent);
}
}
CalObj.prototype.getSelectedDate = function() {
return this.datepicker.getSelectedDate();
}
CalObj.prototype.show = function() {
var selectedDate = new Date();
var _this = this; // scope our callback appropriately
var r = { left:0, bottom:0 };

if( this.textfield && this.config.showPickerOnFocus ) {
r = this.textfield.getRegion();
} else if( this.showDatePicker ) {
r = this.showDatePicker.getRegion();
}

if( this.textfield && this.textfield.dom.value != '' ) {
selectedDate = Date.parse(this.textfield.dom.value);
if( isNaN(selectedDate) ) {
selectedDate = new Date();
} else {
selectedDate = new Date(selectedDate);
if( this.config.twoDigitYearCutoff && selectedDate.getFullYear() <= this.config.twoDigitYearCutoff ) {
selectedDate.setFullYear(selectedDate.getFullYear()+100);
}
}
}
this.datepicker.show(r.left, r.bottom, selectedDate, function() { _this.onSelectEventFire(_this); } );
}
CalObj.prototype.onSelectEventFire = function() {
this.onSelectEvent.fire();
}

CalObj.prototype.setTextfield = function() {
var selDate = this.getSelectedDate();
if( this.textfield ) {
this.textfield.dom.value = selDate.dateFormat(this.config.outputDateFmt);
}
}

YAHOO.ext.DatePicker.prototype.constrainXY = function(x, y) {
var w = YAHOO.util.Dom.getViewportWidth();
var h = YAHOO.util.Dom.getViewportHeight();
var size = this.element.getSize();
var mx = x;
var my = y;
if( size.width + x > w ) {
mx = w - size.width;
}
if( size.height + y > h ) {
my = y - size.height;
}
return [ mx, my ];
}

// override the base picker to show it inline
YAHOO.ext.InlineDatePicker = function(id, parentElement, callback){
this.id = id;
this.selectedDate = new Date();
this.visibleDate = new Date();
this.element = null;
this.shadow = null;
this.callback = callback;
this.buildControl(parentElement);
this.refresh();
};
YAHOO.ext.InlineDatePicker.prototype.buildControl = function(parentElement){
var c = document.createElement('div');
parentElement = $(parentElement);
parentElement.appendChild(c);
var html = '<div class="ypopcal" id="'+this.id+'" style="-moz-outline:none;">' +
'<table class="ypopcal-head" border=0 cellpadding=0 cellspacing=0><tbody><tr><td class="ypopcal-arrow"><div class="prev-month"> </div></td><td class="ypopcal-month"> </td><td class="ypopcal-arrow"><div class="next-month"> </div></td></tr></tbody></table>' +
'<center><div class="ypopcal-inner">';
html += "<table border=0 cellpadding=2 cellspacing=0 class=\"ypopcal-table\"><thead><tr class=\"ypopcal-daynames\">";
var names = this.dayNames;
for(var i = 0; i < names.length; i++){
html += '<td>' + names[i].substr(0, 1) + '</td>';
}
html+= "</tr></thead><tbody><tr>";
for(var i = 0; i < 42; i++) {
if(i % 7 == 0 && i != 0){
html += '</tr><tr>';
}
html += "<td></td>";
}
html += "</tr></tbody></table>";
html += '</div><button class="ypopcal-today" style="margin-top:2px;">'+this.todayText+'</button></center></div>';
c.innerHTML = html;
this.element = getEl(c.childNodes[0], true);
parentElement.appendChild(this.element.dom);
parentElement.removeChild(c);
this.element.on("selectstart", function(){return false;});
var tbody = this.element.dom.getElementsByTagName('tbody')[1];
this.cells = tbody.getElementsByTagName('td');
this.calHead = this.element.getChildrenByClassName('ypopcal-month', 'td')[0];
this.element.mon('mousedown', this.handleClick, this, true);
};
YAHOO.ext.InlineDatePicker.prototype.refresh = function(){
var d = this.visibleDate;
this.buildInnerCal(d);
this.calHead.update(this.monthNames[d.getMonth()] + ' ' + d.getFullYear());
}
YAHOO.ext.InlineDatePicker.prototype.show = function(x, y, value, callback) {
this.selectedDate = value;
this.visibleDate = value;
this.callback = callback;
this.refresh();
}
YAHOO.ext.InlineDatePicker.prototype.hide = function() { }

YAHOO.augment(YAHOO.ext.InlineDatePicker, YAHOO.ext.DatePicker);

jack.slocum
5 Jan 2007, 9:16 AM
The whole control needs a refactor. It was written almost 8 years ago with some updates here and there. :) The refactor is coming soon, it will be part of the forms lib.

corey.gilmore
5 Jan 2007, 9:28 AM
The whole control needs a refactor. It was written almost 8 years ago with some updates here and there. :) The refactor is coming soon, it will be part of the forms lib.

Sounds like an excuse to wait to me :)

hcristea
17 Jan 2007, 9:04 AM
The way the datepicker is constructed causes a bug when you try to localize the calendar.
The method buildControl of the DatePicker is called in the constructor and uses the dayNames from the default (Date.dayNames).

Changing the property dayNames of the DatePicker object after it is initialized doesnt affect the dayNames from the calendar header. they remain always the same (S, M, T, W, T, F, S ie. .substr(0, 1) from the default english days).

Currently without modifying the code in yui-ext.js you cannot set the dayNames for the Calendar. Please have this in mind when you do the refactor.

jack.slocum
20 Jan 2007, 6:01 AM
To localize, you would modify them on the prototype, before the DP is used:

Ext.DatePicker.prototype.dayNames = ['Lunes', .... 'Sabado'];

ter
30 Jan 2007, 5:15 AM
But if week begins not with sunday?

hcristea
1 Feb 2007, 3:32 AM
To localize, you would modify them on the prototype, before the DP is used:

Ext.DatePicker.prototype.dayNames = ['Lunes', .... 'Sabado'];

Hi Jack,

yes, i tried that but it doesn't affect the days initials in the "Calendar". when the calendar is build i remember that you take the first letter from the day names. that constructor is called before my line that modify the prototype.

(sorry i don't have the code handy to look and tell you specifically why i think its a bug this. I'll come back with a post later).

changing the prototype of the monthNames works. maybe changing the prototype of the dayNames will localize the data in a "verbose" format but it doesn't affect the INITIALS of the days in the calendar.

forgot to say that this behaviour is happening on 0.33 version

hcristea
1 Feb 2007, 4:37 AM
To localize, you would modify them on the prototype, before the DP is used:

Ext.DatePicker.prototype.dayNames = ['Lunes', .... 'Sabado'];


hi jack,

you are right, i got to modify the prototype on the DP BEFORE is used. This works and this is what i'm currently doing. But i think it shouldn't work like this.

i've got the code now and i got more specific information.
I'll explain this "bug" with some code examples.

I implemented a very basic DateTimeEditor which has a input field type=text and on focus will show the DatePicker.
So the code looks like this: (i will show only the specific lines)


YAHOO.cw.DateTimeEditor = function(el, config){
....
this.cal = null;
....
};

YAHOO.cw.DateTimeEditor.prototype = {
init : function(){ ... },

initEvents: function(){ ... },

.....,

showCalendar : function(){
if(this.cal == null){
YAHOO.ext.DatePicker.prototype.dayNames = this.dayNames;
this.cal = new YAHOO.ext.DatePicker(YAHOO.util.Dom.generateId());
}
this.cal.monthNames = this.monthNames;
//this.cal.dayNames = this.dayNames;

....
var r = this.element.getRegion();
this.cal.show(r.left, r.bottom, this.getValue() || new Date(), this.setValue.createDelegate(this));
},

.....
};

YAHOO.cw.DateTimeEditor.prototype.monthNames = ["Januar", .... "Dezember"];
YAHOO.cw.DateTimeEditor.prototype.dayNames = ["Sonntag", ...., "Samstag"];


To localize the dayNames i use YAHOO.ext.DatePicker.prototype.dayNames = this.dayNames; before i instantiate the DP.
However i can localize the monthNames after i have an instance of DP and before calling the this.cal.show() method.

When the show() method is called the method refresh() is called before the actual calendar is displayed. the refresh() method is updating the calHeader with the proper month name which is localized when do this.cal.monthNames = this.monthNames; in my code above.

I'm saying that also to localize the dayNames to be done with this.cal.dayNames = this.dayNames; which now has no effect on the actual calendar.

maybe the DP.refresh() method should do more like updating also the day Initials in the DP control and also the Today text.

what do you think?