You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
14 KiB
435 lines
14 KiB
define([ |
|
"dojo/_base/array", // array.forEach |
|
"dojo/date", // date.compare |
|
"dojo/date/locale", // locale.format |
|
"dojo/date/stamp", // stamp.fromISOString stamp.toISOString |
|
"dojo/_base/declare", // declare |
|
"dojo/dom-class", // domClass.add domClass.contains domClass.toggle |
|
"dojo/dom-construct", // domConstruct.create |
|
"dojo/_base/kernel", // deprecated |
|
"dojo/keys", // keys |
|
"dojo/_base/lang", // lang.mixin |
|
"dojo/sniff", // has(...) |
|
"dojo/query", // query |
|
"dojo/mouse", // mouse.wheel |
|
"dojo/on", |
|
"./_WidgetBase", |
|
"./form/_ListMouseMixin" |
|
], function(array, ddate, locale, stamp, declare, domClass, domConstruct, kernel, keys, lang, has, query, mouse, on, |
|
_WidgetBase, _ListMouseMixin){ |
|
|
|
// module: |
|
// dijit/_TimePicker |
|
|
|
|
|
var TimePicker = declare("dijit._TimePicker", [_WidgetBase, _ListMouseMixin], { |
|
// summary: |
|
// A time picker dropdown, used by dijit/form/TimeTextBox. |
|
// This widget is not available as a standalone widget due to lack of accessibility support. |
|
|
|
// baseClass: [protected] String |
|
// The root className to use for the various states of this widget |
|
baseClass: "dijitTimePicker", |
|
|
|
// pickerMin: String |
|
// ISO-8601 string representing the time of the first |
|
// visible element in the time picker. |
|
// Set in local time, without a time zone. |
|
pickerMin: "T00:00:00", |
|
|
|
// pickerMax: String |
|
// ISO-8601 string representing the last (possible) time |
|
// added to the time picker. |
|
// Set in local time, without a time zone. |
|
pickerMax: "T23:59:59", |
|
|
|
// clickableIncrement: String |
|
// ISO-8601 string representing the interval between choices in the time picker. |
|
// Set in local time, without a time zone. |
|
// Example: `T00:15:00` creates 15 minute increments |
|
// Must divide dijit/_TimePicker.visibleIncrement evenly |
|
clickableIncrement: "T00:15:00", |
|
|
|
// visibleIncrement: String |
|
// ISO-8601 string representing the interval between "major" choices in the time picker. |
|
// Each theme will highlight the major choices with a larger font / different color / etc. |
|
// Set in local time, without a time zone. |
|
// Example: `T01:00:00` creates text in every 1 hour increment |
|
visibleIncrement: "T01:00:00", |
|
|
|
// value: String |
|
// Time to display. |
|
// Defaults to current time. |
|
// Can be a Date object or an ISO-8601 string. |
|
// If you specify the GMT time zone (`-01:00`), |
|
// the time will be converted to the local time in the local time zone. |
|
// Otherwise, the time is considered to be in the local time zone. |
|
// If you specify the date and isDate is true, the date is used. |
|
// Example: if your local time zone is `GMT -05:00`, |
|
// `T10:00:00` becomes `T10:00:00-05:00` (considered to be local time), |
|
// `T10:00:00-01:00` becomes `T06:00:00-05:00` (4 hour difference), |
|
// `T10:00:00Z` becomes `T05:00:00-05:00` (5 hour difference between Zulu and local time) |
|
// `yyyy-mm-ddThh:mm:ss` is the format to set the date and time |
|
// Example: `2007-06-01T09:00:00` |
|
value: new Date(), |
|
|
|
_visibleIncrement: 2, |
|
_clickableIncrement: 1, |
|
_totalIncrements: 10, |
|
|
|
// constraints: TimePicker.__Constraints |
|
// Specifies valid range of times (start time, end time), and also used by TimeTextBox to pass other |
|
// options to the TimePicker: pickerMin, pickerMax, clickableIncrement, and visibleIncrement. |
|
constraints: {}, |
|
|
|
/*===== |
|
serialize: function(val, options){ |
|
// summary: |
|
// User overridable function used to convert the attr('value') result to a String |
|
// val: Date |
|
// The current value |
|
// options: Object? |
|
// tags: |
|
// protected |
|
}, |
|
=====*/ |
|
serialize: stamp.toISOString, |
|
|
|
/*===== |
|
// filterString: string |
|
// The string to filter by |
|
filterString: "", |
|
=====*/ |
|
|
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
this.containerNode = this.domNode; // expected by _ListBase |
|
this.timeMenu = this.domNode; // for back-compat |
|
}, |
|
|
|
setValue: function(/*Date*/ value){ |
|
// summary: |
|
// Deprecated. Used set('value') instead. |
|
// tags: |
|
// deprecated |
|
kernel.deprecated("dijit._TimePicker:setValue() is deprecated. Use set('value', ...) instead.", "", "2.0"); |
|
this.set('value', value); |
|
}, |
|
|
|
_setValueAttr: function(/*Date*/ date){ |
|
// summary: |
|
// Hook so set('value', ...) works. |
|
// description: |
|
// Set the value of the TimePicker. |
|
// Redraws the TimePicker around the new date. |
|
// tags: |
|
// protected |
|
this._set("value", date); |
|
this._showText(); |
|
}, |
|
|
|
_setFilterStringAttr: function(val){ |
|
// summary: |
|
// Called by TimeTextBox to filter the values shown in my list |
|
this._set("filterString", val); |
|
this._showText(); |
|
}, |
|
|
|
isDisabledDate: function(/*===== dateObject, locale =====*/){ |
|
// summary: |
|
// May be overridden to disable certain dates in the TimePicker e.g. `isDisabledDate=locale.isWeekend` |
|
// dateObject: Date |
|
// locale: String? |
|
// type: |
|
// extension |
|
return false; // Boolean |
|
}, |
|
|
|
_getFilteredNodes: function(/*number*/ start, /*number*/ maxNum, /*Boolean*/ before, /*DOMNode*/ lastNode){ |
|
// summary: |
|
// Returns a DocumentFragment of nodes with the filter applied. At most maxNum nodes |
|
// will be returned - but fewer may be returned as well. If the |
|
// before parameter is set to true, then it will return the elements |
|
// before the given index |
|
// tags: |
|
// private |
|
|
|
var nodes = this.ownerDocument.createDocumentFragment(); |
|
|
|
for(var i = 0 ; i < this._maxIncrement; i++){ |
|
var n = this._createOption(i); |
|
if(n){ |
|
nodes.appendChild(n); |
|
} |
|
} |
|
|
|
return nodes; |
|
}, |
|
|
|
_showText: function(){ |
|
// summary: |
|
// Displays the relevant choices in the drop down list |
|
// tags: |
|
// private |
|
var fromIso = stamp.fromISOString; |
|
this.domNode.innerHTML = ""; |
|
this._clickableIncrementDate = fromIso(this.clickableIncrement); |
|
this._visibleIncrementDate = fromIso(this.visibleIncrement); |
|
// get the value of the increments to find out how many divs to create |
|
var |
|
sinceMidnight = function(/*Date*/ date){ |
|
return date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds(); |
|
}, |
|
clickableIncrementSeconds = sinceMidnight(this._clickableIncrementDate), |
|
visibleIncrementSeconds = sinceMidnight(this._visibleIncrementDate), |
|
// round reference date to previous visible increment |
|
time = (this.value || this.currentFocus).getTime(); |
|
|
|
this._refDate = fromIso(this.pickerMin); |
|
this._refDate.setFullYear(1970, 0, 1); // match parse defaults |
|
|
|
// assume clickable increment is the smallest unit |
|
this._clickableIncrement = 1; |
|
// divide the visible range by the clickable increment to get the number of divs to create |
|
// example: 10:00:00/00:15:00 -> display 40 divs |
|
// divide the visible increments by the clickable increments to get how often to display the time inline |
|
// example: 01:00:00/00:15:00 -> display the time every 4 divs |
|
this._visibleIncrement = visibleIncrementSeconds / clickableIncrementSeconds; |
|
|
|
// get the number of increments (i.e. number of entries in the picker) |
|
var endDate = fromIso(this.pickerMax); |
|
endDate.setFullYear(1970, 0, 1); |
|
var visibleRange = (endDate.getTime() - this._refDate.getTime()) * 0.001; |
|
this._maxIncrement = Math.ceil((visibleRange + 1) / clickableIncrementSeconds); |
|
|
|
var nodes = this._getFilteredNodes(); |
|
|
|
// never show empty due to a bad filter |
|
if(!nodes.firstChild && this.filterString){ |
|
this.filterString = ''; |
|
this._showText(); |
|
}else{ |
|
this.domNode.appendChild(nodes); |
|
} |
|
}, |
|
|
|
constructor: function(/*===== params, srcNodeRef =====*/){ |
|
// summary: |
|
// Create the widget. |
|
// params: Object|null |
|
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.) |
|
// and functions, typically callbacks like onClick. |
|
// The hash can contain any of the widget's properties, excluding read-only properties. |
|
// srcNodeRef: DOMNode|String? |
|
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree |
|
|
|
this.constraints = {}; |
|
}, |
|
|
|
postMixInProperties: function(){ |
|
this.inherited(arguments); |
|
this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls |
|
}, |
|
|
|
// For historical reasons TimeTextBox sends all the options for the _TimePicker inside of a constraints{} object |
|
_setConstraintsAttr: function(/* Object */ constraints){ |
|
// brings in increments, etc. |
|
for (var key in { clickableIncrement: 1, visibleIncrement: 1, pickerMin: 1, pickerMax: 1 }) { |
|
if (key in constraints) { |
|
this[key] = constraints[key]; |
|
} |
|
} |
|
|
|
// locale needs the lang in the constraints as locale |
|
if(!constraints.locale){ |
|
constraints.locale = this.lang; |
|
} |
|
}, |
|
|
|
_createOption: function(/*Number*/ index){ |
|
// summary: |
|
// Creates a clickable time option, or returns null if the specified index doesn't match the filter |
|
// tags: |
|
// private |
|
var date = new Date(this._refDate); |
|
var incrementDate = this._clickableIncrementDate; |
|
date.setHours(date.getHours() + incrementDate.getHours() * index, |
|
date.getMinutes() + incrementDate.getMinutes() * index, |
|
date.getSeconds() + incrementDate.getSeconds() * index); |
|
if(this.constraints.selector == "time"){ |
|
date.setFullYear(1970, 0, 1); // make sure each time is for the same date |
|
} |
|
var dateString = locale.format(date, this.constraints); |
|
if(this.filterString && dateString.toLowerCase().indexOf(this.filterString) !== 0){ |
|
// Doesn't match the filter - return null |
|
return null; |
|
} |
|
|
|
var div = this.ownerDocument.createElement("div"); |
|
div.className = this.baseClass + "Item"; |
|
div.date = date; |
|
div.idx = index; |
|
domConstruct.create('div', { |
|
"class": this.baseClass + "ItemInner", |
|
innerHTML: dateString |
|
}, div); |
|
|
|
var marker = index % this._visibleIncrement < 1 && index % this._visibleIncrement > -1, |
|
tick = !marker && !(index % this._clickableIncrement); |
|
if(marker){ |
|
div.className += " " + this.baseClass + "Marker"; |
|
}else if(tick){ |
|
div.className += " " + this.baseClass + "Tick"; |
|
} |
|
|
|
if(this.isDisabledDate(date)){ |
|
// set disabled |
|
div.className += " " + this.baseClass + "ItemDisabled"; |
|
} |
|
if(this.value && !ddate.compare(this.value, date, this.constraints.selector)){ |
|
div.selected = true; |
|
div.className += " " + this.baseClass + "ItemSelected"; |
|
this._selectedDiv = div; |
|
if(marker){ |
|
div.className += " " + this.baseClass + "MarkerSelected"; |
|
}else if(tick){ |
|
div.className += " " + this.baseClass + "TickSelected"; |
|
} |
|
|
|
// Initially highlight the current value. User can change highlight by up/down arrow keys |
|
// or mouse movement. |
|
this._highlightOption(div, true); |
|
} |
|
return div; |
|
}, |
|
|
|
onOpen: function(){ |
|
this.inherited(arguments); |
|
|
|
// Since _ListBase::_setSelectedAttr() calls scrollIntoView(), shouldn't call it until list is visible. |
|
this.set("selected", this._selectedDiv); |
|
}, |
|
|
|
_onOptionSelected: function(/*Object*/ tgt, /*Boolean*/ change){ |
|
// summary: |
|
// Called when user clicks or keys to an option in the drop down list |
|
// tgt: Object |
|
// tgt.target specifies the node that was clicked |
|
// change: Boolean |
|
// If true, fire "change" event, otherwise just fire "input" event. |
|
// tags: |
|
// private |
|
var tdate = tgt.target.date || tgt.target.parentNode.date; |
|
if(!tdate || this.isDisabledDate(tdate)){ |
|
return; |
|
} |
|
this._set('value', tdate); |
|
this.emit("input"); |
|
if(change) { |
|
this._highlighted_option = null; |
|
this.set('value', tdate); |
|
this.onChange(tdate); |
|
} |
|
}, |
|
|
|
onChange: function(/*Date*/ /*===== time =====*/){ |
|
// summary: |
|
// Notification that a time was selected. It may be the same as the previous value. |
|
// tags: |
|
// public |
|
}, |
|
|
|
_highlightOption: function(/*node*/ node, /*Boolean*/ highlight){ |
|
// summary: |
|
// Turns on/off highlight effect on a node based on mouse out/over event |
|
// tags: |
|
// private |
|
if(!node){ |
|
return; |
|
} |
|
if(highlight){ |
|
if(this._highlighted_option){ |
|
this._highlightOption(this._highlighted_option, false); |
|
} |
|
this._highlighted_option = node; |
|
}else if(this._highlighted_option !== node){ |
|
return; |
|
}else{ |
|
this._highlighted_option = null; |
|
} |
|
domClass.toggle(node, this.baseClass + "ItemHover", highlight); |
|
if(domClass.contains(node, this.baseClass + "Marker")){ |
|
domClass.toggle(node, this.baseClass + "MarkerHover", highlight); |
|
}else{ |
|
domClass.toggle(node, this.baseClass + "TickHover", highlight); |
|
} |
|
}, |
|
|
|
handleKey: function(/*Event*/ e){ |
|
// summary: |
|
// Called from `dijit/form/_DateTimeTextBox` to pass a keypress event |
|
// from the `dijit/form/TimeTextBox` to be handled in this widget |
|
// tags: |
|
// protected |
|
if(e.keyCode == keys.DOWN_ARROW){ |
|
this.selectNextNode(); |
|
this._onOptionSelected({target: this._highlighted_option}, false); |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
return false; |
|
}else if(e.keyCode == keys.UP_ARROW){ |
|
this.selectPreviousNode(); |
|
this._onOptionSelected({target: this._highlighted_option}, false); |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
return false; |
|
}else if(e.keyCode == keys.ENTER || e.keyCode === keys.TAB){ |
|
// mouse hover followed by TAB is NO selection |
|
if(!this._keyboardSelected && e.keyCode === keys.TAB){ |
|
return true; // true means don't call stopEvent() |
|
} |
|
|
|
// Accept the currently-highlighted option as the value |
|
if(this._highlighted_option){ |
|
this._onOptionSelected({target: this._highlighted_option}, true); |
|
} |
|
|
|
// Call stopEvent() for ENTER key so that form doesn't submit, |
|
// but not for TAB, so that TAB does switch focus |
|
return e.keyCode === keys.TAB; |
|
} |
|
return undefined; |
|
}, |
|
|
|
// Implement abstract methods for _ListBase |
|
onHover: function(/*DomNode*/ node){ |
|
this._highlightOption(node, true); |
|
}, |
|
|
|
onUnhover: function(/*DomNode*/ node){ |
|
this._highlightOption(node, false); |
|
}, |
|
|
|
onSelect: function(/*DomNode*/ node){ |
|
this._highlightOption(node, true); |
|
}, |
|
|
|
onDeselect: function(/*DomNode*/ node){ |
|
this._highlightOption(node, false); |
|
}, |
|
|
|
onClick: function(/*DomNode*/ node){ |
|
this._onOptionSelected({target: node}, true); |
|
} |
|
}); |
|
|
|
/*===== |
|
TimePicker.__Constraints = declare(locale.__FormatOptions, { |
|
// clickableIncrement: String |
|
// See `dijit/_TimePicker.clickableIncrement` |
|
clickableIncrement: "T00:15:00" |
|
}); |
|
=====*/ |
|
|
|
return TimePicker; |
|
});
|
|
|