A simple GUI for choosing a date in the context of a monthly calendar.
description
A simple GUI for choosing a date in the context of a monthly calendar.
This widget can't be used in a form because it doesn't serialize the date to an
`<input>` field. For a form element, use dijit.form.DateTextBox instead.
Note that the parser takes all dates attributes passed in the
[RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00`
so that they are serializable and locale-independent.
example
var calendar = new dijit.Calendar({}, dojo.byId("calendarNode"));
The currently selected Date, initially set to invalid date to indicate no selection.
TODO: for 2.0 make this a string (ISO format) rather than a Date
dijit.Calendar.datePackage
type
String
summary
JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines
at dojo.date by default.
dijit.Calendar.dayWidth
type
String
summary
How to represent the days of the week in the calendar header. See dojo.date.locale
dijit.Calendar.tabIndex
type
Integer
summary
Order fields are traversed when user hits the tab key
dijit.Calendar.currentFocus
type
Date
summary
Date object containing the currently focused date, or the date which would be focused
if the calendar itself was focused. Also indicates which year and month to display,
i.e. the current "page" the calendar is on.
dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], { // summary: // A simple GUI for choosing a date in the context of a monthly calendar. // // description: // A simple GUI for choosing a date in the context of a monthly calendar. // This widget can't be used in a form because it doesn't serialize the date to an // `` field. For a form element, use dijit.form.DateTextBox instead. // // Note that the parser takes all dates attributes passed in the // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00` // so that they are serializable and locale-independent. // // example: // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode")); // // example: // |
// value: Date // The currently selected Date, initially set to invalid date to indicate no selection. value: new Date(""), // TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines // at dojo.date by default. datePackage: "dojo.date",
// dayWidth: String // How to represent the days of the week in the calendar header. See dojo.date.locale dayWidth: "narrow",
// tabIndex: Integer // Order fields are traversed when user hits the tab key tabIndex: "0",
// currentFocus: Date // Date object containing the currently focused date, or the date which would be focused // if the calendar itself was focused. Also indicates which year and month to display, // i.e. the current "page" the calendar is on. currentFocus: new Date(),
baseClass:"dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details cssStateNodes: { "decrementMonth": "dijitCalendarArrow", "incrementMonth": "dijitCalendarArrow", "previousYearLabelNode": "dijitCalendarPreviousYear", "nextYearLabelNode": "dijitCalendarNextYear" },
_isValidDate: function(/*Date*/ value){ // summary: // Runs various tests on the value, checking that it's a valid date, rather // than blank or NaN. // tags: // private return value && !isNaN(value) && typeof value == "object" && value.toString() != this.constructor.prototype.value.toString(); },
setValue: function(/*Date*/ value){ // summary: // Deprecated. Use set('value', ...) instead. // tags: // deprecated dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); this.set('value', value); },
_getValueAttr: function(){ // summary: // Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat var value = new this.dateClassObj(this.value); value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date // object to point at 1am so it will represent the correct day. See #9366 if(value.getDate() < this.value.getDate()){ value = this.dateFuncObj.add(value, "hour", 1); } return value;
summary
dijit.Calendar._setValueAttr
type
Function
parameters:
value: (typeof Date|Number)
Either a Date or the number of seconds since 1970.
if(value){ // convert from Number to Date, or make copy of Date object so that setHours() call below // doesn't affect original value value = new this.dateClassObj(value); } if(this._isValidDate(value)){ if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){ value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
// Set focus cell to the new value. Arguably this should only happen when there isn't a current // focus point. This will also repopulate the grid, showing the new selected value (and possibly // new month/year). this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){ this.onChange(this.get('value')); this.onValueSelected(this.get('value')); // remove in 2.0 } } } }else{ // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus this._set("value", null); this.set("currentFocus", this.currentFocus); }
summary
Support set("value", ...)
description
Set the current date and update the UI. If the date is disabled, the value will
not change, but the display will change to the corresponding month.
// Iterate through dates in the calendar and fill in date numbers and style info dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){ i += dayOffset; var date = new this.dateClassObj(month), number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){ number = daysInPreviousMonth - firstDay + i + 1; adj = -1; clazz += "Previous"; }else if(i >= (firstDay + daysInMonth)){ number = i - firstDay - daysInMonth + 1; adj = 1; clazz += "Next"; }else{ number = i - firstDay + 1; clazz += "Current"; }
if(adj){ date = this.dateFuncObj.add(date, "month", adj); } date.setDate(number);
template.className = clazz + "Month dijitCalendarDateTemplate"; template.dijitDateValue = date.valueOf(); // original code dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it var label = dojo.query(".dijitCalendarDateLabel", template)[0], text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate(); this._setText(label, text); }, this);
// Repopulate month drop down list based on current year. // Need to do this to hide leap months in Hebrew calendar. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month); this.monthDropDownButton.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names // (invisible) so that the maximum width will affect layout. But not on IE6 because then // the center
overlaps the right
(due to a browser bug). this.monthDropDownButton.containerNode.innerHTML = (dojo.isIE == 6 ? "" : "
// Fill in localized prev/current/next years var y = month.getFullYear() - 1; var d = new this.dateClassObj(); dojo.forEach(["previous", "current", "next"], function(name){ d.setFullYear(y++); this._setText(this[name+"YearLabelNode"], this.dateLocaleModule.format(d, {selector:'year', locale:this.lang})); }, this);
dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], { // summary: // A simple GUI for choosing a date in the context of a monthly calendar. // // description: // A simple GUI for choosing a date in the context of a monthly calendar. // This widget can't be used in a form because it doesn't serialize the date to an // `` field. For a form element, use dijit.form.DateTextBox instead. // // Note that the parser takes all dates attributes passed in the // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00` // so that they are serializable and locale-independent. // // example: // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode")); // // example: // |
// value: Date // The currently selected Date, initially set to invalid date to indicate no selection. value: new Date(""), // TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines // at dojo.date by default. datePackage: "dojo.date",
// dayWidth: String // How to represent the days of the week in the calendar header. See dojo.date.locale dayWidth: "narrow",
// tabIndex: Integer // Order fields are traversed when user hits the tab key tabIndex: "0",
// currentFocus: Date // Date object containing the currently focused date, or the date which would be focused // if the calendar itself was focused. Also indicates which year and month to display, // i.e. the current "page" the calendar is on. currentFocus: new Date(),
baseClass:"dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details cssStateNodes: { "decrementMonth": "dijitCalendarArrow", "incrementMonth": "dijitCalendarArrow", "previousYearLabelNode": "dijitCalendarPreviousYear", "nextYearLabelNode": "dijitCalendarNextYear" },
_isValidDate: function(/*Date*/ value){ // summary: // Runs various tests on the value, checking that it's a valid date, rather // than blank or NaN. // tags: // private return value && !isNaN(value) && typeof value == "object" && value.toString() != this.constructor.prototype.value.toString(); },
setValue: function(/*Date*/ value){ // summary: // Deprecated. Use set('value', ...) instead. // tags: // deprecated dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); this.set('value', value); },
_getValueAttr: function(){ // summary: // Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat var value = new this.dateClassObj(this.value); value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date // object to point at 1am so it will represent the correct day. See #9366 if(value.getDate() < this.value.getDate()){ value = this.dateFuncObj.add(value, "hour", 1); } return value; },
_setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){ // summary: // Support set("value", ...) // description: // Set the current date and update the UI. If the date is disabled, the value will // not change, but the display will change to the corresponding month. // value: // Either a Date or the number of seconds since 1970. // tags: // protected if(value){ // convert from Number to Date, or make copy of Date object so that setHours() call below // doesn't affect original value value = new this.dateClassObj(value); } if(this._isValidDate(value)){ if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){ value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
// Set focus cell to the new value. Arguably this should only happen when there isn't a current // focus point. This will also repopulate the grid, showing the new selected value (and possibly // new month/year). this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){ this.onChange(this.get('value')); this.onValueSelected(this.get('value')); // remove in 2.0 } } } }else{ // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus this._set("value", null); this.set("currentFocus", this.currentFocus); } },
_setText: function(node, text){ // summary: // This just sets the content of node to the specified text. // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434. // tags: // private while(node.firstChild){ node.removeChild(node.firstChild); } node.appendChild(dojo.doc.createTextNode(text)); },
_populateGrid: function(){ // summary: // Fills in the calendar grid with each day (1-31) // tags: // private
var month = new this.dateClassObj(this.currentFocus); month.setDate(1);
// Iterate through dates in the calendar and fill in date numbers and style info dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){ i += dayOffset; var date = new this.dateClassObj(month), number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){ number = daysInPreviousMonth - firstDay + i + 1; adj = -1; clazz += "Previous"; }else if(i >= (firstDay + daysInMonth)){ number = i - firstDay - daysInMonth + 1; adj = 1; clazz += "Next"; }else{ number = i - firstDay + 1; clazz += "Current"; }
if(adj){ date = this.dateFuncObj.add(date, "month", adj); } date.setDate(number);
template.className = clazz + "Month dijitCalendarDateTemplate"; template.dijitDateValue = date.valueOf(); // original code dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it var label = dojo.query(".dijitCalendarDateLabel", template)[0], text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate(); this._setText(label, text); }, this);
// Repopulate month drop down list based on current year. // Need to do this to hide leap months in Hebrew calendar. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month); this.monthDropDownButton.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names // (invisible) so that the maximum width will affect layout. But not on IE6 because then // the center
overlaps the right
(due to a browser bug). this.monthDropDownButton.containerNode.innerHTML = (dojo.isIE == 6 ? "" : "
// Fill in localized prev/current/next years var y = month.getFullYear() - 1; var d = new this.dateClassObj(); dojo.forEach(["previous", "current", "next"], function(name){ d.setFullYear(y++); this._setText(this[name+"YearLabelNode"], this.dateLocaleModule.format(d, {selector:'year', locale:this.lang})); }, this); },
goToToday: function(){ // summary: // Sets calendar's value to today's date this.set('value', new this.dateClassObj()); },
postMixInProperties: function(){ // Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead. // TODO: remove this for 2.0 (thanks to #11511) if(isNaN(this.value)){ delete this.value; }
var cloneClass = dojo.hitch(this, function(clazz, n){ var template = dojo.query(clazz, this.domNode)[0]; for(var i=0; i template.parentNode.appendChild(template.cloneNode(true)); } });
// clone the day label and calendar day templates 6 times to make 7 columns cloneClass(".dijitCalendarDayLabelTemplate", 6); cloneClass(".dijitCalendarDateTemplate", 6);
// now make 6 week rows cloneClass(".dijitCalendarWeekTemplate", 5);
// insert localized day names in the header var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang); var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang); dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){ this._setText(label, dayNames[(i + dayOffset) % 7]); }, this);
var dateObj = new this.dateClassObj(this.currentFocus);
// round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366) date = new this.dateClassObj(date); date.setHours(1, 0, 0, 0);
this._set("currentFocus", date);
// TODO: only re-populate grid when month/year has changed this._populateGrid();
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused) var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0]; newCell.setAttribute("tabIndex", this.tabIndex); if(this._focused || forceFocus){ newCell.focus(); }
// set tabIndex=-1 on old focusable cell if(oldCell && oldCell != newCell){ if(dojo.isWebKit){ // see #11064 about webkit bug oldCell.setAttribute("tabIndex", "-1"); }else{ oldCell.removeAttribute("tabIndex"); } }
summary
If the calendar currently has focus, then focuses specified date,
changing the currently displayed month/year if necessary.
If the calendar doesn't have focus, updates currently
displayed month/year, and sets the cell that will get focus.
dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], { // summary: // A simple GUI for choosing a date in the context of a monthly calendar. // // description: // A simple GUI for choosing a date in the context of a monthly calendar. // This widget can't be used in a form because it doesn't serialize the date to an // `` field. For a form element, use dijit.form.DateTextBox instead. // // Note that the parser takes all dates attributes passed in the // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00` // so that they are serializable and locale-independent. // // example: // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode")); // // example: // |
// value: Date // The currently selected Date, initially set to invalid date to indicate no selection. value: new Date(""), // TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines // at dojo.date by default. datePackage: "dojo.date",
// dayWidth: String // How to represent the days of the week in the calendar header. See dojo.date.locale dayWidth: "narrow",
// tabIndex: Integer // Order fields are traversed when user hits the tab key tabIndex: "0",
// currentFocus: Date // Date object containing the currently focused date, or the date which would be focused // if the calendar itself was focused. Also indicates which year and month to display, // i.e. the current "page" the calendar is on. currentFocus: new Date(),
baseClass:"dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details cssStateNodes: { "decrementMonth": "dijitCalendarArrow", "incrementMonth": "dijitCalendarArrow", "previousYearLabelNode": "dijitCalendarPreviousYear", "nextYearLabelNode": "dijitCalendarNextYear" },
_isValidDate: function(/*Date*/ value){ // summary: // Runs various tests on the value, checking that it's a valid date, rather // than blank or NaN. // tags: // private return value && !isNaN(value) && typeof value == "object" && value.toString() != this.constructor.prototype.value.toString(); },
setValue: function(/*Date*/ value){ // summary: // Deprecated. Use set('value', ...) instead. // tags: // deprecated dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); this.set('value', value); },
_getValueAttr: function(){ // summary: // Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat var value = new this.dateClassObj(this.value); value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date // object to point at 1am so it will represent the correct day. See #9366 if(value.getDate() < this.value.getDate()){ value = this.dateFuncObj.add(value, "hour", 1); } return value; },
_setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){ // summary: // Support set("value", ...) // description: // Set the current date and update the UI. If the date is disabled, the value will // not change, but the display will change to the corresponding month. // value: // Either a Date or the number of seconds since 1970. // tags: // protected if(value){ // convert from Number to Date, or make copy of Date object so that setHours() call below // doesn't affect original value value = new this.dateClassObj(value); } if(this._isValidDate(value)){ if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){ value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
// Set focus cell to the new value. Arguably this should only happen when there isn't a current // focus point. This will also repopulate the grid, showing the new selected value (and possibly // new month/year). this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){ this.onChange(this.get('value')); this.onValueSelected(this.get('value')); // remove in 2.0 } } } }else{ // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus this._set("value", null); this.set("currentFocus", this.currentFocus); } },
_setText: function(node, text){ // summary: // This just sets the content of node to the specified text. // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434. // tags: // private while(node.firstChild){ node.removeChild(node.firstChild); } node.appendChild(dojo.doc.createTextNode(text)); },
_populateGrid: function(){ // summary: // Fills in the calendar grid with each day (1-31) // tags: // private
var month = new this.dateClassObj(this.currentFocus); month.setDate(1);
// Iterate through dates in the calendar and fill in date numbers and style info dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){ i += dayOffset; var date = new this.dateClassObj(month), number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){ number = daysInPreviousMonth - firstDay + i + 1; adj = -1; clazz += "Previous"; }else if(i >= (firstDay + daysInMonth)){ number = i - firstDay - daysInMonth + 1; adj = 1; clazz += "Next"; }else{ number = i - firstDay + 1; clazz += "Current"; }
if(adj){ date = this.dateFuncObj.add(date, "month", adj); } date.setDate(number);
template.className = clazz + "Month dijitCalendarDateTemplate"; template.dijitDateValue = date.valueOf(); // original code dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it var label = dojo.query(".dijitCalendarDateLabel", template)[0], text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate(); this._setText(label, text); }, this);
// Repopulate month drop down list based on current year. // Need to do this to hide leap months in Hebrew calendar. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month); this.monthDropDownButton.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names // (invisible) so that the maximum width will affect layout. But not on IE6 because then // the center
overlaps the right
(due to a browser bug). this.monthDropDownButton.containerNode.innerHTML = (dojo.isIE == 6 ? "" : "
// Fill in localized prev/current/next years var y = month.getFullYear() - 1; var d = new this.dateClassObj(); dojo.forEach(["previous", "current", "next"], function(name){ d.setFullYear(y++); this._setText(this[name+"YearLabelNode"], this.dateLocaleModule.format(d, {selector:'year', locale:this.lang})); }, this); },
goToToday: function(){ // summary: // Sets calendar's value to today's date this.set('value', new this.dateClassObj()); },
postMixInProperties: function(){ // Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead. // TODO: remove this for 2.0 (thanks to #11511) if(isNaN(this.value)){ delete this.value; }
var cloneClass = dojo.hitch(this, function(clazz, n){ var template = dojo.query(clazz, this.domNode)[0]; for(var i=0; i template.parentNode.appendChild(template.cloneNode(true)); } });
// clone the day label and calendar day templates 6 times to make 7 columns cloneClass(".dijitCalendarDayLabelTemplate", 6); cloneClass(".dijitCalendarDateTemplate", 6);
// now make 6 week rows cloneClass(".dijitCalendarWeekTemplate", 5);
// insert localized day names in the header var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang); var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang); dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){ this._setText(label, dayNames[(i + dayOffset) % 7]); }, this);
var dateObj = new this.dateClassObj(this.currentFocus);
this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
// Set up repeating mouse behavior for increment/decrement of months/years var _this = this; var typematic = function(nodeProp, dateProp, adj){ _this._connects.push( dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){ if(count >= 0){ _this._adjustDisplay(dateProp, adj); } }, 0.8, 500) ); }; typematic("incrementMonth", "month", 1); typematic("decrementMonth", "month", -1); typematic("nextYearLabelNode", "year", 1); typematic("previousYearLabelNode", "year", -1); },
_adjustDisplay: function(/*String*/ part, /*int*/ amount){ // summary: // Moves calendar forwards or backwards by months or years // part: // "month" or "year" // amount: // Number of months or years // tags: // private this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount)); },
_setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){ // summary: // If the calendar currently has focus, then focuses specified date, // changing the currently displayed month/year if necessary. // If the calendar doesn't have focus, updates currently // displayed month/year, and sets the cell that will get focus. // forceFocus: // If true, will focus() the cell even if calendar itself doesn't have focus
// round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366) date = new this.dateClassObj(date); date.setHours(1, 0, 0, 0);
this._set("currentFocus", date);
// TODO: only re-populate grid when month/year has changed this._populateGrid();
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused) var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0]; newCell.setAttribute("tabIndex", this.tabIndex); if(this._focused || forceFocus){ newCell.focus(); }
// set tabIndex=-1 on old focusable cell if(oldCell && oldCell != newCell){ if(dojo.isWebKit){ // see #11064 about webkit bug oldCell.setAttribute("tabIndex", "-1"); }else{ oldCell.removeAttribute("tabIndex"); } } },
focus: function(){ // summary: // Focus the calendar by focusing one of the calendar cells this._setCurrentFocusAttr(this.currentFocus, true); },
_onMonthSelect: function(/*Number*/ newMonth){ // summary: // Handler for when user selects a month from the drop down list // tags: // protected
// move to selected month, bounding by the number of days in the month // (ex: dec 31 --> jan 28, not jan 31) this.currentFocus = this.dateFuncObj.add(this.currentFocus, "month", newMonth - this.currentFocus.getMonth()); this._populateGrid();
summary
Handler for when user selects a month from the drop down list
dojo.declare( "dijit.Calendar", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], { // summary: // A simple GUI for choosing a date in the context of a monthly calendar. // // description: // A simple GUI for choosing a date in the context of a monthly calendar. // This widget can't be used in a form because it doesn't serialize the date to an // `` field. For a form element, use dijit.form.DateTextBox instead. // // Note that the parser takes all dates attributes passed in the // [RFC 3339 format](http://www.faqs.org/rfcs/rfc3339.html), e.g. `2005-06-30T08:05:00-07:00` // so that they are serializable and locale-independent. // // example: // | var calendar = new dijit.Calendar({}, dojo.byId("calendarNode")); // // example: // |
// value: Date // The currently selected Date, initially set to invalid date to indicate no selection. value: new Date(""), // TODO: for 2.0 make this a string (ISO format) rather than a Date
// datePackage: String // JavaScript namespace to find Calendar routines. Uses Gregorian Calendar routines // at dojo.date by default. datePackage: "dojo.date",
// dayWidth: String // How to represent the days of the week in the calendar header. See dojo.date.locale dayWidth: "narrow",
// tabIndex: Integer // Order fields are traversed when user hits the tab key tabIndex: "0",
// currentFocus: Date // Date object containing the currently focused date, or the date which would be focused // if the calendar itself was focused. Also indicates which year and month to display, // i.e. the current "page" the calendar is on. currentFocus: new Date(),
baseClass:"dijitCalendar",
// Set node classes for various mouse events, see dijit._CssStateMixin for more details cssStateNodes: { "decrementMonth": "dijitCalendarArrow", "incrementMonth": "dijitCalendarArrow", "previousYearLabelNode": "dijitCalendarPreviousYear", "nextYearLabelNode": "dijitCalendarNextYear" },
_isValidDate: function(/*Date*/ value){ // summary: // Runs various tests on the value, checking that it's a valid date, rather // than blank or NaN. // tags: // private return value && !isNaN(value) && typeof value == "object" && value.toString() != this.constructor.prototype.value.toString(); },
setValue: function(/*Date*/ value){ // summary: // Deprecated. Use set('value', ...) instead. // tags: // deprecated dojo.deprecated("dijit.Calendar:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); this.set('value', value); },
_getValueAttr: function(){ // summary: // Support get('value')
// this.value is set to 1AM, but return midnight, local time for back-compat var value = new this.dateClassObj(this.value); value.setHours(0, 0, 0, 0);
// If daylight savings pushes midnight to the previous date, fix the Date // object to point at 1am so it will represent the correct day. See #9366 if(value.getDate() < this.value.getDate()){ value = this.dateFuncObj.add(value, "hour", 1); } return value; },
_setValueAttr: function(/*Date|Number*/ value, /*Boolean*/ priorityChange){ // summary: // Support set("value", ...) // description: // Set the current date and update the UI. If the date is disabled, the value will // not change, but the display will change to the corresponding month. // value: // Either a Date or the number of seconds since 1970. // tags: // protected if(value){ // convert from Number to Date, or make copy of Date object so that setHours() call below // doesn't affect original value value = new this.dateClassObj(value); } if(this._isValidDate(value)){ if(!this._isValidDate(this.value) || this.dateFuncObj.compare(value, this.value)){ value.setHours(1, 0, 0, 0); // round to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366)
// Set focus cell to the new value. Arguably this should only happen when there isn't a current // focus point. This will also repopulate the grid, showing the new selected value (and possibly // new month/year). this.set("currentFocus", value);
if(priorityChange || typeof priorityChange == "undefined"){ this.onChange(this.get('value')); this.onValueSelected(this.get('value')); // remove in 2.0 } } } }else{ // clear value, and repopulate grid (to deselect the previously selected day) without changing currentFocus this._set("value", null); this.set("currentFocus", this.currentFocus); } },
_setText: function(node, text){ // summary: // This just sets the content of node to the specified text. // Can't do "node.innerHTML=text" because of an IE bug w/tables, see #3434. // tags: // private while(node.firstChild){ node.removeChild(node.firstChild); } node.appendChild(dojo.doc.createTextNode(text)); },
_populateGrid: function(){ // summary: // Fills in the calendar grid with each day (1-31) // tags: // private
var month = new this.dateClassObj(this.currentFocus); month.setDate(1);
// Iterate through dates in the calendar and fill in date numbers and style info dojo.query(".dijitCalendarDateTemplate", this.domNode).forEach(function(template, i){ i += dayOffset; var date = new this.dateClassObj(month), number, clazz = "dijitCalendar", adj = 0;
if(i < firstDay){ number = daysInPreviousMonth - firstDay + i + 1; adj = -1; clazz += "Previous"; }else if(i >= (firstDay + daysInMonth)){ number = i - firstDay - daysInMonth + 1; adj = 1; clazz += "Next"; }else{ number = i - firstDay + 1; clazz += "Current"; }
if(adj){ date = this.dateFuncObj.add(date, "month", adj); } date.setDate(number);
template.className = clazz + "Month dijitCalendarDateTemplate"; template.dijitDateValue = date.valueOf(); // original code dojo.attr(template, "dijitDateValue", date.valueOf()); // so I can dojo.query() it var label = dojo.query(".dijitCalendarDateLabel", template)[0], text = date.getDateLocalized ? date.getDateLocalized(this.lang) : date.getDate(); this._setText(label, text); }, this);
// Repopulate month drop down list based on current year. // Need to do this to hide leap months in Hebrew calendar. var monthNames = this.dateLocaleModule.getNames('months', 'wide', 'standAlone', this.lang, month); this.monthDropDownButton.dropDown.set("months", monthNames);
// Set name of current month and also fill in spacer element with all the month names // (invisible) so that the maximum width will affect layout. But not on IE6 because then // the center
overlaps the right
(due to a browser bug). this.monthDropDownButton.containerNode.innerHTML = (dojo.isIE == 6 ? "" : "
// Fill in localized prev/current/next years var y = month.getFullYear() - 1; var d = new this.dateClassObj(); dojo.forEach(["previous", "current", "next"], function(name){ d.setFullYear(y++); this._setText(this[name+"YearLabelNode"], this.dateLocaleModule.format(d, {selector:'year', locale:this.lang})); }, this); },
goToToday: function(){ // summary: // Sets calendar's value to today's date this.set('value', new this.dateClassObj()); },
postMixInProperties: function(){ // Parser.instantiate sometimes passes in NaN for IE. Use default value in prototype instead. // TODO: remove this for 2.0 (thanks to #11511) if(isNaN(this.value)){ delete this.value; }
var cloneClass = dojo.hitch(this, function(clazz, n){ var template = dojo.query(clazz, this.domNode)[0]; for(var i=0; i template.parentNode.appendChild(template.cloneNode(true)); } });
// clone the day label and calendar day templates 6 times to make 7 columns cloneClass(".dijitCalendarDayLabelTemplate", 6); cloneClass(".dijitCalendarDateTemplate", 6);
// now make 6 week rows cloneClass(".dijitCalendarWeekTemplate", 5);
// insert localized day names in the header var dayNames = this.dateLocaleModule.getNames('days', this.dayWidth, 'standAlone', this.lang); var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.lang); dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){ this._setText(label, dayNames[(i + dayOffset) % 7]); }, this);
var dateObj = new this.dateClassObj(this.currentFocus);
this.set('currentFocus', dateObj, false); // draw the grid to the month specified by currentFocus
// Set up repeating mouse behavior for increment/decrement of months/years var _this = this; var typematic = function(nodeProp, dateProp, adj){ _this._connects.push( dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){ if(count >= 0){ _this._adjustDisplay(dateProp, adj); } }, 0.8, 500) ); }; typematic("incrementMonth", "month", 1); typematic("decrementMonth", "month", -1); typematic("nextYearLabelNode", "year", 1); typematic("previousYearLabelNode", "year", -1); },
_adjustDisplay: function(/*String*/ part, /*int*/ amount){ // summary: // Moves calendar forwards or backwards by months or years // part: // "month" or "year" // amount: // Number of months or years // tags: // private this._setCurrentFocusAttr(this.dateFuncObj.add(this.currentFocus, part, amount)); },
_setCurrentFocusAttr: function(/*Date*/ date, /*Boolean*/ forceFocus){ // summary: // If the calendar currently has focus, then focuses specified date, // changing the currently displayed month/year if necessary. // If the calendar doesn't have focus, updates currently // displayed month/year, and sets the cell that will get focus. // forceFocus: // If true, will focus() the cell even if calendar itself doesn't have focus
// round specified value to nearest day (1am to avoid issues when DST shift occurs at midnight, see #8521, #9366) date = new this.dateClassObj(date); date.setHours(1, 0, 0, 0);
this._set("currentFocus", date);
// TODO: only re-populate grid when month/year has changed this._populateGrid();
// set tabIndex=0 on new cell, and focus it (but only if Calendar itself is focused) var newCell = dojo.query("[dijitDateValue=" + date.valueOf() + "]", this.domNode)[0]; newCell.setAttribute("tabIndex", this.tabIndex); if(this._focused || forceFocus){ newCell.focus(); }
// set tabIndex=-1 on old focusable cell if(oldCell && oldCell != newCell){ if(dojo.isWebKit){ // see #11064 about webkit bug oldCell.setAttribute("tabIndex", "-1"); }else{ oldCell.removeAttribute("tabIndex"); } } },
focus: function(){ // summary: // Focus the calendar by focusing one of the calendar cells this._setCurrentFocusAttr(this.currentFocus, true); },
_onMonthSelect: function(/*Number*/ newMonth){ // summary: // Handler for when user selects a month from the drop down list // tags: // protected
// move to selected month, bounding by the number of days in the month // (ex: dec 31 --> jan 28, not jan 31) this.currentFocus = this.dateFuncObj.add(this.currentFocus, "month", newMonth - this.currentFocus.getMonth()); this._populateGrid(); },
_onDayClick: function(/*Event*/ evt){ // summary: // Handler for day clicks, selects the date if appropriate // tags: // protected dojo.stopEvent(evt); for(var node = evt.target; node && !node.dijitDateValue; node = node.parentNode); if(node && !dojo.hasClass(node, "dijitCalendarDisabledDate")){ this.set('value', node.dijitDateValue); } },
_onDayMouseOver: function(/*Event*/ evt){ // summary: // Handler for mouse over events on days, sets hovered style // tags: // protected
// event can occur on
or the inside the td, // set node to the
. var node = dojo.hasClass(evt.target, "dijitCalendarDateLabel") ? evt.target.parentNode : evt.target;
var dk = dojo.keys, increment = -1, interval, newValue = this.currentFocus; switch(evt.keyCode){ case dk.RIGHT_ARROW: increment = 1; //fallthrough... case dk.LEFT_ARROW: interval = "day"; if(!this.isLeftToRight()){ increment *= -1; } break; case dk.DOWN_ARROW: increment = 1; //fallthrough... case dk.UP_ARROW: interval = "week"; break; case dk.PAGE_DOWN: increment = 1; //fallthrough... case dk.PAGE_UP: interval = evt.ctrlKey || evt.altKey ? "year" : "month"; break; case dk.END: // go to the next month newValue = this.dateFuncObj.add(newValue, "month", 1); // subtract a day from the result when we're done interval = "day"; //fallthrough... case dk.HOME: newValue = new this.dateClassObj(newValue); newValue.setDate(1); break; case dk.ENTER: case dk.SPACE: this.set("value", this.currentFocus); break; default: return true; }
Called from _onKeyPress() to handle keypress on a stand alone Calendar,
and also from `dijit.form._DateTimeTextBox` to pass a keypress event
from the `dijit.form.DateTextBox` to be handled in this widget
return_summary
False if the key was recognized as a navigation key,
to indicate that the event was handled by Calendar and shouldn't be propogated
// summary: // Notification that a date cell was selected. It may be the same as the previous value. // description: // Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`) // to get notification when the user has clicked a date. Now onExecute() (above) is used. // tags: // protected
summary
Notification that a date cell was selected. It may be the same as the previous value.
description
Formerly used by `dijit.form._DateTimeTextBox` (and thus `dijit.form.DateTextBox`)
to get notification when the user has clicked a date. Now onExecute() (above) is used.
May be overridden to return CSS classes to associate with the date entry for the given dateObject,
for example to indicate a holiday in specified locale.
List of names of months, possibly w/some undefined entries for Hebrew leap months
(ex: ["January", "February", undefined, "April", ...])