~bac/juju-gui/trunkcopy

« back to all changes in this revision

Viewing changes to lib/yui/build/calendar/calendar.js

  • Committer: kapil.foss at gmail
  • Date: 2012-07-13 18:45:59 UTC
  • Revision ID: kapil.foss@gmail.com-20120713184559-2xl7be17egsrz0c9
reshape

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
YUI 3.5.1 (build 22)
3
 
Copyright 2012 Yahoo! Inc. All rights reserved.
4
 
Licensed under the BSD License.
5
 
http://yuilibrary.com/license/
6
 
*/
7
 
YUI.add('calendar', function(Y) {
8
 
 
9
 
/**
10
 
 * The Calendar component is a UI widget that allows users
11
 
 * to view dates in a two-dimensional month grid, as well as
12
 
 * to select one or more dates, or ranges of dates. Calendar
13
 
 * is generated dynamically and relies on the developer to
14
 
 * provide for a progressive enhancement alternative.
15
 
 *
16
 
 *
17
 
 * @module calendar
18
 
 */
19
 
 
20
 
var getCN             = Y.ClassNameManager.getClassName,
21
 
    CALENDAR          = 'calendar',
22
 
    KEY_DOWN          = 40,
23
 
    KEY_UP            = 38,
24
 
    KEY_LEFT          = 37,
25
 
    KEY_RIGHT         = 39,
26
 
    KEY_ENTER         = 13,
27
 
    KEY_SPACE         = 32,
28
 
    CAL_HD            = getCN(CALENDAR, 'header'),
29
 
    CAL_DAY_SELECTED  = getCN(CALENDAR, 'day-selected'),
30
 
    CAL_DAY_HILITED   = getCN(CALENDAR, 'day-highlighted'),
31
 
    CAL_DAY           = getCN(CALENDAR, 'day'),
32
 
    CAL_PREVMONTH_DAY = getCN(CALENDAR, 'prevmonth-day'),
33
 
    CAL_NEXTMONTH_DAY = getCN(CALENDAR, 'nextmonth-day'),
34
 
    CAL_GRID          = getCN(CALENDAR, 'grid'),
35
 
    ydate             = Y.DataType.Date,
36
 
    delegate          = Y.delegate,
37
 
    CAL_PANE          = getCN(CALENDAR, 'pane'),
38
 
    os                = Y.UA.os;
39
 
 
40
 
/** Create a calendar view to represent a single or multiple
41
 
  * month range of dates, rendered as a grid with date and
42
 
  * weekday labels.
43
 
  * 
44
 
  * @class Calendar
45
 
  * @extends CalendarBase
46
 
  * @param config {Object} Configuration object (see Configuration attributes)
47
 
  * @constructor
48
 
  */
49
 
function Calendar(config) {
50
 
  Calendar.superclass.constructor.apply ( this, arguments );
51
 
}
52
 
 
53
 
Y.Calendar = Y.extend(Calendar, Y.CalendarBase, {
54
 
 
55
 
    _keyEvents: [],
56
 
 
57
 
    _highlightedDateNode: null,
58
 
 
59
 
  /**
60
 
   * A property tracking the last selected date on the calendar, for the
61
 
   * purposes of multiple selection.
62
 
   *
63
 
   * @property _lastSelectedDate
64
 
   * @type Date
65
 
   * @default null
66
 
   * @private
67
 
   */  
68
 
    _lastSelectedDate: null,
69
 
 
70
 
  /**
71
 
   * Designated initializer. Activates the navigation plugin for the calendar.
72
 
   *
73
 
   * @method initializer
74
 
   */ 
75
 
  initializer : function () {
76
 
    this.plug(Y.Plugin.CalendarNavigator);
77
 
 
78
 
 
79
 
    this._keyEvents = [];
80
 
    this._highlightedDateNode = null;
81
 
    this._lastSelectedDate = null;
82
 
  },
83
 
 
84
 
  /**
85
 
    * syncUI implementation
86
 
    *
87
 
    * Update the scroll position, based on the current value of scrollY
88
 
    * @method syncUI
89
 
    */  
90
 
  syncUI : function () {
91
 
 
92
 
  },
93
 
 
94
 
  /**
95
 
   * Overrides the _bindCalendarEvents placeholder in CalendarBase
96
 
   * and binds calendar events during bindUI stage.
97
 
   * @method _bindCalendarEvents
98
 
   * @protected
99
 
   */   
100
 
  _bindCalendarEvents : function () {
101
 
    var contentBox = this.get('contentBox'),
102
 
        pane       = contentBox.one("." + CAL_PANE);
103
 
    pane.on("selectstart", function (ev) { ev.preventDefault();});
104
 
    pane.delegate("click", this._clickCalendar, "." + CAL_DAY, this);
105
 
    pane.delegate("keydown", this._keydownCalendar, "." + CAL_GRID, this);
106
 
    pane.delegate("focus", this._focusCalendarGrid, "." + CAL_GRID, this);
107
 
    pane.delegate("focus", this._focusCalendarCell, "." + CAL_DAY, this);
108
 
    pane.delegate("blur", this._blurCalendarGrid, "." + CAL_GRID + ",." + CAL_DAY, this);
109
 
  },
110
 
 
111
 
  /**
112
 
   * Highlights a specific date node with keyboard highlight class
113
 
   * @method _highlightDateNode
114
 
   * @param oDate {Date} Date corresponding the node to be highlighted
115
 
   * @protected
116
 
   */   
117
 
  _highlightDateNode : function (oDate) {
118
 
    this._unhighlightCurrentDateNode();
119
 
    var newNode = this._dateToNode(oDate);
120
 
    newNode.focus();
121
 
    newNode.addClass(CAL_DAY_HILITED);
122
 
  },
123
 
 
124
 
  /**
125
 
   * Unhighlights a specific date node currently highlighted with keyboard highlight class
126
 
   * @method _unhighlightCurrentDateNode
127
 
   * @protected
128
 
   */   
129
 
  _unhighlightCurrentDateNode : function () {
130
 
    var allHilitedNodes = this.get("contentBox").all("." + CAL_DAY_HILITED);
131
 
    if (allHilitedNodes) {
132
 
      allHilitedNodes.removeClass(CAL_DAY_HILITED);
133
 
    }
134
 
  },
135
 
 
136
 
  /**
137
 
   * Returns the grid number for a specific calendar grid (for multi-grid templates)
138
 
   * @method _getGridNumber
139
 
   * @param gridNode {Node} Node corresponding to a specific grid
140
 
   * @protected
141
 
   */   
142
 
  _getGridNumber : function (gridNode) {
143
 
    var idParts = gridNode.get("id").split("_").reverse();
144
 
        return parseInt(idParts[0], 10);
145
 
  },
146
 
 
147
 
  /**
148
 
   * Handler for loss of focus of calendar grid
149
 
   * @method _blurCalendarGrid
150
 
   * @protected
151
 
   */   
152
 
   _blurCalendarGrid : function (ev) {
153
 
      this._unhighlightCurrentDateNode();
154
 
   },
155
 
 
156
 
 
157
 
  /**
158
 
   * Handler for gain of focus of calendar cell
159
 
   * @method _focusCalendarCell
160
 
   * @protected
161
 
   */ 
162
 
   _focusCalendarCell : function (ev) {
163
 
       this._highlightedDateNode = ev.target;
164
 
       ev.stopPropagation();
165
 
   },
166
 
 
167
 
  /**
168
 
   * Handler for gain of focus of calendar grid
169
 
   * @method _focusCalendarGrid
170
 
   * @protected
171
 
   */ 
172
 
   _focusCalendarGrid : function (ev) {     
173
 
       this._unhighlightCurrentDateNode();
174
 
       this._highlightedDateNode = null;
175
 
   },
176
 
 
177
 
  /**
178
 
   * Handler for keyboard press on a calendar grid
179
 
   * @method _keydownCalendar
180
 
   * @protected
181
 
   */ 
182
 
   _keydownCalendar : function (ev) {
183
 
    var gridNum = this._getGridNumber(ev.target),
184
 
        curDate = !this._highlightedDateNode ? null : this._nodeToDate(this._highlightedDateNode),
185
 
        keyCode = ev.keyCode,
186
 
        dayNum = 0,
187
 
        dir = '';
188
 
 
189
 
        switch(keyCode) {
190
 
          case KEY_DOWN: 
191
 
            dayNum = 7;
192
 
            dir = 's';
193
 
          break;
194
 
          case KEY_UP: 
195
 
            dayNum = -7;
196
 
            dir = 'n';
197
 
          break;
198
 
          case KEY_LEFT: 
199
 
            dayNum = -1;
200
 
            dir = 'w';
201
 
          break;
202
 
          case KEY_RIGHT:
203
 
            dayNum = 1;
204
 
            dir = 'e';
205
 
          break;
206
 
          case KEY_SPACE: case KEY_ENTER:
207
 
            ev.preventDefault();
208
 
            if (this._highlightedDateNode) {
209
 
            var selMode = this.get("selectionMode");
210
 
            if (selMode === "single" && !this._highlightedDateNode.hasClass(CAL_DAY_SELECTED)) {
211
 
                this._clearSelection(true);
212
 
                this._addDateToSelection(curDate);
213
 
            }
214
 
            else if (selMode === "multiple" || selMode === "multiple-sticky") {
215
 
                if (this._highlightedDateNode.hasClass(CAL_DAY_SELECTED)) {
216
 
                  this._removeDateFromSelection(curDate);
217
 
                }
218
 
                else {
219
 
                  this._addDateToSelection(curDate);
220
 
                }
221
 
             }
222
 
            }
223
 
          break;
224
 
        }
225
 
 
226
 
 
227
 
      if (keyCode == KEY_DOWN || keyCode == KEY_UP || keyCode == KEY_LEFT || keyCode == KEY_RIGHT) {
228
 
 
229
 
      if (!curDate) {
230
 
             curDate = ydate.addMonths(this.get("date"), gridNum);
231
 
             dayNum = 0;
232
 
      }
233
 
              ev.preventDefault();
234
 
          var newDate = ydate.addDays(curDate, dayNum),
235
 
              startDate = this.get("date"),
236
 
              endDate = ydate.addMonths(this.get("date"), this._paneNumber - 1),
237
 
              lastPaneDate = new Date(endDate);
238
 
              endDate.setDate(ydate.daysInMonth(endDate));
239
 
          
240
 
          if (ydate.isInRange(newDate, startDate, endDate)) {
241
 
/*
242
 
              var paneShift = (newDate.getMonth() - curDate.getMonth()) % 10;
243
 
 
244
 
 
245
 
              if (paneShift != 0) {
246
 
                var newGridNum = gridNum + paneShift,
247
 
                    contentBox = this.get('contentBox'),
248
 
                    newPane = contentBox.one("#" + this._calendarId + "_pane_" + newGridNum);
249
 
                    newPane.focus();
250
 
              }
251
 
*/
252
 
              this._highlightDateNode(newDate);
253
 
          }
254
 
          else if (ydate.isGreater(startDate, newDate)) {
255
 
            if (!ydate.isGreaterOrEqual(this.get("minimumDate"), startDate)) {
256
 
                 this.set("date", ydate.addMonths(startDate, -1));
257
 
                 this._highlightDateNode(newDate);
258
 
            }
259
 
          }
260
 
          else if (ydate.isGreater(newDate, endDate)) {
261
 
            if (!ydate.isGreaterOrEqual(lastPaneDate, this.get("maximumDate"))) {
262
 
                 this.set("date", ydate.addMonths(startDate, 1));
263
 
                 this._highlightDateNode(newDate);
264
 
            }
265
 
          }
266
 
 
267
 
        }
268
 
   },
269
 
 
270
 
  /**
271
 
   * Handles the calendar clicks based on selection mode.
272
 
   * @method _clickCalendar
273
 
   * @param {Event} ev A click event
274
 
   * @private
275
 
   */   
276
 
    _clickCalendar : function (ev) {
277
 
        var clickedCell = ev.target,
278
 
            clickedCellIsDay = clickedCell.hasClass(CAL_DAY) && 
279
 
                               !clickedCell.hasClass(CAL_PREVMONTH_DAY) && 
280
 
                               !clickedCell.hasClass(CAL_NEXTMONTH_DAY),
281
 
            clickedCellIsSelected = clickedCell.hasClass(CAL_DAY_SELECTED);
282
 
        switch (this.get("selectionMode")) {
283
 
          case("single"):
284
 
               if (clickedCellIsDay) {
285
 
                  if (!clickedCellIsSelected) {
286
 
                    this._clearSelection(true);  
287
 
                    this._addDateToSelection(this._nodeToDate(clickedCell));
288
 
                  }
289
 
             }
290
 
               break;
291
 
            case("multiple-sticky"):
292
 
               if (clickedCellIsDay) {
293
 
                 if (clickedCellIsSelected) {
294
 
                  this._removeDateFromSelection(this._nodeToDate(clickedCell));
295
 
                 }
296
 
                 else {
297
 
                  this._addDateToSelection(this._nodeToDate(clickedCell));
298
 
                 }
299
 
               }
300
 
               break;
301
 
            case("multiple"):
302
 
               if (!ev.metaKey && !ev.ctrlKey && !ev.shiftKey) {
303
 
                    this._clearSelection(true);
304
 
                    this._lastSelectedDate = this._nodeToDate(clickedCell);
305
 
                    this._addDateToSelection(this._lastSelectedDate);
306
 
               }
307
 
               else if (((os == 'macintosh' && ev.metaKey) || (os != 'macintosh' && ev.ctrlKey)) && !ev.shiftKey) {
308
 
                  if (clickedCellIsSelected) {
309
 
                    this._removeDateFromSelection(this._nodeToDate(clickedCell));
310
 
                    this._lastSelectedDate = null;
311
 
                  }
312
 
                  else {
313
 
                    this._lastSelectedDate = this._nodeToDate(clickedCell);
314
 
                    this._addDateToSelection(this._lastSelectedDate);
315
 
                  }
316
 
               }
317
 
               else if (((os == 'macintosh' && ev.metaKey) || (os != 'macintosh' && ev.ctrlKey)) && ev.shiftKey) {
318
 
                  if (this._lastSelectedDate) {
319
 
                    var selectedDate = this._nodeToDate(clickedCell);
320
 
                    this._addDateRangeToSelection(selectedDate, this._lastSelectedDate);
321
 
                    this._lastSelectedDate = selectedDate;
322
 
                  }
323
 
                  else {
324
 
                    this._lastSelectedDate = this._nodeToDate(clickedCell);
325
 
                    this._addDateToSelection(this._lastSelectedDate);
326
 
                  }
327
 
 
328
 
               }
329
 
               else if (ev.shiftKey) {
330
 
                    if (this._lastSelectedDate) {
331
 
                      var selectedDate = this._nodeToDate(clickedCell);
332
 
                      this._clearSelection(true);
333
 
                      this._addDateRangeToSelection(selectedDate, this._lastSelectedDate);
334
 
                      this._lastSelectedDate = selectedDate;
335
 
                    }
336
 
                    else {
337
 
                      this._clearSelection(true);
338
 
                      this._lastSelectedDate = this._nodeToDate(clickedCell);
339
 
                        this._addDateToSelection(this._lastSelectedDate);
340
 
                    }
341
 
               }
342
 
               break;
343
 
        }
344
 
 
345
 
      if (clickedCellIsDay) {
346
 
   /**
347
 
     * Fired when a specific date cell in the calendar is clicked. The event carries a 
348
 
     * payload which includes a `cell` property corresponding to the node of the actual
349
 
     * date cell, and a `date` property, with the `Date` that was clicked.
350
 
     *
351
 
     * @event dateClick
352
 
     */
353
 
        this.fire("dateClick", {cell: clickedCell, date: this._nodeToDate(clickedCell)});
354
 
      }
355
 
      else if (clickedCell.hasClass(CAL_PREVMONTH_DAY)) {
356
 
   /**
357
 
     * Fired when any of the previous month's days displayed before the calendar grid
358
 
     * are clicked.
359
 
     *
360
 
     * @event prevMonthClick
361
 
     */
362
 
        this.fire("prevMonthClick");
363
 
      }
364
 
      else if (clickedCell.hasClass(CAL_NEXTMONTH_DAY)) {
365
 
   /**
366
 
     * Fired when any of the next month's days displayed after the calendar grid
367
 
     * are clicked.
368
 
     *
369
 
     * @event nextMonthClick
370
 
     */
371
 
        this.fire("nextMonthClick");
372
 
      }
373
 
    },
374
 
 
375
 
  /**
376
 
   * Subtracts one month from the current calendar view.
377
 
   * @method subtractMonth
378
 
   */   
379
 
  subtractMonth : function (e) {
380
 
    this.set("date", ydate.addMonths(this.get("date"), -1));
381
 
    e.halt();
382
 
  },
383
 
 
384
 
  /**
385
 
   * Subtracts one year from the current calendar view.
386
 
   * @method subtractYear
387
 
   */ 
388
 
  subtractYear : function (e) {
389
 
    this.set("date", ydate.addYears(this.get("date"), -1));
390
 
    e.halt();
391
 
  },
392
 
 
393
 
  /**
394
 
   * Adds one month to the current calendar view.
395
 
   * @method addMonth
396
 
   */   
397
 
  addMonth : function (e) {    
398
 
    this.set("date", ydate.addMonths(this.get("date"), 1));
399
 
    e.halt();
400
 
  },
401
 
 
402
 
  /**
403
 
   * Adds one year to the current calendar view.
404
 
   * @method addYear
405
 
   */   
406
 
  addYear : function (e) {
407
 
    this.set("date", ydate.addYears(this.get("date"), 1));
408
 
    e.halt();
409
 
  }
410
 
},
411
 
 
412
 
{
413
 
   /**
414
 
    * The identity of the widget.
415
 
    *
416
 
    * @property NAME
417
 
    * @type String
418
 
    * @default 'calendar'
419
 
    * @readOnly
420
 
    * @protected
421
 
    * @static
422
 
    */  
423
 
  NAME: "calendar",
424
 
 
425
 
   /**
426
 
    * Static property used to define the default attribute configuration of
427
 
    * the Widget.
428
 
    *
429
 
    * @property ATTRS
430
 
    * @type {Object}
431
 
    * @protected
432
 
    * @static
433
 
    */  
434
 
  ATTRS: {
435
 
 
436
 
    /**
437
 
     * A setting specifying the type of selection the calendar allows.
438
 
     * Possible values include:
439
 
     * <ul>
440
 
     *   <li>`single` - One date at a time</li>
441
 
     *   <li>`multiple-sticky` - Multiple dates, selected one at a time (the dates "stick"). This option
442
 
     *   is appropriate for mobile devices, where function keys from the keyboard are not available.</li>
443
 
     *   <li>`multiple` - Multiple dates, selected with Ctrl/Meta keys for additional single
444
 
     *   dates, and Shift key for date ranges.</li>
445
 
     *
446
 
     * @attribute selectionMode
447
 
     * @type String
448
 
     * @default single
449
 
     */
450
 
    selectionMode: {
451
 
      value: "single"
452
 
    },
453
 
 
454
 
    /**
455
 
     * The date corresponding to the current calendar view. Always
456
 
     * normalized to the first of the month that contains the date
457
 
     * at assignment time. Used as the first date visible in the
458
 
     * calendar.
459
 
     *
460
 
     * @attribute date
461
 
     * @type Date
462
 
     * @default Today's date as set on the user's computer.
463
 
     */
464
 
    date: {
465
 
      value: new Date(),
466
 
      lazyAdd: false,
467
 
      setter: function (val) {
468
 
 
469
 
        var newDate = this._normalizeDate(val),
470
 
            newTopDate = ydate.addMonths(newDate, this._paneNumber - 1);
471
 
        var minDate = this.get("minimumDate");
472
 
        var maxDate = this.get("maximumDate");
473
 
            if ((!minDate || ydate.isGreaterOrEqual(newDate, minDate)) && 
474
 
                (!maxDate || ydate.isGreaterOrEqual(maxDate, newTopDate))) {
475
 
                return newDate;
476
 
            }
477
 
 
478
 
            else if (minDate && ydate.isGreater(minDate, newDate)) {
479
 
                   return minDate;
480
 
            }
481
 
 
482
 
            else if (maxDate && ydate.isGreater(newTopDate, maxDate)) {
483
 
                var actualMaxDate = ydate.addMonths(maxDate, -1*(this._paneNumber - 1));
484
 
                  return actualMaxDate;
485
 
            }
486
 
     }
487
 
    },
488
 
 
489
 
    /**
490
 
     * The minimum date that can be displayed by the calendar. The calendar will not
491
 
     * allow dates earlier than this one to be set, and will reset any earlier date to
492
 
     * this date. Should be `null` if no minimum date is needed.
493
 
     *
494
 
     * @attribute minimumDate
495
 
     * @type Date
496
 
     * @default null
497
 
     */
498
 
    minimumDate: {
499
 
      value: null,
500
 
      setter: function (val) {
501
 
        if (val) {
502
 
          var curDate = this.get('date'),
503
 
              newMinDate = this._normalizeDate(val);
504
 
          if (curDate && !ydate.isGreaterOrEqual(curDate, newMinDate)) {
505
 
              this.set('date', newMinDate);
506
 
          }
507
 
          return newMinDate;
508
 
        }
509
 
        else {
510
 
          return this._normalizeDate(val);
511
 
        }
512
 
      }
513
 
    },
514
 
 
515
 
    /**
516
 
     * The maximum date that can be displayed by the calendar. The calendar will not
517
 
     * allow dates later than this one to be set, and will reset any later date to
518
 
     * this date. Should be `null` if no maximum date is needed.
519
 
     *
520
 
     * @attribute maximumDate
521
 
     * @type Date
522
 
     * @default null
523
 
     */
524
 
    maximumDate: {
525
 
      value: null,
526
 
      setter: function (val) {
527
 
        if (val) {
528
 
          var curDate = this.get('date'),
529
 
              newMaxDate = this._normalizeDate(val);
530
 
          if (curDate && !ydate.isGreaterOrEqual(val, ydate.addMonths(curDate, this._paneNumber - 1))) {
531
 
              this.set('date', ydate.addMonths(newMaxDate, -1*(this._paneNumber -1)));
532
 
          }
533
 
          return newMaxDate;
534
 
        }
535
 
        else {
536
 
          return val;
537
 
        }
538
 
      }
539
 
    }
540
 
  }
541
 
});
542
 
 
543
 
 
544
 
}, '3.5.1' ,{requires:['calendar-base', 'calendarnavigator'], lang:['de', 'en', 'fr', 'ja', 'nb-NO', 'pt-BR', 'ru', 'zh-HANT-TW']});