source: Dev/trunk/src/client/dijit/_TimePicker.js @ 483

Last change on this file since 483 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 13.0 KB
Line 
1define([
2        "dojo/_base/array", // array.forEach
3        "dojo/date", // date.compare
4        "dojo/date/locale", // locale.format
5        "dojo/date/stamp", // stamp.fromISOString stamp.toISOString
6        "dojo/_base/declare", // declare
7        "dojo/dom-class", // domClass.add domClass.contains domClass.toggle
8        "dojo/dom-construct", // domConstruct.create
9        "dojo/_base/kernel", // deprecated
10        "dojo/keys", // keys
11        "dojo/_base/lang", // lang.mixin
12        "dojo/sniff", // has(...)
13        "dojo/query", // query
14        "dojo/mouse", // mouse.wheel
15        "dojo/on",
16        "./_WidgetBase",
17        "./form/_ListMouseMixin"
18], function(array, ddate, locale, stamp, declare, domClass, domConstruct, kernel, keys, lang, has, query, mouse, on,
19                        _WidgetBase, _ListMouseMixin){
20
21        // module:
22        //              dijit/_TimePicker
23
24
25        var TimePicker = declare("dijit._TimePicker", [_WidgetBase, _ListMouseMixin], {
26                // summary:
27                //              A time picker dropdown, used by dijit/form/TimeTextBox.
28                //              This widget is not available as a standalone widget due to lack of accessibility support.
29
30                // baseClass: [protected] String
31                //              The root className to use for the various states of this widget
32                baseClass: "dijitTimePicker",
33
34                // clickableIncrement: String
35                //              ISO-8601 string representing the amount by which
36                //              every clickable element in the time picker increases.
37                //              Set in local time, without a time zone.
38                //              Example: `T00:15:00` creates 15 minute increments
39                //              Must divide dijit/_TimePicker.visibleIncrement evenly
40                clickableIncrement: "T00:15:00",
41
42                // visibleIncrement: String
43                //              ISO-8601 string representing the amount by which
44                //              every element with a visible time in the time picker increases.
45                //              Set in local time, without a time zone.
46                //              Example: `T01:00:00` creates text in every 1 hour increment
47                visibleIncrement: "T01:00:00",
48
49                // value: String
50                //              Date to display.
51                //              Defaults to current time and date.
52                //              Can be a Date object or an ISO-8601 string.
53                //              If you specify the GMT time zone (`-01:00`),
54                //              the time will be converted to the local time in the local time zone.
55                //              Otherwise, the time is considered to be in the local time zone.
56                //              If you specify the date and isDate is true, the date is used.
57                //              Example: if your local time zone is `GMT -05:00`,
58                //              `T10:00:00` becomes `T10:00:00-05:00` (considered to be local time),
59                //              `T10:00:00-01:00` becomes `T06:00:00-05:00` (4 hour difference),
60                //              `T10:00:00Z` becomes `T05:00:00-05:00` (5 hour difference between Zulu and local time)
61                //              `yyyy-mm-ddThh:mm:ss` is the format to set the date and time
62                //              Example: `2007-06-01T09:00:00`
63                value: new Date(),
64
65                _visibleIncrement: 2,
66                _clickableIncrement: 1,
67                _totalIncrements: 10,
68
69                // constraints: TimePicker.__Constraints
70                //              Specifies valid range of times (start time, end time)
71                constraints: {},
72
73                /*=====
74                 serialize: function(val, options){
75                         // summary:
76                         //             User overridable function used to convert the attr('value') result to a String
77                         // val: Date
78                         //             The current value
79                         // options: Object?
80                         // tags:
81                         //             protected
82                 },
83                 =====*/
84                serialize: stamp.toISOString,
85
86                /*=====
87                 // filterString: string
88                 //             The string to filter by
89                 filterString: "",
90                 =====*/
91
92                buildRendering: function(){
93                        this.inherited(arguments);
94                        this.containerNode = this.domNode;      // expected by _ListBase
95                        this.timeMenu = this.domNode;   // for back-compat
96                },
97
98                setValue: function(/*Date*/ value){
99                        // summary:
100                        //              Deprecated.  Used set('value') instead.
101                        // tags:
102                        //              deprecated
103                        kernel.deprecated("dijit._TimePicker:setValue() is deprecated.  Use set('value', ...) instead.", "", "2.0");
104                        this.set('value', value);
105                },
106
107                _setValueAttr: function(/*Date*/ date){
108                        // summary:
109                        //              Hook so set('value', ...) works.
110                        // description:
111                        //              Set the value of the TimePicker.
112                        //              Redraws the TimePicker around the new date.
113                        // tags:
114                        //              protected
115                        this._set("value", date);
116                        this._showText();
117                },
118
119                _setFilterStringAttr: function(val){
120                        // summary:
121                        //              Called by TimeTextBox to filter the values shown in my list
122                        this._set("filterString", val);
123                        this._showText();
124                },
125
126                isDisabledDate: function(/*===== dateObject, locale =====*/){
127                        // summary:
128                        //              May be overridden to disable certain dates in the TimePicker e.g. `isDisabledDate=locale.isWeekend`
129                        // dateObject: Date
130                        // locale: String?
131                        // type:
132                        //              extension
133                        return false; // Boolean
134                },
135
136                _getFilteredNodes: function(/*number*/ start, /*number*/ maxNum, /*Boolean*/ before, /*DOMNode*/ lastNode){
137                        // summary:
138                        //              Returns an array of nodes with the filter applied.  At most maxNum nodes
139                        //              will be returned - but fewer may be returned as well.  If the
140                        //              before parameter is set to true, then it will return the elements
141                        //              before the given index
142                        // tags:
143                        //              private
144
145                        var nodes = [];
146
147                        for(var i = 0 ; i < this._maxIncrement; i++){
148                                var n = this._createOption(i);
149                                if(n){
150                                        nodes.push(n);
151                                }
152                        }
153                        return nodes;
154                },
155
156                _showText: function(){
157                        // summary:
158                        //              Displays the relevant choices in the drop down list
159                        // tags:
160                        //              private
161                        var fromIso = stamp.fromISOString;
162                        this.domNode.innerHTML = "";
163                        this._clickableIncrementDate = fromIso(this.clickableIncrement);
164                        this._visibleIncrementDate = fromIso(this.visibleIncrement);
165                        // get the value of the increments to find out how many divs to create
166                        var
167                                sinceMidnight = function(/*Date*/ date){
168                                        return date.getHours() * 60 * 60 + date.getMinutes() * 60 + date.getSeconds();
169                                },
170                                clickableIncrementSeconds = sinceMidnight(this._clickableIncrementDate),
171                                visibleIncrementSeconds = sinceMidnight(this._visibleIncrementDate),
172                                // round reference date to previous visible increment
173                                time = (this.value || this.currentFocus).getTime();
174
175                        this._refDate = fromIso("T00:00:00");
176                        this._refDate.setFullYear(1970, 0, 1); // match parse defaults
177
178                        // assume clickable increment is the smallest unit
179                        this._clickableIncrement = 1;
180                        // divide the visible range by the clickable increment to get the number of divs to create
181                        // example: 10:00:00/00:15:00 -> display 40 divs
182                        // divide the visible increments by the clickable increments to get how often to display the time inline
183                        // example: 01:00:00/00:15:00 -> display the time every 4 divs
184                        this._visibleIncrement = visibleIncrementSeconds / clickableIncrementSeconds;
185                        // divide the number of seconds in a day by the clickable increment in seconds to get the
186                        // absolute max number of increments.
187                        this._maxIncrement = (60 * 60 * 24) / clickableIncrementSeconds;
188
189                        var nodes  = this._getFilteredNodes();
190                        array.forEach(nodes, function(n){
191                                this.domNode.appendChild(n);
192                        }, this);
193
194                        // never show empty due to a bad filter
195                        if(!nodes.length && this.filterString){
196                                this.filterString = '';
197                                this._showText();
198                        }
199                },
200
201                constructor: function(/*===== params, srcNodeRef =====*/){
202                        // summary:
203                        //              Create the widget.
204                        // params: Object|null
205                        //              Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
206                        //              and functions, typically callbacks like onClick.
207                        //              The hash can contain any of the widget's properties, excluding read-only properties.
208                        // srcNodeRef: DOMNode|String?
209                        //              If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
210
211                        this.constraints = {};
212                },
213
214                postMixInProperties: function(){
215                        this.inherited(arguments);
216                        this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls
217                },
218
219                _setConstraintsAttr: function(/* Object */ constraints){
220                        // brings in increments, etc.
221                        for (var key in { clickableIncrement: 1, visibleIncrement: 1 }) {
222                                if (key in constraints) {
223                                        this[key] = constraints[key];
224                                }
225                        }
226
227                        // locale needs the lang in the constraints as locale
228                        if(!constraints.locale){
229                                constraints.locale = this.lang;
230                        }
231                },
232
233                _createOption: function(/*Number*/ index){
234                        // summary:
235                        //              Creates a clickable time option, or returns null if the specified index doesn't match the filter
236                        // tags:
237                        //              private
238                        var date = new Date(this._refDate);
239                        var incrementDate = this._clickableIncrementDate;
240                        date.setHours(date.getHours() + incrementDate.getHours() * index,
241                                date.getMinutes() + incrementDate.getMinutes() * index,
242                                date.getSeconds() + incrementDate.getSeconds() * index);
243                        if(this.constraints.selector == "time"){
244                                date.setFullYear(1970, 0, 1); // make sure each time is for the same date
245                        }
246                        var dateString = locale.format(date, this.constraints);
247                        if(this.filterString && dateString.toLowerCase().indexOf(this.filterString) !== 0){
248                                // Doesn't match the filter - return null
249                                return null;
250                        }
251
252                        var div = this.ownerDocument.createElement("div");
253                        div.className = this.baseClass + "Item";
254                        div.date = date;
255                        div.idx = index;
256                        domConstruct.create('div', {
257                                "class": this.baseClass + "ItemInner",
258                                innerHTML: dateString
259                        }, div);
260
261                        if(index % this._visibleIncrement < 1 && index % this._visibleIncrement > -1){
262                                domClass.add(div, this.baseClass + "Marker");
263                        }else if(!(index % this._clickableIncrement)){
264                                domClass.add(div, this.baseClass + "Tick");
265                        }
266
267                        if(this.isDisabledDate(date)){
268                                // set disabled
269                                domClass.add(div, this.baseClass + "ItemDisabled");
270                        }
271                        if(this.value && !ddate.compare(this.value, date, this.constraints.selector)){
272                                div.selected = true;
273                                domClass.add(div, this.baseClass + "ItemSelected");
274                                this._selectedDiv = div;
275                                if(domClass.contains(div, this.baseClass + "Marker")){
276                                        domClass.add(div, this.baseClass + "MarkerSelected");
277                                }else{
278                                        domClass.add(div, this.baseClass + "TickSelected");
279                                }
280
281                                // Initially highlight the current value.   User can change highlight by up/down arrow keys
282                                // or mouse movement.
283                                this._highlightOption(div, true);
284                        }
285                        return div;
286                },
287
288                onOpen: function(){
289                        this.inherited(arguments);
290
291                        // Since _ListBase::_setSelectedAttr() calls scrollIntoView(), shouldn't call it until list is visible.
292                        this.set("selected", this._selectedDiv);
293                },
294
295                _onOptionSelected: function(/*Object*/ tgt){
296                        // summary:
297                        //              Called when user clicks an option in the drop down list
298                        // tags:
299                        //              private
300                        var tdate = tgt.target.date || tgt.target.parentNode.date;
301                        if(!tdate || this.isDisabledDate(tdate)){
302                                return;
303                        }
304                        this._highlighted_option = null;
305                        this.set('value', tdate);
306                        this.onChange(tdate);
307                },
308
309                onChange: function(/*Date*/ /*===== time =====*/){
310                        // summary:
311                        //              Notification that a time was selected.  It may be the same as the previous value.
312                        // tags:
313                        //              public
314                },
315
316                _highlightOption: function(/*node*/ node, /*Boolean*/ highlight){
317                        // summary:
318                        //              Turns on/off highlight effect on a node based on mouse out/over event
319                        // tags:
320                        //              private
321                        if(!node){
322                                return;
323                        }
324                        if(highlight){
325                                if(this._highlighted_option){
326                                        this._highlightOption(this._highlighted_option, false);
327                                }
328                                this._highlighted_option = node;
329                        }else if(this._highlighted_option !== node){
330                                return;
331                        }else{
332                                this._highlighted_option = null;
333                        }
334                        domClass.toggle(node, this.baseClass + "ItemHover", highlight);
335                        if(domClass.contains(node, this.baseClass + "Marker")){
336                                domClass.toggle(node, this.baseClass + "MarkerHover", highlight);
337                        }else{
338                                domClass.toggle(node, this.baseClass + "TickHover", highlight);
339                        }
340                },
341
342                handleKey: function(/*Event*/ e){
343                        // summary:
344                        //              Called from `dijit/form/_DateTimeTextBox` to pass a keypress event
345                        //              from the `dijit/form/TimeTextBox` to be handled in this widget
346                        // tags:
347                        //              protected
348                        if(e.keyCode == keys.DOWN_ARROW){
349                                this.selectNextNode();
350                                e.stopPropagation();
351                                e.preventDefault();
352                                return false;
353                        }else if(e.keyCode == keys.UP_ARROW){
354                                this.selectPreviousNode();
355                                e.stopPropagation();
356                                e.preventDefault();
357                                return false;
358                        }else if(e.keyCode == keys.ENTER || e.keyCode === keys.TAB){
359                                // mouse hover followed by TAB is NO selection
360                                if(!this._keyboardSelected && e.keyCode === keys.TAB){
361                                        return true;    // true means don't call stopEvent()
362                                }
363
364                                // Accept the currently-highlighted option as the value
365                                if(this._highlighted_option){
366                                        this._onOptionSelected({target: this._highlighted_option});
367                                }
368
369                                // Call stopEvent() for ENTER key so that form doesn't submit,
370                                // but not for TAB, so that TAB does switch focus
371                                return e.keyCode === keys.TAB;
372                        }
373                        return undefined;
374                },
375
376                // Implement abstract methods for _ListBase
377                onHover: function(/*DomNode*/ node){
378                        this._highlightOption(node, true);
379                },
380
381                onUnhover: function(/*DomNode*/ node){
382                        this._highlightOption(node, false);
383                },
384
385                onSelect: function(/*DomNode*/ node){
386                        this._highlightOption(node, true);
387                },
388
389                onDeselect: function(/*DomNode*/ node){
390                        this._highlightOption(node, false);
391                },
392
393                onClick: function(/*DomNode*/ node){
394                        this._onOptionSelected({target: node});
395                }
396        });
397
398        /*=====
399         TimePicker.__Constraints = declare(locale.__FormatOptions, {
400                 // clickableIncrement: String
401                 //             See `dijit/_TimePicker.clickableIncrement`
402                 clickableIncrement: "T00:15:00"
403         });
404         =====*/
405
406        return TimePicker;
407});
Note: See TracBrowser for help on using the repository browser.