/* Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ if(!dojo._hasResource["dijit._TimePicker"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit._TimePicker"] = true; dojo.provide("dijit._TimePicker"); dojo.require("dijit.form._FormWidget"); dojo.require("dojo.date.locale"); /*===== dojo.declare( "dijit._TimePicker.__Constraints", dojo.date.locale.__FormatOptions, { // clickableIncrement: String // See `dijit._TimePicker.clickableIncrement` clickableIncrement: "T00:15:00", // visibleIncrement: String // See `dijit._TimePicker.visibleIncrement` visibleIncrement: "T01:00:00", // visibleRange: String // See `dijit._TimePicker.visibleRange` visibleRange: "T05:00:00" } ); =====*/ dojo.declare("dijit._TimePicker", [dijit._Widget, dijit._Templated], { // summary: // A graphical time picker. // This widget is used internally by other widgets and is not available // as a standalone widget due to lack of accessibility support. templateString: dojo.cache("dijit", "templates/TimePicker.html", "
 
 
\n"), // baseClass: [protected] String // The root className to use for the various states of this widget baseClass: "dijitTimePicker", // clickableIncrement: String // ISO-8601 string representing the amount by which // every clickable element in the time picker increases. // 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 amount by which // every element with a visible time in the time picker increases. // Set in local time, without a time zone. // Example: `T01:00:00` creates text in every 1 hour increment visibleIncrement: "T01:00:00", // visibleRange: String // ISO-8601 string representing the range of this TimePicker. // The TimePicker will only display times in this range. // Example: `T05:00:00` displays 5 hours of options visibleRange: "T05:00:00", // value: String // Date to display. // Defaults to current time and date. // 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: dijit._TimePicker.__Constraints // Specifies valid range of times (start time, end time) 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: dojo.date.stamp.toISOString, /*===== // filterString: string // The string to filter by filterString: "", =====*/ setValue: function(/*Date*/ value){ // summary: // Deprecated. Used set('value') instead. // tags: // deprecated dojo.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(/*Date*/ dateObject, /*String?*/ locale){ // summary: // May be overridden to disable certain dates in the TimePicker e.g. `isDisabledDate=dojo.date.locale.isWeekend` // type: // extension return false; // Boolean }, _getFilteredNodes: function(/*number*/ start, /*number*/ maxNum, /*Boolean*/ before, /*DOMnode*/ lastNode){ // summary: // Returns an array 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 = [], lastValue = lastNode ? lastNode.date : this._refDate, n, i = start, max = this._maxIncrement + Math.abs(i), chk = before ? -1 : 1, dec = before ? 1 : 0, inc = 1 - dec; do{ i = i - dec; n = this._createOption(i); if(n){ if((before && n.date > lastValue) || (!before && n.date < lastValue)){ break; // don't wrap } nodes[before ? "unshift" : "push"](n); lastValue = n.date; } i = i + inc; }while(nodes.length < maxNum && (i*chk) < max); return nodes; }, _showText: function(){ // summary: // Displays the relevant choices in the drop down list // tags: // private var fromIso = dojo.date.stamp.fromISOString; this.timeMenu.innerHTML = ""; this._clickableIncrementDate=fromIso(this.clickableIncrement); this._visibleIncrementDate=fromIso(this.visibleIncrement); this._visibleRangeDate=fromIso(this.visibleRange); // get the value of the increments and the range in seconds (since 00:00:00) 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), visibleRangeSeconds = sinceMidnight(this._visibleRangeDate), // round reference date to previous visible increment time = (this.value || this.currentFocus).getTime(); this._refDate = new Date(time - time % (visibleIncrementSeconds*1000)); 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 this._totalIncrements = visibleRangeSeconds / clickableIncrementSeconds; // 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; // divide the number of seconds in a day by the clickable increment in seconds to get the // absolute max number of increments. this._maxIncrement = (60 * 60 * 24) / clickableIncrementSeconds; var // Find the nodes we should display based on our filter. // Limit to 10 nodes displayed as a half-hearted attempt to stop drop down from overlapping . after = this._getFilteredNodes(0, Math.min(this._totalIncrements >> 1, 10) - 1), before = this._getFilteredNodes(0, Math.min(this._totalIncrements, 10) - after.length, true, after[0]); dojo.forEach(before.concat(after), function(n){this.timeMenu.appendChild(n);}, this); }, constructor: function(){ this.constraints = {}; // create instance object }, postMixInProperties: function(){ this.inherited(arguments); this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls }, _setConstraintsAttr: function(/* Object */ constraints){ // brings in visibleRange, increments, etc. dojo.mixin(this, constraints); // dojo.date.locale needs the lang in the constraints as locale if(!constraints.locale){ constraints.locale = this.lang; } }, postCreate: function(){ // assign typematic mouse listeners to the arrow buttons this.connect(this.timeMenu, dojo.isIE ? "onmousewheel" : 'DOMMouseScroll', "_mouseWheeled"); this._connects.push(dijit.typematic.addMouseListener(this.upArrow, this, "_onArrowUp", 33, 250)); this._connects.push(dijit.typematic.addMouseListener(this.downArrow, this, "_onArrowDown", 33, 250)); this.inherited(arguments); }, _buttonMouse: function(/*Event*/ e){ // summary: // Handler for hover (and unhover) on up/down arrows // tags: // private // in non-IE browser the "mouseenter" event will become "mouseover", // but in IE it's still "mouseenter" dojo.toggleClass(e.currentTarget, e.currentTarget == this.upArrow ? "dijitUpArrowHover" : "dijitDownArrowHover", e.type == "mouseenter" || e.type == "mouseover"); }, _createOption: function(/*Number*/ index){ // summary: // Creates a clickable time option // 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 = dojo.date.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 = dojo.create("div", {"class": this.baseClass+"Item"}); div.date = date; div.index = index; dojo.create('div',{ "class": this.baseClass + "ItemInner", innerHTML: dateString }, div); if(index%this._visibleIncrement<1 && index%this._visibleIncrement>-1){ dojo.addClass(div, this.baseClass+"Marker"); }else if(!(index%this._clickableIncrement)){ dojo.addClass(div, this.baseClass+"Tick"); } if(this.isDisabledDate(date)){ // set disabled dojo.addClass(div, this.baseClass+"ItemDisabled"); } if(this.value && !dojo.date.compare(this.value, date, this.constraints.selector)){ div.selected = true; dojo.addClass(div, this.baseClass+"ItemSelected"); if(dojo.hasClass(div, this.baseClass+"Marker")){ dojo.addClass(div, this.baseClass+"MarkerSelected"); }else{ dojo.addClass(div, 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; }, _onOptionSelected: function(/*Object*/ tgt){ // summary: // Called when user clicks an option in the drop down list // tags: // private var tdate = tgt.target.date || tgt.target.parentNode.date; if(!tdate || this.isDisabledDate(tdate)){ return; } 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; } dojo.toggleClass(node, this.baseClass+"ItemHover", highlight); if(dojo.hasClass(node, this.baseClass+"Marker")){ dojo.toggleClass(node, this.baseClass+"MarkerHover", highlight); }else{ dojo.toggleClass(node, this.baseClass+"TickHover", highlight); } }, onmouseover: function(/*Event*/ e){ // summary: // Handler for onmouseover event // tags: // private this._keyboardSelected = null; var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode; // if we aren't targeting an item, then we return if(!dojo.hasClass(tgr, this.baseClass+"Item")){return;} this._highlightOption(tgr, true); }, onmouseout: function(/*Event*/ e){ // summary: // Handler for onmouseout event // tags: // private this._keyboardSelected = null; var tgr = (e.target.parentNode === this.timeMenu) ? e.target : e.target.parentNode; this._highlightOption(tgr, false); }, _mouseWheeled: function(/*Event*/ e){ // summary: // Handle the mouse wheel events // tags: // private this._keyboardSelected = null; dojo.stopEvent(e); // we're not _measuring_ the scroll amount, just direction var scrollAmount = (dojo.isIE ? e.wheelDelta : -e.detail); this[(scrollAmount>0 ? "_onArrowUp" : "_onArrowDown")](); // yes, we're making a new dom node every time you mousewheel, or click }, _onArrowUp: function(count){ // summary: // Handler for up arrow key. // description: // Removes the bottom time and add one to the top // tags: // private if(typeof count == "number" && count == -1){ return; } // typematic end if(!this.timeMenu.childNodes.length){ return; } var index = this.timeMenu.childNodes[0].index; var divs = this._getFilteredNodes(index, 1, true, this.timeMenu.childNodes[0]); if(divs.length){ this.timeMenu.removeChild(this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1]); this.timeMenu.insertBefore(divs[0], this.timeMenu.childNodes[0]); } }, _onArrowDown: function(count){ // summary: // Handler for up arrow key. // description: // Remove the top time and add one to the bottom // tags: // private if(typeof count == "number" && count == -1){ return; } // typematic end if(!this.timeMenu.childNodes.length){ return; } var index = this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1].index + 1; var divs = this._getFilteredNodes(index, 1, false, this.timeMenu.childNodes[this.timeMenu.childNodes.length - 1]); if(divs.length){ this.timeMenu.removeChild(this.timeMenu.childNodes[0]); this.timeMenu.appendChild(divs[0]); } }, 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 var dk = dojo.keys; if(e.charOrCode == dk.DOWN_ARROW || e.charOrCode == dk.UP_ARROW){ dojo.stopEvent(e); // Figure out which option to highlight now and then highlight it if(this._highlighted_option && !this._highlighted_option.parentNode){ this._highlighted_option = null; } var timeMenu = this.timeMenu, tgt = this._highlighted_option || dojo.query("." + this.baseClass + "ItemSelected", timeMenu)[0]; if(!tgt){ tgt = timeMenu.childNodes[0]; }else if(timeMenu.childNodes.length){ if(e.charOrCode == dk.DOWN_ARROW && !tgt.nextSibling){ this._onArrowDown(); }else if(e.charOrCode == dk.UP_ARROW && !tgt.previousSibling){ this._onArrowUp(); } if(e.charOrCode == dk.DOWN_ARROW){ tgt = tgt.nextSibling; }else{ tgt = tgt.previousSibling; } } this._highlightOption(tgt, true); this._keyboardSelected = tgt; return false; }else if(e.charOrCode == dk.ENTER || e.charOrCode === dk.TAB){ // mouse hover followed by TAB is NO selection if(!this._keyboardSelected && e.charOrCode === dk.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}); } // Call stopEvent() for ENTER key so that form doesn't submit, // but not for TAB, so that TAB does switch focus return e.charOrCode === dk.TAB; } } } ); }