datepicker_508 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2937 @@
1
+ /*!
2
+ * Accessible Datepicker v2.1.0
3
+ * Copyright 2015 Eureka2, Jacques Archimède.
4
+ * Based on the example of the Open AJAX Alliance Accessibility Tools Task Force : http://www.oaa-accessibility.org/examplep/datepicker1/
5
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6
+ */
7
+
8
+ /**
9
+ * Description:
10
+ * ===========
11
+ * This DatePicker widget allows the user to select a date.
12
+ * The DatePicker shows one month at least.
13
+ *
14
+ * The calendar portion of the date picker follows a table structure
15
+ * where days of the week and calendar day numbers are layed out in HTML table cells where WAI-ARIA semantics for a grid are applied.
16
+ * This provides context so an assistive technology can render the day of the week;
17
+ * its corresponding numeric calendar day, and week number if necessary.
18
+ *
19
+ * The calendar portion can be displayed in a numbers of ways, including as a popup associated with another widget,
20
+ * or as a static region of a page.
21
+ *
22
+ * This component complies with the recommendations of the guide http://www.w3.org/TR/wai-aria-practices/#datepicker of W3C, namely :
23
+ *
24
+ * Keyboard Interaction:
25
+ * =====================
26
+ *
27
+ * Keyboard navigation on days that are not included the currently displayed month should move to the month automatically and lead to the day in the next or previous month.
28
+ *
29
+ * - Tab - Like other widgets, the date picker widget receives focus by tabbing into it. Once focus is received, focus is repositioned on today's date in a grid of days and weeks. A second tab will take the user out of the date picker widget. Focus initially is placed on today's date.
30
+ * - Shift+Tab - reverses the direction of the tab order. Once in the widget, a Shift+Tab will take the user to the previous focusable element in the tab order.
31
+ * - Up Arrow and Down Arrow - goes to the same day of the week in the previous or next week respectively. If the user advances past the end of the month they continue into the next or previous month as appropriate.
32
+ * - Left Arrow and Right Arrow - advances one day to the next, also in a continuum. Visually focus is moved from day to day and wraps from row to row in a grid of days and weeks.
33
+ * - Control+Page Up - Moves to the same date in the previous year.
34
+ * - Control+Page Down - Moves to the same date in the next year.
35
+ * - Space -
36
+ * Singleton Mode: acts as a toggle either selecting or deselecting the date.
37
+ * Contiguous Mode: Similar to selecting a range of text. Space selects the first date. Shift+Arrows add to the selection. Pressing Space again deselects the previous selections and selects the current focused date.
38
+ * - Home - Moves to the first day of the current month.
39
+ * - End - Moves to the last day of the current month.
40
+ * - Page Up - Moves to the same date in the previous month.
41
+ * - Page Down - Moves to the same date in the next month.
42
+ * - Enter -
43
+ * If the the calendar is a popup attached to some other widget (e.g., a text field), then Enter dismisses the popup, and the selected date(s) are shown in the associated widget.
44
+ * If the calendar is a static region on the page, then Enter confirms the selected date(s).
45
+ * - Escape - in the case of a popup date picker, closes the widget without any action.
46
+ *
47
+ *
48
+ * WAI-ARIA Roles, States, and Properties:
49
+ * ======================================
50
+ *
51
+ * The current month has a label representing the month and year. It is advisable to use a role heading but is not essential. This "label" should have a unique ID.
52
+ * If the author would like to ensure that a label is announced by a screen reader, as the label changes, include live region properties with the label element: aria-live="assertive" and aria-atomic="true".
53
+ * The container for the day of week headers and numeric days of the week has a role of grid.
54
+ * The grid has an aria-labelled by property with a value equivalent to the id of the label for the grid.
55
+ * Each name for the day of the week has a role column header and is not navigable via the keyboard.
56
+ * Each numeric day of the week has the role gridcell.
57
+ * When a day is selected its aria-selected is set to true, otherwise it is set to false or removed.
58
+ * Changes in aria states, identified here, as well as focus, are clearly styled to show the user where their point of regard is and what days are selected.
59
+ *
60
+ * When the datepicker is active a calender day of the week always has focus.
61
+ * This can be achieved by setting the tab index on that day as appropriate and then using script to give it focus.
62
+ * The grid container set aria-active descendant to the id of the currently focused gridcell.
63
+ *
64
+ */
65
+
66
+ (function () {
67
+ "use strict";
68
+
69
+ if (typeof Date.dp_locales === 'undefined') {
70
+ Date.dp_locales = {
71
+ "texts": {
72
+ "buttonTitle": "Select date ...",
73
+ "buttonLabel": "Click or press the Enter key or the spacebar to open the calendar",
74
+ "prevButtonLabel": "Go to previous month",
75
+ "prevMonthButtonLabel": "Go to the previous year",
76
+ "prevYearButtonLabel": "Go to the previous twenty years",
77
+ "nextButtonLabel": "Go to next month",
78
+ "nextMonthButtonLabel": "Go to the next year",
79
+ "nextYearButtonLabel": "Go to the next twenty years",
80
+ "changeMonthButtonLabel": "Click or press the Enter key or the spacebar to change the month",
81
+ "changeYearButtonLabel": "Click or press the Enter key or the spacebar to change the year",
82
+ "changeRangeButtonLabel": "Click or press the Enter key or the spacebar to go to the next twenty years",
83
+ "closeButtonTitle": "Close",
84
+ "closeButtonLabel": "Close the calendar",
85
+ "calendarHelp": "- Up Arrow and Down Arrow - goes to the same day of the week in the previous or next week respectively. If the end of the month is reached, continues into the next or previous month as appropriate.\r\n- Left Arrow and Right Arrow - advances one day to the next, also in a continuum. Visually focus is moved from day to day and wraps from row to row in the grid of days.\r\n- Control+Page Up - Moves to the same date in the previous year.\r\n- Control+Page Down - Moves to the same date in the next year.\r\n- Home - Moves to the first day of the current month.\r\n- End - Moves to the last day of the current month.\r\n- Page Up - Moves to the same date in the previous month.\r\n- Page Down - Moves to the same date in the next month.\r\n- Enter or Espace - closes the calendar, and the selected date is shown in the associated text box.\r\n- Escape - closes the calendar without any action."
86
+ },
87
+ "directionality": "LTR",
88
+ "month_names": [
89
+ "January",
90
+ "February",
91
+ "March",
92
+ "April",
93
+ "May",
94
+ "June",
95
+ "July",
96
+ "August",
97
+ "September",
98
+ "October",
99
+ "November",
100
+ "December"
101
+ ],
102
+ "month_names_abbreviated": [
103
+ "Jan",
104
+ "Feb",
105
+ "Mar",
106
+ "Apr",
107
+ "May",
108
+ "Jun",
109
+ "Jul",
110
+ "Aug",
111
+ "Sep",
112
+ "Oct",
113
+ "Nov",
114
+ "Dec"
115
+ ],
116
+ "month_names_narrow": [
117
+ "J",
118
+ "F",
119
+ "M",
120
+ "A",
121
+ "M",
122
+ "J",
123
+ "J",
124
+ "A",
125
+ "S",
126
+ "O",
127
+ "N",
128
+ "D"
129
+ ],
130
+ "day_names": [
131
+ "Sunday",
132
+ "Monday",
133
+ "Tuesday",
134
+ "Wednesday",
135
+ "Thursday",
136
+ "Friday",
137
+ "Saturday"
138
+ ],
139
+ "day_names_abbreviated": [
140
+ "Sun",
141
+ "Mon",
142
+ "Tue",
143
+ "Wed",
144
+ "Thu",
145
+ "Fri",
146
+ "Sat"
147
+ ],
148
+ "day_names_short": [
149
+ "Su",
150
+ "Mo",
151
+ "Tu",
152
+ "We",
153
+ "Th",
154
+ "Fr",
155
+ "Sa"
156
+ ],
157
+ "day_names_narrow": [
158
+ "S",
159
+ "M",
160
+ "T",
161
+ "W",
162
+ "T",
163
+ "F",
164
+ "S"
165
+ ],
166
+ "day_periods": {
167
+ "am": "AM",
168
+ "noon": "noon",
169
+ "pm": "PM"
170
+ },
171
+ "day_periods_abbreviated": {
172
+ "am": "AM",
173
+ "noon": "noon",
174
+ "pm": "PM"
175
+ },
176
+ "day_periods_narrow": {
177
+ "am": "a",
178
+ "noon": "n",
179
+ "pm": "p"
180
+ },
181
+ "quarter_names": [
182
+ "1st quarter",
183
+ "2nd quarter",
184
+ "3rd quarter",
185
+ "4th quarter"
186
+ ],
187
+ "quarter_names_abbreviated": [
188
+ "Q1",
189
+ "Q2",
190
+ "Q3",
191
+ "Q4"
192
+ ],
193
+ "quarter_names_narrow": [
194
+ "1",
195
+ "2",
196
+ "3",
197
+ "4"
198
+ ],
199
+ "era_names": [
200
+ "Before Christ",
201
+ "Anno Domini"
202
+ ],
203
+ "era_names_abbreviated": [
204
+ "BC",
205
+ "AD"
206
+ ],
207
+ "era_names_narrow": [
208
+ "B",
209
+ "A"
210
+ ],
211
+ "full_format": "EEEE, MMMM d, y",
212
+ "long_format": "MMMM d, y",
213
+ "medium_format": "MMM d, y",
214
+ "short_format": "M/d/yy",
215
+ "firstday_of_week": 0
216
+ };
217
+ }
218
+ })();
219
+
220
+ (function(factory){
221
+ if (typeof define === "function" && define.amd) {
222
+ define(["jquery"], factory);
223
+ } else if (typeof exports === 'object') {
224
+ factory(require('jquery'));
225
+ } else {
226
+ if (typeof jQuery === 'undefined') {
227
+ throw new Error('Datepicker\'s JavaScript requires jQuery')
228
+ }
229
+ factory(jQuery);
230
+ }
231
+ }(function($, undefined){
232
+ 'use strict';
233
+
234
+ var datepickerButton = [
235
+ '<a class="datepicker-button input-group-addon" role="button" aria-haspopup="true" tabindex="0" aria-labelledby="datepicker-bn-open-label-CALENDARID">',
236
+ ' <span class="glyphicon glyphicon-calendar" title="Select Date..."></span>',
237
+ '</a>'
238
+ ];
239
+ var datepickerCalendar = [
240
+ '<div class="datepicker-calendar" id="datepicker-calendar-CALENDARID" aria-hidden="false">',
241
+ ' <div class="datepicker-month-wrap">',
242
+ ' <div class="datepicker-month-fast-next pull-right" role="button" aria-labelledby="datepicker-bn-fast-next-label-CALENDARID" tabindex="0"><span class="glyphicon glyphicon-forward"></span></div>',
243
+ ' <div class="datepicker-month-next pull-right" role="button" aria-labelledby="datepicker-bn-next-label-CALENDARID" tabindex="0"><span class="glyphicon glyphicon-triangle-right"></span></div>',
244
+ ' <div class="datepicker-month-fast-prev pull-left" role="button" aria-labelledby="datepicker-bn-fast-prev-label-CALENDARID" tabindex="0"><span class="glyphicon glyphicon-backward"></span></div>',
245
+ ' <div class="datepicker-month-prev pull-left" role="button" aria-labelledby="datepicker-bn-prev-label-CALENDARID" tabindex="0"><span class="glyphicon glyphicon-triangle-left"></span></div>',
246
+ ' <div id="datepicker-month-CALENDARID" class="datepicker-month" tabindex="0" role="heading" aria-live="assertive" aria-atomic="true" title="Click or press the Enter key or the spacebar to change the month">July 2015</div>',
247
+ ' </div>',
248
+ ' <table class="datepicker-grid" role="grid" aria-readonly="true" aria-activedescendant="datepicker-err-msg-CALENDARID" aria-labelledby="datepicker-month-CALENDARID" tabindex="0">',
249
+ ' <thead>',
250
+ ' <tr class="datepicker-weekdays" role="row">',
251
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Sunday">Su</abbr></th>',
252
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Monday">Mo</abbr></th>',
253
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Tuesday">Tu</abbr></th>',
254
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Wednesday">We</abbr></th>',
255
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Thursday">Th</abbr></th>',
256
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Friday">Fr</abbr></th>',
257
+ ' <th class="datepicker-day" role="columnheader"><abbr title="Saturday">Sa</abbr></th>',
258
+ ' </tr>',
259
+ ' </thead>',
260
+ ' <tbody>',
261
+ ' <tr>',
262
+ ' <td id="datepicker-err-msg-CALENDARID" colspan="7">Javascript must be enabled</td>',
263
+ ' </tr>',
264
+ ' </tbody>',
265
+ ' </table>',
266
+ ' <div class="datepicker-close-wrap">',
267
+ ' <button class="datepicker-close" id="datepicker-close-CALENDARID" aria-labelledby="datepicker-bn-close-label-CALENDARID">Close</button>',
268
+ ' </div>',
269
+ ' <div id="datepicker-bn-open-label-CALENDARID" class="datepicker-bn-open-label offscreen">Click or press the Enter key or the spacebar to open the calendar</div>',
270
+ ' <div id="datepicker-bn-prev-label-CALENDARID" class="datepicker-bn-prev-label offscreen">Go to previous month</div>',
271
+ ' <div id="datepicker-bn-next-label-CALENDARID" class="datepicker-bn-next-label offscreen">Go to next month</div>',
272
+ ' <div id="datepicker-bn-fast-prev-label-CALENDARID" class="datepicker-bn-fast-prev-label offscreen">Go to previous year</div>',
273
+ ' <div id="datepicker-bn-fast-next-label-CALENDARID" class="datepicker-bn-fast-next-label offscreen">Go to next year</div>',
274
+ ' <div id="datepicker-bn-close-label-CALENDARID" class="datepicker-bn-close-label offscreen">Close the date picker</div>',
275
+ '</div>'
276
+ ];
277
+
278
+ var Datepicker = function (target, options) {
279
+ var self = this;
280
+ this.$target = $(target); // textbox that will receive the selected date string and focus (if modal)
281
+ this.options = $.extend({}, Datepicker.DEFAULTS, options)
282
+ this.locales = Date.dp_locales;
283
+ if (typeof this.options.inputFormat === 'string') {
284
+ this.options.inputFormat = [this.options.inputFormat];
285
+ }
286
+ if (this.options.min != null) {
287
+ this.options.min = this.parseDate(this.options.min);
288
+ } else if (this.$target.attr('min')) {
289
+ this.options.min = this.parseDate(this.$target.attr('min'));
290
+ }
291
+ if (this.options.max != null) {
292
+ this.options.max = this.parseDate(this.options.max);
293
+ } else if (this.$target.attr('max')) {
294
+ this.options.max = this.parseDate(this.$target.attr('max'));
295
+ }
296
+ this.id = this.$target.attr('id') || 'datepicker-' + Math.floor(Math.random() * 100000);
297
+ var calendar = datepickerCalendar.join("");
298
+ calendar = calendar.replace(/CALENDARID/g, this.id + '');
299
+
300
+ // complete the target textbox if any
301
+ if (this.$target.parent('.input-group').length == 0) {
302
+ this.$target.wrap( '<div class="input-group"></div>' );
303
+ }
304
+ this.$label = this.$target.parents().find("label[for=" + this.id + "]");
305
+ this.$group = this.$target.parent('.input-group');
306
+ this.$target.attr('aria-autocomplete', 'none')
307
+ this.$target.css('min-width', '7em')
308
+ this.$target.addClass('form-control');
309
+
310
+ if (! this.$target.attr('placeholder')) {
311
+ this.$target.attr('placeholder', this.options.inputFormat[0]);
312
+ }
313
+
314
+ var button = datepickerButton.join("");
315
+ button = button.replace(/CALENDARID/g, this.id + '');
316
+ this.$button = $(button);
317
+ this.$button.addClass(this.options.theme);
318
+ this.$calendar = $(calendar);
319
+ this.$calendar.addClass(this.options.theme);
320
+ this.$target.after(this.$button);
321
+
322
+ // be sure parent of the calendar is positionned to calculate the position of the calendar
323
+ if (this.$calendar.parent().css('position') === 'static') {
324
+ this.$calendar.parent().css('position', 'relative');
325
+ }
326
+ this.$calendar.find('.datepicker-bn-open-label').html(this.options.buttonLabel);
327
+ if (this.$target.attr('id')) {
328
+ this.$calendar.attr('aria-controls', this.$target.attr('id'));
329
+ }
330
+ this.$button.find('span').attr('title', this.options.buttonTitle);
331
+ this.$calendar.css('left', this.$target.parent().position().left + 'px');
332
+ this.$monthObj = this.$calendar.find('.datepicker-month');
333
+ this.$prev = this.$calendar.find('.datepicker-month-prev');
334
+ this.$next = this.$calendar.find('.datepicker-month-next');
335
+ this.$fastprev = this.$calendar.find('.datepicker-month-fast-prev');
336
+ this.$fastnext = this.$calendar.find('.datepicker-month-fast-next');
337
+ this.$grid = this.$calendar.find('.datepicker-grid');
338
+ if (this.locales.directionality === 'RTL') {
339
+ this.$grid.addClass('rtl');
340
+ }
341
+ var $days = this.$grid.find('th.datepicker-day abbr');
342
+ this.drawCalendarHeader();
343
+ if (this.options.inline == false && this.options.modal == true) {
344
+ this.$close = this.$calendar.find('.datepicker-close');
345
+ this.$close.html(this.options.closeButtonTitle).attr('title', this.options.closeButtonLabel);
346
+ this.$calendar.find('.datepicker-bn-close-label').html(this.options.closeButtonLabel);
347
+ } else {
348
+ this.hideObject(this.$calendar.find('.datepicker-close-wrap'));
349
+ this.hideObject(this.$calendar.find('.datepicker-bn-close-label'));
350
+ }
351
+
352
+ this.keys = {
353
+ tab: 9,
354
+ enter: 13,
355
+ esc: 27,
356
+ space: 32,
357
+ pageup: 33,
358
+ pagedown: 34,
359
+ end: 35,
360
+ home: 36,
361
+ left: 37,
362
+ up: 38,
363
+ right: 39,
364
+ down: 40
365
+ };
366
+
367
+ this.bindHandlers();
368
+ this.$button.click(function(e) {
369
+ if (self.$calendar.attr('aria-hidden') === 'true') {
370
+ self.initializeDate();
371
+ self.show();
372
+ } else {
373
+ self.hide();
374
+ }
375
+ e.stopPropagation();
376
+ return false;
377
+ });
378
+ this.$button.keydown(function(e) {
379
+ var ev = e || event;
380
+ if(ev.keyCode == self.keys.enter || ev.keyCode == self.keys.space) {
381
+ $(this).trigger('click');
382
+ return false;
383
+ }
384
+ });
385
+ this.$calendar.on('blur', function(e) {
386
+ if (self.$calendar.attr('aria-hidden') === 'false') {
387
+ self.hide();
388
+ }
389
+
390
+ });
391
+
392
+ if (this.options.inline != false) {
393
+ this.hideObject(this.$button);
394
+ var $container = typeof this.options.inline === 'string' ? $('#' + this.options.inline) : this.options.inline;
395
+ $container.append(this.$calendar);
396
+ this.$calendar.css({position: 'relative', left: '0px'});
397
+ this.initializeDate();
398
+ } else {
399
+ this.$target.parent().after(this.$calendar);
400
+ this.hide();
401
+ }
402
+ }
403
+
404
+ Datepicker.VERSION = '2.1.0'
405
+
406
+ Datepicker.DEFAULTS = {
407
+ firstDayOfWeek: Date.dp_locales.firstday_of_week, // Determines the first column of the calendar grid
408
+ weekDayFormat: 'short', // Display format of the weekday names - values are 'short' or 'narrow'
409
+ daysOfWeekDisabled: [],
410
+ inputFormat: [Date.dp_locales.short_format],
411
+ outputFormat: Date.dp_locales.short_format,
412
+ titleFormat: Date.dp_locales.full_format,
413
+ buttonTitle: Date.dp_locales.texts.buttonTitle,
414
+ buttonLabel: Date.dp_locales.texts.buttonLabel,
415
+ prevButtonLabel: Date.dp_locales.texts.prevButtonLabel,
416
+ prevMonthButtonLabel: Date.dp_locales.texts.prevMonthButtonLabel,
417
+ prevYearButtonLabel: Date.dp_locales.texts.prevYearButtonLabel,
418
+ nextButtonLabel: Date.dp_locales.texts.nextButtonLabel,
419
+ nextMonthButtonLabel: Date.dp_locales.texts.nextMonthButtonLabel,
420
+ nextYearButtonLabel: Date.dp_locales.texts.nextYearButtonLabel,
421
+ changeMonthButtonLabel: Date.dp_locales.texts.changeMonthButtonLabel,
422
+ changeYearButtonLabel: Date.dp_locales.texts.changeYearButtonLabel,
423
+ changeRangeButtonLabel: Date.dp_locales.texts.changeRangeButtonLabel,
424
+ closeButtonTitle: Date.dp_locales.texts.closeButtonTitle,
425
+ closeButtonLabel: Date.dp_locales.texts.closeButtonLabel,
426
+ theme: 'default',
427
+ modal: false,
428
+ inline: false,
429
+ min: null,
430
+ max: null
431
+ }
432
+
433
+
434
+ /**
435
+ * initializeDate() is a member function to initialize the Datepicker date with the content of the target textbox
436
+ *
437
+ * @return N/A
438
+ *
439
+ */
440
+ Datepicker.prototype.initializeDate = function() {
441
+ var val = this.$target.val();
442
+ this.dateObj = val === '' ? new Date() : this.parseDate(val);
443
+ if (this.dateObj == null) {
444
+ this.$target.attr('aria-invalid', true);
445
+ this.$target.parents('.form-group').addClass('has-error');
446
+ this.dateObj = new Date();
447
+ this.dateObj.setHours(0, 0, 0, 0);
448
+ }
449
+ if (this.options.min != null && this.dateObj < this.options.min) {
450
+ this.$target.attr('aria-invalid', true);
451
+ this.$target.parents('.form-group').addClass('has-error');
452
+ this.dateObj = this.options.min;
453
+ } else if (this.options.max != null && this.dateObj > this.options.max) {
454
+ this.$target.attr('aria-invalid', true);
455
+ this.$target.parents('.form-group').addClass('has-error');
456
+ this.dateObj = this.options.max;
457
+ }
458
+ this.curYear = this.dateObj.getFullYear();
459
+ this.year = this.curYear;
460
+ this.curMonth = this.dateObj.getMonth();
461
+ this.month = this.curMonth;
462
+ this.date = this.dateObj.getDate();
463
+ // populate the calendar grid
464
+ this.populateDaysCalendar();
465
+ // update the table's activedescdendant to point to the current day
466
+ this.$grid.attr('aria-activedescendant', this.$grid.find('.curDay').attr('id'));
467
+ } // end initializeDate()
468
+
469
+ /**
470
+ * drawCalendarHeader() is a member function to populate the calendar header with the days name.
471
+ *
472
+ * @return N/A
473
+ */
474
+ Datepicker.prototype.drawCalendarHeader = function() {
475
+ var $days = this.$grid.find('th.datepicker-day abbr');
476
+ var weekday = this.options.firstDayOfWeek;
477
+ for (var i = 0; i < 7; i++) {
478
+ $days.eq(i).attr('title', this.locales.day_names[weekday]).text(
479
+ this.options.weekDayFormat === 'short' ?
480
+ this.locales.day_names_short[weekday] :
481
+ this.locales.day_names_narrow[weekday]
482
+ );
483
+ weekday = (weekday + 1) % 7;
484
+ }
485
+ } // end drawCalendarHeader()
486
+
487
+ /**
488
+ * populateDaysCalendar() is a member function to populate the datepicker grid with calendar days
489
+ * representing the current month
490
+ *
491
+ * @return N/A
492
+ *
493
+ */
494
+ Datepicker.prototype.populateDaysCalendar = function() {
495
+ this.$calendar.find('.datepicker-bn-prev-label').html(this.options.prevButtonLabel);
496
+ this.$calendar.find('.datepicker-bn-next-label').html(this.options.nextButtonLabel);
497
+ this.$calendar.find('.datepicker-bn-fast-prev-label').html(this.options.prevMonthButtonLabel);
498
+ this.$calendar.find('.datepicker-bn-fast-next-label').html(this.options.nextMonthButtonLabel);
499
+ if (this.options.min != null &&
500
+ ( this.year - 1 < this.options.min.getFullYear() ||
501
+ (this.year - 1 == this.options.min.getFullYear() && this.month < this.options.min.getMonth()))) {
502
+ this.$fastprev.attr('title', '');
503
+ this.$fastprev.addClass('disabled');
504
+ this.$fastprev.removeClass('enabled');
505
+ } else {
506
+ this.$fastprev.attr('title', this.options.prevMonthButtonLabel);
507
+ this.$fastprev.addClass('enabled');
508
+ this.$fastprev.removeClass('disabled');
509
+ }
510
+ var previousMonth = this.previousMonth(this.year, this.month);
511
+ if (this.options.min != null &&
512
+ ( previousMonth.year < this.options.min.getFullYear() ||
513
+ (previousMonth.year == this.options.min.getFullYear() && previousMonth.month < this.options.min.getMonth()))) {
514
+ this.$prev.attr('title', '');
515
+ this.$prev.addClass('disabled');
516
+ this.$prev.removeClass('enabled');
517
+ } else {
518
+ this.$prev.attr('title', this.options.prevButtonLabel);
519
+ this.$prev.addClass('enabled');
520
+ this.$prev.removeClass('disabled');
521
+ }
522
+ this.$monthObj.attr('title', this.options.changeMonthButtonLabel);
523
+ var nextMonth = this.nextMonth(this.year, this.month);
524
+ if (this.options.max != null &&
525
+ ( nextMonth.year > this.options.max.getFullYear() ||
526
+ (nextMonth.year == this.options.max.getFullYear() && nextMonth.month > this.options.max.getMonth()))) {
527
+ this.$next.attr('title', '');
528
+ this.$next.addClass('disabled');
529
+ this.$next.removeClass('enabled');
530
+ } else {
531
+ this.$next.attr('title', this.options.nextButtonLabel);
532
+ this.$next.addClass('enabled');
533
+ this.$next.removeClass('disabled');
534
+ }
535
+ if (this.options.max != null &&
536
+ ( this.year + 1 > this.options.max.getFullYear() ||
537
+ (this.year + 1 == this.options.max.getFullYear() && this.month > this.options.max.getMonth()))) {
538
+ this.$fastnext.attr('title', '');
539
+ this.$fastnext.addClass('disabled');
540
+ this.$fastnext.removeClass('enabled');
541
+ } else {
542
+ this.$fastnext.attr('title', this.options.nextMonthButtonLabel);
543
+ this.$fastnext.addClass('enabled');
544
+ this.$fastnext.removeClass('disabled');
545
+ }
546
+ this.showObject(this.$fastprev);
547
+ this.showObject(this.$fastnext);
548
+ var numDays = this.getDaysInMonth(this.year, this.month);
549
+ var numPrevDays = this.getDaysInMonth(previousMonth.year, previousMonth.month);
550
+ var startWeekday = new Date(this.year, this.month, 1).getDay();
551
+ var lastDayOfWeek = (this.options.firstDayOfWeek + 6) % 7;
552
+ var curDay = 1;
553
+ var rowCount = 1;
554
+ this.$monthObj.html(this.locales.month_names[this.month] + ' ' + this.year);
555
+ this.showObject(this.$grid.find('thead'));
556
+ // clear the grid
557
+ var gridCells = '\t<tr id="row0-'+this.id+'" role="row">\n';
558
+ // Insert the leading empty cells
559
+ var numEmpties = 0;
560
+ var weekday = this.options.firstDayOfWeek;
561
+ while (weekday != startWeekday) {
562
+ numEmpties++;
563
+ weekday = (weekday + 1) % 7;
564
+ }
565
+ for ( ; numEmpties > 0; numEmpties--) {
566
+ gridCells += '\t\t<td class="empty">' + (numPrevDays - numEmpties + 1) + '</td>\n';
567
+ }
568
+ // insert the days of the month.
569
+ for (curDay = 1; curDay <= numDays; curDay++) {
570
+ var date = new Date(this.year, this.month, curDay, 0, 0, 0, 0);
571
+ var longdate = this.formatDate(date, this.options.titleFormat);
572
+ var curDayClass = curDay == this.date && this.month == this.curMonth && this.year == this.curYear ? ' curDay' : '';
573
+ if ($.inArray(weekday, this.options.daysOfWeekDisabled) > -1) {
574
+ gridCells += '\t\t<td id="cell' + curDay + '-' + this.id + '" class="day unselectable' + curDayClass + '"';
575
+ } else if (this.options.min != null && date < this.options.min) {
576
+ gridCells += '\t\t<td id="cell' + curDay + '-' + this.id + '" class="day unselectable' + curDayClass + '"';
577
+ } else if (this.options.max != null && date > this.options.max) {
578
+ gridCells += '\t\t<td id="cell' + curDay + '-' + this.id + '" class="day unselectable' + curDayClass + '"';
579
+ } else {
580
+ gridCells += '\t\t<td id="cell' + curDay + '-' + this.id + '" class="day selectable' + curDayClass + '"';
581
+ }
582
+ gridCells += ' data-value="' + curDay + '"';
583
+ gridCells += ' title="' + longdate + '"';
584
+ gridCells += ' headers="row' + rowCount + '-' + this.id + ' ' + this.locales.day_names[weekday] + '" role="gridcell" aria-selected="false">' + curDay;
585
+ gridCells += '</td>';
586
+ if (weekday == lastDayOfWeek && curDay < numDays) {
587
+ // This was the last day of the week, close it out
588
+ // and begin a new one
589
+ gridCells += '\t</tr>\n\t<tr id="row' + rowCount + '-' + this.id + '" role="row">\n';
590
+ rowCount++;
591
+ }
592
+ if (curDay < numDays) {
593
+ weekday = (weekday + 1) % 7;
594
+ }
595
+ }
596
+ // Insert any trailing empty cells
597
+ while (weekday != lastDayOfWeek) {
598
+ gridCells += '\t\t<td class="empty">' + (++numEmpties) + '</td>\n';
599
+ weekday = (weekday + 1) % 7;
600
+ }
601
+ gridCells += '\t</tr>';
602
+ var $tbody = this.$grid.find('tbody');
603
+ $tbody.empty();
604
+ $tbody.append(gridCells);
605
+ this.gridType = 0; // 0 = days grid, 1 = months grid, 2 = years Grid
606
+ } // end populateDaysCalendar()
607
+
608
+ /**
609
+ * populateMonthsCalendar() is a member function to populate the datepicker grid with calendar months
610
+ * representing the current year
611
+ *
612
+ * @return N/A
613
+ *
614
+ */
615
+ Datepicker.prototype.populateMonthsCalendar = function() {
616
+ this.$calendar.find('.datepicker-bn-prev-label').html(this.options.prevMonthButtonLabel);
617
+ this.$calendar.find('.datepicker-bn-next-label').html(this.options.nextMonthButtonLabel);
618
+ this.hideObject(this.$fastprev);
619
+ this.hideObject(this.$fastnext);
620
+ if (this.options.min != null && this.year - 1 < this.options.min.getFullYear()) {
621
+ this.$prev.attr('title', '');
622
+ this.$prev.addClass('disabled');
623
+ this.$prev.removeClass('enabled');
624
+ } else {
625
+ this.$prev.attr('title', this.options.prevMonthButtonLabel);
626
+ this.$prev.addClass('enabled');
627
+ this.$prev.removeClass('disabled');
628
+ }
629
+ this.$monthObj.attr('title', this.options.changeYearButtonLabel);
630
+ if (this.options.max != null && this.year + 1 > this.options.max.getFullYear()) {
631
+ this.$next.attr('title', '');
632
+ this.$next.addClass('disabled');
633
+ this.$next.removeClass('enabled');
634
+ } else {
635
+ this.$next.attr('title', this.options.nextMonthButtonLabel);
636
+ this.$next.addClass('enabled');
637
+ this.$next.removeClass('disabled');
638
+ }
639
+ var curMonth = 0;
640
+ var rowCount = 1;
641
+ var $tbody = this.$grid.find('tbody');
642
+ this.$monthObj.html(this.year);
643
+ // clear the grid
644
+ this.hideObject(this.$grid.find('thead'));
645
+ $tbody.empty();
646
+ $('#datepicker-err-msg-' + this.id).empty();
647
+ var gridCells = '\t<tr id="row0-'+this.id+'" role="row">\n';
648
+ // insert the months of the year.
649
+ for (curMonth = 0; curMonth < 12; curMonth++) {
650
+ if (curMonth == this.month && this.year == this.curYear) {
651
+ gridCells += '\t\t<td id="cell' + (curMonth + 1) + '-' + this.id + '" class="month curMonth selectable"';
652
+ } else if (this.options.min != null && (this.year < this.options.min.getFullYear() || (this.year == this.options.min.getFullYear() && curMonth < this.options.min.getMonth()))) {
653
+ gridCells += '\t\t<td id="cell' + (curMonth + 1) + '-' + this.id + '" class="month unselectable"';
654
+ } else if (this.options.max != null && (this.year > this.options.max.getFullYear() || (this.year == this.options.max.getFullYear() && curMonth > this.options.max.getMonth()))) {
655
+ gridCells += '\t\t<td id="cell' + (curMonth + 1) + '-' + this.id + '" class="month unselectable"';
656
+ } else {
657
+ gridCells += '\t\t<td id="cell' + (curMonth + 1) + '-' + this.id + '" class="month selectable"';
658
+ }
659
+ gridCells += ' data-value="' + curMonth + '"';
660
+ gridCells += ' title="' + this.locales.month_names[curMonth] + ' ' + this.year + '"';
661
+ gridCells += ' role="gridcell" aria-selected="false">' + this.locales.month_names_abbreviated[curMonth];
662
+ gridCells += '</td>';
663
+ if (curMonth == 3 || curMonth == 7) {
664
+ gridCells += '\t</tr>\n\t<tr id="row' + rowCount + '-' + this.id + '" role="row">\n';
665
+ rowCount++;
666
+ }
667
+ }
668
+ gridCells += '\t</tr>';
669
+ $tbody.append(gridCells);
670
+ this.gridType = 1; // 0 = days grid, 1 = months grid, 2 = years Grid
671
+ } // end populateMonthsCalendar()
672
+
673
+ /**
674
+ * populateYearsCalendar() is a member function to populate the datepicker grid with 20 calendar years
675
+ * around the current year
676
+ *
677
+ * @return N/A
678
+ *
679
+ */
680
+ Datepicker.prototype.populateYearsCalendar = function() {
681
+ this.$calendar.find('.datepicker-bn-prev-label').html(this.options.prevYearButtonLabel);
682
+ this.$calendar.find('.datepicker-bn-next-label').html(this.options.nextYearButtonLabel);
683
+ this.hideObject(this.$fastprev);
684
+ this.hideObject(this.$fastnext);
685
+ if (this.options.min != null && this.year - 20 < this.options.min.getFullYear()) {
686
+ this.$prev.attr('title', '');
687
+ this.$prev.addClass('disabled');
688
+ this.$prev.removeClass('enabled');
689
+ } else {
690
+ this.$prev.attr('title', this.options.prevYearButtonLabel);
691
+ this.$prev.addClass('enabled');
692
+ this.$prev.removeClass('disabled');
693
+ }
694
+ this.$monthObj.attr('title', this.options.changeRangeButtonLabel);
695
+ if (this.options.max != null && this.year + 20 > this.options.max.getFullYear()) {
696
+ this.$next.attr('title', '');
697
+ this.$next.addClass('disabled');
698
+ this.$next.removeClass('enabled');
699
+ } else {
700
+ this.$next.attr('title', this.options.nextYearButtonLabel);
701
+ this.$next.addClass('enabled');
702
+ this.$next.removeClass('disabled');
703
+ }
704
+ var startYear = Math.floor(this.year / 10) * 10;
705
+ var endYear = startYear + 19;
706
+ var rowCount = 1;
707
+ var $tbody = this.$grid.find('tbody');
708
+ this.$monthObj.html(startYear + '-' + endYear);
709
+ // clear the grid
710
+ this.hideObject(this.$grid.find('thead'));
711
+ $tbody.empty();
712
+ $('#datepicker-err-msg-' + this.id).empty();
713
+ var gridCells = '\t<tr id="row0-'+this.id+'" role="row">\n';
714
+ // insert the months of the year.
715
+ for (var curYear = startYear; curYear <= endYear; curYear++) {
716
+ if (curYear == this.year) {
717
+ gridCells += '\t\t<td id="cell' + (curYear - startYear + 1) + '-' + this.id + '" class="year curYear selectable"';
718
+ } else if (this.options.min != null && (curYear < this.options.min.getFullYear())) {
719
+ gridCells += '\t\t<td id="cell' + (curYear - startYear + 1) + '-' + this.id + '" class="year unselectable"';
720
+ } else if (this.options.max != null && (curYear > this.options.max.getFullYear())) {
721
+ gridCells += '\t\t<td id="cell' + (curYear - startYear + 1) + '-' + this.id + '" class="year unselectable"';
722
+ } else {
723
+ gridCells += '\t\t<td id="cell' + (curYear - startYear + 1) + '-' + this.id + '" class="year selectable"';
724
+ }
725
+ gridCells += ' data-value="' + curYear + '"';
726
+ gridCells += ' title="' + curYear + '"';
727
+ gridCells += ' role="gridcell" aria-selected="false">' + curYear;
728
+ gridCells += '</td>';
729
+ var curPos = curYear - startYear;
730
+ if (curPos == 4 || curPos == 9 || curPos == 14) {
731
+ gridCells += '\t</tr>\n\t<tr id="row' + rowCount + '-' + this.id + '" role="row">\n';
732
+ rowCount++;
733
+ }
734
+ }
735
+ gridCells += '\t</tr>';
736
+ $tbody.append(gridCells);
737
+ this.gridType = 2; // 0 = days grid, 1 = months grid, 2 = years Grid
738
+ } // end populateYearsCalendar()
739
+
740
+ /**
741
+ * showDaysOfPrevMonth() is a member function to show the days of the previous month
742
+ *
743
+ * @param (offset int) offset may be used to specify an offset for setting
744
+ * focus on a day the specified number of days from the end of the month.
745
+ * @return true if the previous month is between the minimum and the maximum date otherwise return false
746
+ */
747
+ Datepicker.prototype.showDaysOfPrevMonth = function(offset) {
748
+ // show the previous month
749
+ var previousMonth = this.previousMonth(this.year, this.month);
750
+ if (this.options.min != null &&
751
+ ( previousMonth.year < this.options.min.getFullYear() ||
752
+ (previousMonth.year == this.options.min.getFullYear() && previousMonth.month < this.options.min.getMonth()))) {
753
+ return false;
754
+ }
755
+ this.month = previousMonth.month;
756
+ this.year = previousMonth.year;
757
+ // populate the calendar grid
758
+ this.populateDaysCalendar();
759
+
760
+ // if offset was specified, set focus on the last day - specified offset
761
+ if (offset != null) {
762
+ var numDays = this.getDaysInMonth(this.year, this.month);
763
+ var day = 'cell' + (numDays - offset) + '-' + this.id;
764
+ this.$grid.attr('aria-activedescendant', day);
765
+ $('#' + day).addClass('focus').attr('aria-selected', 'true');
766
+ }
767
+ return true;
768
+ } // end showDaysOfPrevMonth()
769
+
770
+ /**
771
+ * showDaysOfMonth() is a member function to show the days of the specified month
772
+ *
773
+ * @param (month int) the month to show.
774
+ * @return true if the month is between the minimum and the maximum date otherwise return false
775
+ */
776
+ Datepicker.prototype.showDaysOfMonth = function(month) {
777
+ if (this.options.min != null &&
778
+ ( this.year < this.options.min.getFullYear() ||
779
+ (this.year == this.options.min.getFullYear() && month < this.options.min.getMonth()))) {
780
+ return false;
781
+ }
782
+ if (this.options.max != null &&
783
+ ( this.year > this.options.max.getFullYear() ||
784
+ (this.year == this.options.max.getFullYear() && month > this.options.max.getMonth()))) {
785
+ return false;
786
+ }
787
+ this.month = month;
788
+ this.date = Math.min(this.date, this.getDaysInMonth(this.year, this.month));
789
+ this.populateDaysCalendar();
790
+ // update the table's activedescendant to point to the active day
791
+ var $active = this.$grid.find("tbody td[data-value='" + this.date + "']");
792
+ $active.addClass('focus').attr('aria-selected', 'true');
793
+ this.$grid.attr('aria-activedescendant', $active.attr('id'));
794
+ return true;
795
+ } // end showDaysOfMonth()
796
+
797
+
798
+ /**
799
+ * showMonthsOfPrevYear() is a member function to show the months of the previous year
800
+ *
801
+ * @param (offset int) offset may be used to specify an offset for setting
802
+ * focus on a month the specified number of months from the end of the year.
803
+ * @return true if the previous year is between the minimum and the maximum date otherwise return false
804
+ */
805
+ Datepicker.prototype.showMonthsOfPrevYear = function(offset) {
806
+ if (this.options.min != null && this.year - 1 < this.options.min.getFullYear()) {
807
+ return false;
808
+ }
809
+ // show the previous year
810
+ this.year--;
811
+ // populate the calendar grid
812
+ this.populateMonthsCalendar();
813
+
814
+ // if offset was specified, set focus on the last month - specified offset
815
+ if (offset != null) {
816
+ var month = 'cell' + (12 - offset) + '-' + this.id;
817
+ this.$grid.attr('aria-activedescendant', month);
818
+ $('#' + month).addClass('focus').attr('aria-selected', 'true');
819
+ }
820
+ return true;
821
+ } // end showMonthsOfPrevYear()
822
+
823
+ /**
824
+ * showMonthsOfYear() is a member function to show the months of the specified year
825
+ *
826
+ * @param (year int) the year to show.
827
+ * @return true if the year is between the minimum and the maximum date otherwise return false
828
+ */
829
+ Datepicker.prototype.showMonthsOfYear = function(year) {
830
+ if (this.options.min != null && year < this.options.min.getFullYear()) {
831
+ return false;
832
+ }
833
+ if (this.options.max != null && year > this.options.max.getFullYear()) {
834
+ return false;
835
+ }
836
+ this.year = year;
837
+ this.populateMonthsCalendar();
838
+ // update the table's activedescendant to point to the active month
839
+ var $active = this.$grid.find("tbody td[data-value='" + this.month + "']");
840
+ $active.addClass('focus').attr('aria-selected', 'true');
841
+ this.$grid.attr('aria-activedescendant', $active.attr('id'));
842
+ return true;
843
+ } // end showMonthsOfYear()
844
+
845
+
846
+ /**
847
+ * showYearsOfPrevRange() is a member function to show the years of the previous range of twenty years
848
+ *
849
+ * @param (offset int) offset may be used to specify an offset for setting
850
+ * focus on a year the specified number of years from the end of the range.
851
+ * @return true if the year - 20 is between the minimum and the maximum date otherwise return false
852
+ */
853
+ Datepicker.prototype.showYearsOfPrevRange = function(offset) {
854
+ if (this.options.min != null && this.year - 20 < this.options.min.getFullYear()) {
855
+ return false;
856
+ }
857
+ // show the previous range
858
+ this.year -= 20;
859
+ // populate the calendar grid
860
+ this.populateYearsCalendar();
861
+
862
+ // if offset was specified, set focus on the last month - specified offset
863
+ if (offset != null) {
864
+ var year = 'cell' + (20 - offset) + '-' + this.id;
865
+ this.$grid.attr('aria-activedescendant', year);
866
+ $('#' + year).addClass('focus').attr('aria-selected', 'true');
867
+ }
868
+ return true;
869
+ } // end showYearsOfPrevRange()
870
+
871
+ /**
872
+ * showDaysOfNextMonth() is a member function to show the next month
873
+ *
874
+ * @param (offset int) offset may be used to specify an offset for setting
875
+ * focus on a day the specified number of days from
876
+ * the beginning of the month.
877
+ * @return true if the nextmMonth is between the minimum and the maximum date otherwise return false
878
+ */
879
+ Datepicker.prototype.showDaysOfNextMonth = function(offset) {
880
+ // show the next month
881
+ var nextMonth = this.nextMonth(this.year, this.month);
882
+ if (this.options.max != null &&
883
+ ( nextMonth.year > this.options.max.getFullYear() ||
884
+ (nextMonth.year == this.options.max.getFullYear() && nextMonth.month > this.options.max.getMonth()))) {
885
+ return false;
886
+ }
887
+ this.month = nextMonth.month;
888
+ this.year = nextMonth.year;
889
+ // populate the calendar grid
890
+ this.populateDaysCalendar();
891
+
892
+ // if offset was specified, set focus on the first day + specified offset
893
+ if (offset != null) {
894
+ var day = 'cell' + offset + '-' + this.id;
895
+
896
+ this.$grid.attr('aria-activedescendant', day);
897
+ $('#' + day).addClass('focus').attr('aria-selected', 'true');
898
+ }
899
+ return true;
900
+ } // end showDaysOfNextMonth()
901
+
902
+ /**
903
+ * showMonthsOfNextYear() is a member function to show the months of next year
904
+ *
905
+ * @param (offset int) offset may be used to specify an offset for setting
906
+ * focus on a month the specified number of month from
907
+ * the beginning of the year.
908
+ * @return true if the next year is between the minimum and the maximum date otherwise return false
909
+ */
910
+ Datepicker.prototype.showMonthsOfNextYear = function(offset) {
911
+ if (this.options.max != null && this.year + 1 > this.options.max.getFullYear()) {
912
+ return false;
913
+ }
914
+ // show the next year
915
+ this.year++;
916
+ // populate the calendar grid
917
+ this.populateMonthsCalendar();
918
+
919
+ // if offset was specified, set focus on the first day + specified offset
920
+ if (offset != null) {
921
+ var month = 'cell' + offset + '-' + this.id;
922
+
923
+ this.$grid.attr('aria-activedescendant', month);
924
+ $('#' + month).addClass('focus').attr('aria-selected', 'true');
925
+ }
926
+ return true;
927
+ } // end showMonthsOfNextYear()
928
+
929
+ /**
930
+ * showYearsOfNextRange() is a member function to show the years of next range of years
931
+ *
932
+ * @param (offset int) offset may be used to specify an offset for setting
933
+ * focus on a year the specified number of years from
934
+ * the beginning of the range.
935
+ * @return true if the year + 20 is between the minimum and the maximum date otherwise return false
936
+ */
937
+ Datepicker.prototype.showYearsOfNextRange = function(offset) {
938
+ if (this.options.max != null && this.year + 20 > this.options.max.getFullYear()) {
939
+ return false;
940
+ }
941
+ // show the next year
942
+ this.year += 20;
943
+ // populate the calendar grid
944
+ this.populateYearsCalendar();
945
+
946
+ // if offset was specified, set focus on the first day + specified offset
947
+ if (offset != null) {
948
+ var year = 'cell' + offset + '-' + this.id;
949
+
950
+ this.$grid.attr('aria-activedescendant', year);
951
+ $('#' + year).addClass('focus').attr('aria-selected', 'true');
952
+ }
953
+ return true;
954
+ } // end showYearsOfNextRange()
955
+
956
+ /**
957
+ * showDaysOfPrevYear() is a member function to show the previous year
958
+ *
959
+ * @return true if the previous year is between the minimum and the maximum date otherwise return false
960
+ */
961
+ Datepicker.prototype.showDaysOfPrevYear = function() {
962
+ if (this.options.min != null &&
963
+ ( this.year - 1 < this.options.min.getFullYear() ||
964
+ (this.year - 1 == this.options.min.getFullYear() && this.month < this.options.min.getMonth()))) {
965
+ return false;
966
+ }
967
+ // decrement the year
968
+ this.year--;
969
+
970
+ // populate the calendar grid
971
+ this.populateDaysCalendar();
972
+ return true;
973
+ } // end showDaysOfPrevYear()
974
+
975
+ /**
976
+ * showDaysOfNextYear() is a member function to show the next year
977
+ *
978
+ * @return true if the next year is between the minimum and the maximum date otherwise return false
979
+ */
980
+ Datepicker.prototype.showDaysOfNextYear = function() {
981
+ if (this.options.max != null &&
982
+ ( this.year + 1 > this.options.max.getFullYear() ||
983
+ (this.year + 1 == this.options.max.getFullYear() && this.month > this.options.max.getMonth()))) {
984
+ return false;
985
+ }
986
+ // increment the year
987
+ this.year++;
988
+
989
+ // populate the calendar grid
990
+ this.populateDaysCalendar();
991
+ return true;
992
+ } // end showDaysOfNextYear()
993
+
994
+ /**
995
+ * bindHandlers() is a member function to bind event handlers for the widget
996
+ *
997
+ * @return N/A
998
+ */
999
+ Datepicker.prototype.bindHandlers = function() {
1000
+ var self = this;
1001
+
1002
+ // bind button handlers
1003
+ this.$fastprev.click(function(e) {
1004
+ return self.handleFastPrevClick(e);
1005
+ });
1006
+ this.$prev.click(function(e) {
1007
+ return self.handlePrevClick(e);
1008
+ });
1009
+ this.$next.click(function(e) {
1010
+ return self.handleNextClick(e);
1011
+ });
1012
+ this.$fastnext.click(function(e) {
1013
+ return self.handleFastNextClick(e);
1014
+ });
1015
+ this.$monthObj.click(function(e) {
1016
+ return self.handleMonthClick(e);
1017
+ });
1018
+ this.$monthObj.keydown(function(e) {
1019
+ return self.handleMonthKeyDown(e);
1020
+ });
1021
+ this.$fastprev.keydown(function(e) {
1022
+ return self.handleFastPrevKeyDown(e);
1023
+ });
1024
+ this.$prev.keydown(function(e) {
1025
+ return self.handlePrevKeyDown(e);
1026
+ });
1027
+ this.$next.keydown(function(e) {
1028
+ return self.handleNextKeyDown(e);
1029
+ });
1030
+ this.$fastnext.keydown(function(e) {
1031
+ return self.handleFastNextKeyDown(e);
1032
+ });
1033
+ if (this.options.modal == true) {
1034
+ this.$close.click(function(e) {
1035
+ return self.handleCloseClick(e);
1036
+ });
1037
+ this.$close.keydown(function(e) {
1038
+ return self.handleCloseKeyDown(e);
1039
+ });
1040
+ }
1041
+
1042
+ // bind grid handlers
1043
+ this.$grid.keydown(function(e) {
1044
+ return self.handleGridKeyDown(e);
1045
+ });
1046
+ this.$grid.keypress(function(e) {
1047
+ return self.handleGridKeyPress(e);
1048
+ });
1049
+ this.$grid.focus(function(e) {
1050
+ return self.handleGridFocus(e);
1051
+ });
1052
+ this.$grid.blur(function(e) {
1053
+ return self.handleGridBlur(e);
1054
+ });
1055
+ this.$grid.delegate('td', 'click', function(e) {
1056
+ return self.handleGridClick(this, e);
1057
+ });
1058
+ } // end bindHandlers();
1059
+
1060
+ /**
1061
+ * handleFastPrevClick() is a member function to process click events for the fast prev month button
1062
+ *
1063
+ * @param (e obj) e is the event object associated with the event
1064
+ *
1065
+ * @return (boolean) false if consuming event, true if propagating
1066
+ */
1067
+ Datepicker.prototype.handleFastPrevClick = function(e) {
1068
+ if (this.showDaysOfPrevYear()) {
1069
+ var active = this.$grid.attr('aria-activedescendant');
1070
+ if (this.month != this.curMonth || this.year != this.curYear) {
1071
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1072
+ } else {
1073
+ this.$grid.attr('aria-activedescendant', active);
1074
+ }
1075
+ this.$grid.focus();
1076
+ }
1077
+ e.stopPropagation();
1078
+ return false;
1079
+ } // end handleFastPrevClick()
1080
+
1081
+ /**
1082
+ * handlePrevClick() is a member function to process click events for the prev month button
1083
+ *
1084
+ * @param (e obj) e is the event object associated with the event
1085
+ *
1086
+ * @return (boolean) false if consuming event, true if propagating
1087
+ */
1088
+ Datepicker.prototype.handlePrevClick = function(e) {
1089
+ var active = this.$grid.attr('aria-activedescendant');
1090
+ switch (this.gridType) {
1091
+ case 0: // days grid
1092
+ var ok;
1093
+ if (e.ctrlKey) {
1094
+ ok = this.showDaysOfPrevYear();
1095
+ } else {
1096
+ ok = this.showDaysOfPrevMonth();
1097
+ }
1098
+ if (ok) {
1099
+ if (this.month != this.curMonth || this.year != this.curYear) {
1100
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1101
+ } else {
1102
+ this.$grid.attr('aria-activedescendant', active);
1103
+ }
1104
+ this.$grid.focus();
1105
+ }
1106
+ break;
1107
+ case 1: // months grid
1108
+ if (this.showMonthsOfPrevYear()) {
1109
+ if (this.year != this.curYear) {
1110
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1111
+ } else {
1112
+ this.$grid.attr('aria-activedescendant', active);
1113
+ }
1114
+ this.$grid.focus();
1115
+ }
1116
+ break;
1117
+ case 2: // years grid
1118
+ if (this.showYearsOfPrevRange()) {
1119
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1120
+ this.$grid.focus();
1121
+ }
1122
+ break;
1123
+ }
1124
+ e.stopPropagation();
1125
+ return false;
1126
+ } // end handlePrevClick()
1127
+
1128
+ /**
1129
+ * handleMonthClick() is a member function to process click events for the month header
1130
+ *
1131
+ * @param (e obj) e is the event object associated with the event
1132
+ *
1133
+ * @return (boolean) false if consuming event, true if propagating
1134
+ */
1135
+ Datepicker.prototype.handleMonthClick = function(e) {
1136
+ this.changeGrid(e);
1137
+ this.$grid.focus();
1138
+ e.stopPropagation();
1139
+ return false;
1140
+ } // end handleMonthClick()
1141
+
1142
+ /**
1143
+ * handleNextClick() is a member function to process click events for the next month button
1144
+ *
1145
+ * @param (e obj) e is the event object associated with the event
1146
+ *
1147
+ * @return (boolean) false if consuming event, true if propagating
1148
+ */
1149
+ Datepicker.prototype.handleNextClick = function(e) {
1150
+ var active = this.$grid.attr('aria-activedescendant');
1151
+ switch (this.gridType) {
1152
+ case 0: // days grid
1153
+ var ok;
1154
+ if (e.ctrlKey) {
1155
+ ok = this.showDaysOfNextYear();
1156
+ } else {
1157
+ ok = this.showDaysOfNextMonth();
1158
+ }
1159
+ if (ok) {
1160
+ if (this.month != this.curMonth || this.year != this.curYear) {
1161
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1162
+ } else {
1163
+ this.$grid.attr('aria-activedescendant', active);
1164
+ }
1165
+ this.$grid.focus();
1166
+ }
1167
+ break;
1168
+ case 1: // months grid
1169
+ if (this.showMonthsOfNextYear()) {
1170
+ if (this.year != this.curYear) {
1171
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1172
+ } else {
1173
+ this.$grid.attr('aria-activedescendant', active);
1174
+ }
1175
+ this.$grid.focus();
1176
+ }
1177
+ break;
1178
+ case 2: // years grid
1179
+ if (this.showYearsOfNextRange()) {
1180
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1181
+ this.$grid.focus();
1182
+ }
1183
+ break;
1184
+ }
1185
+ e.stopPropagation();
1186
+ return false;
1187
+
1188
+ } // end handleNextClick()
1189
+
1190
+ /**
1191
+ * handleFastNextClick() is a member function to process click events for the fast next month button
1192
+ *
1193
+ * @param (e obj) e is the event object associated with the event
1194
+ *
1195
+ * @return (boolean) false if consuming event, true if propagating
1196
+ */
1197
+ Datepicker.prototype.handleFastNextClick = function(e) {
1198
+ if (this.showDaysOfNextYear()) {
1199
+ var active = this.$grid.attr('aria-activedescendant');
1200
+ if (this.month != this.curMonth || this.year != this.curYear) {
1201
+ this.$grid.attr('aria-activedescendant', 'cell1' + '-' + this.id);
1202
+ } else {
1203
+ this.$grid.attr('aria-activedescendant', active);
1204
+ }
1205
+ this.$grid.focus();
1206
+ }
1207
+ e.stopPropagation();
1208
+ return false;
1209
+
1210
+ } // end handleFastNextClick()
1211
+
1212
+ /**
1213
+ * handleCloseClick() is a member function to process click events for the close button
1214
+ *
1215
+ * @param (e obj) e is the event object associated with the event
1216
+ *
1217
+ * @return (boolean) false if consuming event, true if propagating
1218
+ */
1219
+ Datepicker.prototype.handleCloseClick = function(e) {
1220
+ // dismiss the dialog box
1221
+ this.hide();
1222
+ e.stopPropagation();
1223
+ return false;
1224
+ } // end handleCloseClick()
1225
+
1226
+ /**
1227
+ * handleFastPrevKeyDown() is a member function to process keydown events for the fast prev month button
1228
+ *
1229
+ * @param (e obj) e is the event object associated with the event
1230
+ *
1231
+ * @return (boolean) false if consuming event, true if propagating
1232
+ */
1233
+ Datepicker.prototype.handleFastPrevKeyDown = function(e) {
1234
+ if (e.altKey) {
1235
+ return true;
1236
+ }
1237
+ switch (e.keyCode) {
1238
+ case this.keys.tab:
1239
+ {
1240
+ if (this.options.modal == false || e.ctrlKey) {
1241
+ return true;
1242
+ }
1243
+ if (e.shiftKey) {
1244
+ this.$close.focus();
1245
+ } else {
1246
+ this.$prev.focus();
1247
+ }
1248
+ e.stopPropagation();
1249
+ return false;
1250
+ }
1251
+ case this.keys.enter:
1252
+ case this.keys.space:
1253
+ {
1254
+ if (e.shiftKey || e.ctrlKey) {
1255
+ return true;
1256
+ }
1257
+ this.showDaysOfPrevYear();
1258
+ e.stopPropagation();
1259
+ return false;
1260
+
1261
+ }
1262
+ case this.keys.esc:
1263
+ {
1264
+ // dismiss the dialog box
1265
+ this.hide();
1266
+ e.stopPropagation();
1267
+ return false;
1268
+ }
1269
+ }
1270
+ return true;
1271
+ } // end handleFastPrevKeyDown()
1272
+
1273
+ /**
1274
+ * handlePrevKeyDown() is a member function to process keydown events for the prev month button
1275
+ *
1276
+ * @param (e obj) e is the event object associated with the event
1277
+ *
1278
+ * @return (boolean) false if consuming event, true if propagating
1279
+ */
1280
+ Datepicker.prototype.handlePrevKeyDown = function(e) {
1281
+ if (e.altKey) {
1282
+ return true;
1283
+ }
1284
+ switch (e.keyCode) {
1285
+ case this.keys.tab:
1286
+ {
1287
+ if (this.options.modal == false || e.ctrlKey) {
1288
+ return true;
1289
+ }
1290
+ if (e.shiftKey) {
1291
+ if (this.gridType == 0) {
1292
+ this.$fastprev.focus();
1293
+ } else {
1294
+ this.$close.focus();
1295
+ }
1296
+ } else {
1297
+ // @TODO : do not work with FF, Chrome, Safari but work with IE, why ?
1298
+ this.$monthObj.focus();
1299
+ }
1300
+ e.stopPropagation();
1301
+ return false;
1302
+ }
1303
+ case this.keys.enter:
1304
+ case this.keys.space:
1305
+ {
1306
+ if (e.shiftKey) {
1307
+ return true;
1308
+ }
1309
+ switch (this.gridType) {
1310
+ case 0: // days grid
1311
+ if (e.ctrlKey) {
1312
+ this.showDaysOfPrevYear();
1313
+ } else {
1314
+ this.showDaysOfPrevMonth();
1315
+ }
1316
+ break;
1317
+ case 1: // months grid
1318
+ this.showMonthsOfPrevYear();
1319
+ break;
1320
+ case 2: // years grid
1321
+ this.showYearsOfPrevRange();
1322
+ break;
1323
+ }
1324
+ e.stopPropagation();
1325
+ return false;
1326
+
1327
+ }
1328
+ case this.keys.esc:
1329
+ {
1330
+ // dismiss the dialog box
1331
+ this.hide();
1332
+ e.stopPropagation();
1333
+ return false;
1334
+ }
1335
+ }
1336
+ return true;
1337
+ } // end handlePrevKeyDown()
1338
+
1339
+ /**
1340
+ * handleMonthKeyDown() is a member function to process keydown events for the month title
1341
+ *
1342
+ * @param (e obj) e is the event object associated with the event
1343
+ *
1344
+ * @return (boolean) false if consuming event, true if propagating
1345
+ */
1346
+ Datepicker.prototype.handleMonthKeyDown = function(e) {
1347
+ if (e.altKey) {
1348
+ return true;
1349
+ }
1350
+ switch (e.keyCode) {
1351
+ case this.keys.tab:
1352
+ {
1353
+ if (this.options.modal == false || e.ctrlKey) {
1354
+ return true;
1355
+ }
1356
+ if (e.shiftKey) {
1357
+ this.$prev.focus();
1358
+ } else {
1359
+ this.$next.focus();
1360
+ }
1361
+ e.stopPropagation();
1362
+ return false;
1363
+ }
1364
+ case this.keys.enter:
1365
+ case this.keys.space:
1366
+ {
1367
+ this.changeGrid(e);
1368
+ e.stopPropagation();
1369
+ return false;
1370
+
1371
+ }
1372
+ case this.keys.esc:
1373
+ {
1374
+ // dismiss the dialog box
1375
+ this.hide();
1376
+ e.stopPropagation();
1377
+ return false;
1378
+ }
1379
+ }
1380
+ return true;
1381
+ } // end handleMonthKeyDown()
1382
+
1383
+ /**
1384
+ * handleNextKeyDown() is a member function to process keydown events for the next month button
1385
+ *
1386
+ * @param (e obj) e is the event object associated with the event
1387
+ *
1388
+ * @return (boolean) false if consuming event, true if propagating
1389
+ */
1390
+ Datepicker.prototype.handleNextKeyDown = function(e) {
1391
+ if (e.altKey) {
1392
+ return true;
1393
+ }
1394
+ switch (e.keyCode) {
1395
+ case this.keys.tab:
1396
+ {
1397
+ if (this.options.modal == false || e.ctrlKey) {
1398
+ return true;
1399
+ }
1400
+ if (e.shiftKey) {
1401
+ this.$monthObj.focus();
1402
+ } else {
1403
+ if (this.gridType == 0) {
1404
+ this.$fastnext.focus();
1405
+ } else {
1406
+ this.$grid.focus();
1407
+ }
1408
+ }
1409
+ e.stopPropagation();
1410
+ return false;
1411
+ }
1412
+ case this.keys.enter:
1413
+ case this.keys.space:
1414
+ {
1415
+ switch (this.gridType) {
1416
+ case 0: // days grid
1417
+ if (e.ctrlKey) {
1418
+ this.showDaysOfNextYear();
1419
+ } else {
1420
+ this.showDaysOfNextMonth();
1421
+ }
1422
+ break;
1423
+ case 1: // months grid
1424
+ this.showMonthsOfNextYear();
1425
+ break;
1426
+ case 2: // years grid
1427
+ this.showYearsOfNextRange();
1428
+ break;
1429
+ }
1430
+ e.stopPropagation();
1431
+ return false;
1432
+ }
1433
+ case this.keys.esc:
1434
+ {
1435
+ // dismiss the dialog box
1436
+ this.hide();
1437
+ e.stopPropagation();
1438
+ return false;
1439
+ }
1440
+ }
1441
+ return true;
1442
+ } // end handleNextKeyDown()
1443
+
1444
+ /**
1445
+ * handleFastNextKeyDown() is a member function to process keydown events for the fast next month button
1446
+ *
1447
+ * @param (e obj) e is the event object associated with the event
1448
+ *
1449
+ * @return (boolean) false if consuming event, true if propagating
1450
+ */
1451
+ Datepicker.prototype.handleFastNextKeyDown = function(e) {
1452
+ if (e.altKey) {
1453
+ return true;
1454
+ }
1455
+ switch (e.keyCode) {
1456
+ case this.keys.tab:
1457
+ {
1458
+ if (this.options.modal == false || e.ctrlKey) {
1459
+ return true;
1460
+ }
1461
+ if (e.shiftKey) {
1462
+ this.$next.focus();
1463
+ } else {
1464
+ this.$grid.focus();
1465
+ }
1466
+ e.stopPropagation();
1467
+ return false;
1468
+ }
1469
+ case this.keys.enter:
1470
+ case this.keys.space:
1471
+ {
1472
+ this.showDaysOfNextYear();
1473
+ e.stopPropagation();
1474
+ return false;
1475
+ }
1476
+ case this.keys.esc:
1477
+ {
1478
+ // dismiss the dialog box
1479
+ this.hide();
1480
+ e.stopPropagation();
1481
+ return false;
1482
+ }
1483
+ }
1484
+ return true;
1485
+ } // end handleFastNextKeyDown()
1486
+
1487
+ /**
1488
+ * handleCloseKeyDown() is a member function to process keydown events for the close button
1489
+ *
1490
+ * @param (e obj) e is the event object associated with the event
1491
+ *
1492
+ * @return (boolean) false if consuming event, true if propagating
1493
+ */
1494
+ Datepicker.prototype.handleCloseKeyDown = function(e) {
1495
+ if (e.altKey) {
1496
+ return true;
1497
+ }
1498
+ switch (e.keyCode) {
1499
+ case this.keys.tab:
1500
+ {
1501
+ if (e.ctrlKey) {
1502
+ return true;
1503
+ }
1504
+ if (e.shiftKey) {
1505
+ this.$grid.focus();
1506
+ } else {
1507
+ if (this.gridType == 0) {
1508
+ this.$fastprev.focus();
1509
+ } else {
1510
+ this.$prev.focus();
1511
+ }
1512
+ }
1513
+ e.stopPropagation();
1514
+ return false;
1515
+ }
1516
+ case this.keys.enter:
1517
+ case this.keys.esc:
1518
+ case this.keys.space:
1519
+ {
1520
+ if (e.shiftKey || e.ctrlKey) {
1521
+ return true;
1522
+ }
1523
+ // dismiss the dialog box
1524
+ this.hide();
1525
+ e.stopPropagation();
1526
+ return false;
1527
+
1528
+ }
1529
+ }
1530
+ return true;
1531
+ } // end handlePrevKeyDown()
1532
+
1533
+ /**
1534
+ * handleGridKeyDown() is a member function to process keydown events for the Datepicker grid
1535
+ *
1536
+ * @param (e obj) e is the event object associated with the event
1537
+ *
1538
+ * @return (boolean) false if consuming event, true if propagating
1539
+ */
1540
+ Datepicker.prototype.handleGridKeyDown = function(e) {
1541
+ var $curCell = $('#' + this.$grid.attr('aria-activedescendant'));
1542
+ var $cells = this.$grid.find('td.selectable');
1543
+ var colCount = this.$grid.find('tbody tr').eq(0).find('td').length;
1544
+ if (e.altKey) {
1545
+ return true;
1546
+ }
1547
+ switch (e.keyCode) {
1548
+ case this.keys.tab:
1549
+ {
1550
+ if (this.options.modal == true) {
1551
+ if (e.shiftKey) {
1552
+ if (this.gridType == 0) {
1553
+ this.$fastnext.focus();
1554
+ } else {
1555
+ this.$next.focus();
1556
+ }
1557
+ } else {
1558
+ this.$close.focus();
1559
+ }
1560
+ e.stopPropagation()
1561
+ return false;
1562
+ } else {
1563
+ // dismiss the dialog box
1564
+ this.hide();
1565
+ this.handleTabOut(e);
1566
+ e.stopPropagation();
1567
+ return false;
1568
+ }
1569
+ break;
1570
+ }
1571
+ case this.keys.enter:
1572
+ case this.keys.space:
1573
+ {
1574
+ if (e.ctrlKey) {
1575
+ return true;
1576
+ }
1577
+ switch (this.gridType) {
1578
+ case 0: // days grid
1579
+ // update the target box
1580
+ this.update();
1581
+ // dismiss the dialog box
1582
+ this.hide();
1583
+ break;
1584
+ case 1: // months grid
1585
+ this.showDaysOfMonth(parseInt($curCell.attr('data-value')));
1586
+ break;
1587
+ case 2: // years grid
1588
+ this.showMonthsOfYear(parseInt($curCell.attr('data-value')));
1589
+ break;
1590
+ }
1591
+ e.stopPropagation();
1592
+ return false;
1593
+ }
1594
+ case this.keys.esc:
1595
+ {
1596
+ // dismiss the dialog box
1597
+ this.hide();
1598
+ e.stopPropagation();
1599
+ return false;
1600
+ }
1601
+ case this.keys.left:
1602
+ case this.keys.right:
1603
+ {
1604
+ if ((e.keyCode == this.keys.left && this.locales.directionality === 'LTR') || (e.keyCode == this.keys.right && this.locales.directionality === 'RTL')) {
1605
+ if (e.ctrlKey || e.shiftKey) {
1606
+ return true;
1607
+ }
1608
+ var cellIndex = $cells.index($curCell) - 1;
1609
+ var $prevCell = null;
1610
+ if (cellIndex >= 0) {
1611
+ $prevCell = $cells.eq(cellIndex);
1612
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1613
+ $prevCell.addClass('focus').attr('aria-selected', 'true');
1614
+ this.$grid.attr('aria-activedescendant', $prevCell.attr('id'));
1615
+ } else {
1616
+ switch (this.gridType) {
1617
+ case 0: // days grid
1618
+ this.showDaysOfPrevMonth(0);
1619
+ break;
1620
+ case 1: // months grid
1621
+ this.showMonthsOfPrevYear(0);
1622
+ break;
1623
+ case 2: // years grid
1624
+ this.showYearsOfPrevRange(0);
1625
+ break;
1626
+ }
1627
+ }
1628
+ e.stopPropagation();
1629
+ return false;
1630
+ } else {
1631
+ if (e.ctrlKey || e.shiftKey) {
1632
+ return true;
1633
+ }
1634
+ var cellIndex = $cells.index($curCell) + 1;
1635
+ var $nextCell = null;
1636
+ if (cellIndex < $cells.length) {
1637
+ $nextCell = $cells.eq(cellIndex);
1638
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1639
+ $nextCell.addClass('focus').attr('aria-selected', 'true');
1640
+ this.$grid.attr('aria-activedescendant', $nextCell.attr('id'));
1641
+ } else {
1642
+ switch (this.gridType) {
1643
+ case 0: // days grid
1644
+ // move to the next month
1645
+ this.showDaysOfNextMonth(1);
1646
+ break;
1647
+ case 1: // months grid
1648
+ this.showMonthsOfNextYear(1);
1649
+ break;
1650
+ case 2: // years grid
1651
+ this.showYearsOfNextRange(1);
1652
+ break;
1653
+ }
1654
+ }
1655
+ e.stopPropagation();
1656
+ return false;
1657
+ }
1658
+ }
1659
+ case this.keys.up:
1660
+ {
1661
+ if (e.ctrlKey || e.shiftKey) {
1662
+ return true;
1663
+ }
1664
+ var cellIndex = $cells.index($curCell) - colCount;
1665
+ var $prevCell = null;
1666
+ if (cellIndex >= 0) {
1667
+ $prevCell = $cells.eq(cellIndex);
1668
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1669
+ $prevCell.addClass('focus').attr('aria-selected', 'true');
1670
+ this.$grid.attr('aria-activedescendant', $prevCell.attr('id'));
1671
+ } else {
1672
+ // move to appropriate day in previous month
1673
+ cellIndex = colCount - 1 - $cells.index($curCell);
1674
+ switch (this.gridType) {
1675
+ case 0: // days grid
1676
+ this.showDaysOfPrevMonth(cellIndex);
1677
+ break;
1678
+ case 1: // months grid
1679
+ this.showMonthsOfPrevYear(cellIndex);
1680
+ break;
1681
+ case 2: // years grid
1682
+ this.showYearsOfPrevRange(cellIndex);
1683
+ break;
1684
+ }
1685
+ }
1686
+ e.stopPropagation();
1687
+ return false;
1688
+ }
1689
+ case this.keys.down:
1690
+ {
1691
+ if (e.ctrlKey || e.shiftKey) {
1692
+ return true;
1693
+ }
1694
+ var cellIndex = $cells.index($curCell) + colCount;
1695
+ var $nextCell = null;
1696
+ if (cellIndex < $cells.length) {
1697
+ $nextCell = $cells.eq(cellIndex);
1698
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1699
+ $nextCell.addClass('focus').attr('aria-selected', 'true');
1700
+ this.$grid.attr('aria-activedescendant', $nextCell.attr('id'));
1701
+ } else {
1702
+ // move to appropriate day in next month
1703
+ cellIndex = colCount + 1 - ($cells.length - $cells.index($curCell));
1704
+ switch (this.gridType) {
1705
+ case 0: // days grid
1706
+ this.showDaysOfNextMonth(cellIndex);
1707
+ break;
1708
+ case 1: // months grid
1709
+ this.showMonthsOfNextYear(cellIndex);
1710
+ break;
1711
+ case 2: // years grid
1712
+ this.showYearsOfNextRange(cellIndex);
1713
+ break;
1714
+ }
1715
+ }
1716
+ e.stopPropagation();
1717
+ return false;
1718
+ }
1719
+ case this.keys.pageup:
1720
+ {
1721
+ var active = this.$grid.attr('aria-activedescendant');
1722
+ if (e.shiftKey) {
1723
+ return true;
1724
+ }
1725
+ e.preventDefault();
1726
+ var ok = false;
1727
+ switch (this.gridType) {
1728
+ case 0: // days grid
1729
+ if (e.ctrlKey) {
1730
+ e.stopImmediatePropagation();
1731
+ ok = this.showDaysOfPrevYear();
1732
+ } else {
1733
+ ok = this.showDaysOfPrevMonth();
1734
+ }
1735
+ break;
1736
+ case 1: // months grid
1737
+ ok = this.showMonthsOfPrevYear();
1738
+ break;
1739
+ case 2: // years grid
1740
+ ok = this.showYearsOfPrevRange();
1741
+ break;
1742
+ }
1743
+ if (ok) {
1744
+ if ($('#' + active).attr('id') == undefined) {
1745
+ var $lastCell = $cells.eq($cells.length - 1);
1746
+ $lastCell.addClass('focus').attr('aria-selected', 'true');
1747
+ this.$grid.attr('aria-activedescendant', $lastCell.attr('id'));
1748
+ } else {
1749
+ $('#' + active).addClass('focus').attr('aria-selected', 'true');
1750
+ }
1751
+ }
1752
+ e.stopPropagation();
1753
+ return false;
1754
+ }
1755
+ case this.keys.pagedown:
1756
+ {
1757
+ var active = this.$grid.attr('aria-activedescendant');
1758
+ if (e.shiftKey) {
1759
+ return true;
1760
+ }
1761
+ e.preventDefault();
1762
+ var ok = false;
1763
+ switch (this.gridType) {
1764
+ case 0: // days grid
1765
+ if (e.ctrlKey) {
1766
+ e.stopImmediatePropagation();
1767
+ ok = this.showDaysOfNextYear();
1768
+ } else {
1769
+ ok = this.showDaysOfNextMonth();
1770
+ }
1771
+ break;
1772
+ case 1: // months grid
1773
+ ok = this.showMonthsOfNextYear();
1774
+ break;
1775
+ case 2: // years grid
1776
+ ok = this.showYearsOfNextRange();
1777
+ break;
1778
+ }
1779
+ if (ok) {
1780
+ if ($('#' + active).attr('id') == undefined) {
1781
+ var $lastCell = $cells.eq($cells.length - 1);
1782
+ $lastCell.addClass('focus').attr('aria-selected', 'true');
1783
+ this.$grid.attr('aria-activedescendant', $lastCell.attr('id'));
1784
+ } else {
1785
+ $('#' + active).addClass('focus').attr('aria-selected', 'true');
1786
+ }
1787
+ }
1788
+ e.stopPropagation();
1789
+ return false;
1790
+ }
1791
+ case this.keys.home:
1792
+ {
1793
+ if (e.ctrlKey || e.shiftKey) {
1794
+ return true;
1795
+ }
1796
+ var $firstCell = $cells.eq(0);
1797
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1798
+ $firstCell.addClass('focus').attr('aria-selected', 'true');
1799
+ this.$grid.attr('aria-activedescendant', $firstCell.attr('id'));
1800
+ e.stopPropagation();
1801
+ return false;
1802
+ }
1803
+ case this.keys.end:
1804
+ {
1805
+ if (e.ctrlKey || e.shiftKey) {
1806
+ return true;
1807
+ }
1808
+ var $lastCell = $cells.eq($cells.length - 1);
1809
+ $curCell.removeClass('focus').attr('aria-selected', 'false');
1810
+ $lastCell.addClass('focus').attr('aria-selected', 'true');
1811
+ this.$grid.attr('aria-activedescendant', $lastCell.attr('id'));
1812
+ e.stopPropagation();
1813
+ return false;
1814
+ }
1815
+ }
1816
+ return true;
1817
+ } // end handleGridKeyDown()
1818
+
1819
+ /**
1820
+ * handleGridKeyPress() is a member function to consume keypress events for browsers that
1821
+ * use keypress to scroll the screen and manipulate tabs
1822
+ *
1823
+ * @param (e obj) e is the event object associated with the event
1824
+ *
1825
+ * @return (boolean) false if consuming event, true if propagating
1826
+ */
1827
+ Datepicker.prototype.handleGridKeyPress = function(e) {
1828
+ if (e.altKey) {
1829
+ return true;
1830
+ }
1831
+ switch (e.keyCode) {
1832
+ case this.keys.tab:
1833
+ case this.keys.enter:
1834
+ case this.keys.space:
1835
+ case this.keys.esc:
1836
+ case this.keys.left:
1837
+ case this.keys.right:
1838
+ case this.keys.up:
1839
+ case this.keys.down:
1840
+ case this.keys.pageup:
1841
+ case this.keys.pagedown:
1842
+ case this.keys.home:
1843
+ case this.keys.end:
1844
+ {
1845
+ e.stopPropagation();
1846
+ return false;
1847
+ }
1848
+ }
1849
+ return true;
1850
+ } // end handleGridKeyPress()
1851
+
1852
+ /**
1853
+ * handleGridClick() is a member function to process mouse click events for the Datepicker grid
1854
+ *
1855
+ * @param (id string) id is the id of the object triggering the event
1856
+ *
1857
+ * @param (e obj) e is the event object associated with the event
1858
+ *
1859
+ * @return (boolean) false if consuming event, true if propagating
1860
+ */
1861
+ Datepicker.prototype.handleGridClick = function(id, e) {
1862
+ var $cell = $(id);
1863
+ if ($cell.is('.empty') || $cell.is('.unselectable')) {
1864
+ return true;
1865
+ }
1866
+ this.$grid.find('.focus').removeClass('focus').attr('aria-selected', 'false');
1867
+ switch (this.gridType) {
1868
+ case 0: // days grid
1869
+ $cell.addClass('focus').attr('aria-selected', 'true');
1870
+ this.$grid.attr('aria-activedescendant', $cell.attr('id'));
1871
+ // update the target box
1872
+ this.update();
1873
+ // dismiss the dialog box
1874
+ this.hide();
1875
+ break;
1876
+ case 1: // months grid
1877
+ this.showDaysOfMonth(parseInt($cell.attr('data-value')));
1878
+ break;
1879
+ case 2: // years grid
1880
+ this.showMonthsOfYear(parseInt($cell.attr('data-value')));
1881
+ break;
1882
+ }
1883
+ e.stopPropagation();
1884
+ return false;
1885
+ } // end handleGridClick()
1886
+
1887
+ /**
1888
+ * handleGridFocus() is a member function to process focus events for the Datepicker grid
1889
+ *
1890
+ * @param (e obj) e is the event object associated with the event
1891
+ *
1892
+ * @return (boolean) true
1893
+ */
1894
+ Datepicker.prototype.handleGridFocus = function(e) {
1895
+ var active = this.$grid.attr('aria-activedescendant');
1896
+ if ($('#' + active).attr('id') == undefined) {
1897
+ var $cells = this.$grid.find('td.selectable');
1898
+ var $lastCell = $cells.eq($cells.length - 1);
1899
+ $lastCell.addClass('focus').attr('aria-selected', 'true');
1900
+ this.$grid.attr('aria-activedescendant', $lastCell.attr('id'));
1901
+ } else {
1902
+ $('#' + active).addClass('focus').attr('aria-selected', 'true');
1903
+ }
1904
+ return true;
1905
+ } // end handleGridFocus()
1906
+
1907
+ /**
1908
+ * handleGridBlur() is a member function to process blur events for the Datepicker grid
1909
+ * @param (e obj) e is the event object associated with the event
1910
+ *
1911
+ * @return (boolean) true
1912
+ */
1913
+ Datepicker.prototype.handleGridBlur = function(e) {
1914
+ $('#' + this.$grid.attr('aria-activedescendant')).removeClass('focus').attr('aria-selected', 'false');
1915
+ return true;
1916
+ } // end handleGridBlur()
1917
+
1918
+ /**
1919
+ * handleTabOut() is a member function to process tab key in Datepicker grid
1920
+ *
1921
+ * @param (e obj) e is the event object associated with the event
1922
+ * @return (boolean) true
1923
+ */
1924
+ Datepicker.prototype.handleTabOut = function(e) {
1925
+ var fields = $('body').find('input:visible,textarea:visible,select:visible');
1926
+ var index = fields.index( this.$target );
1927
+ if ( index > -1 && index < fields.length ) {
1928
+ if (e.shiftKey) {
1929
+ if (index > 0) {
1930
+ index--;
1931
+ }
1932
+ } else {
1933
+ if (index + 1 < fields.length) {
1934
+ index++;
1935
+ }
1936
+ }
1937
+ fields.eq( index ).focus();
1938
+ }
1939
+ return true;
1940
+ } // end handleTabOut()
1941
+
1942
+ /**
1943
+ * changeGrid() is a member function to change the calendar after click or enter into the calendar title
1944
+ *
1945
+ * @param (e obj) e is the event object associated with the event
1946
+ * @return true
1947
+ */
1948
+ Datepicker.prototype.changeGrid = function(e) {
1949
+ switch (this.gridType) {
1950
+ case 0: // days grid
1951
+ this.populateMonthsCalendar();
1952
+ if (this.year != this.curYear) {
1953
+ var $cells = this.$grid.find('td.selectable');
1954
+ this.$grid.attr('aria-activedescendant', $cells.eq(0).attr('id'));
1955
+ } else {
1956
+ this.$grid.attr('aria-activedescendant', this.$grid.find('.curMonth').attr('id'));
1957
+ }
1958
+ break;
1959
+ case 2: // years grid
1960
+ if (e.shiftKey) {
1961
+ // goto previous twenty years
1962
+ this.year -= 20;
1963
+ } else {
1964
+ // goto next twenty years
1965
+ this.year += 20;
1966
+ }
1967
+ case 1: // months grid
1968
+ this.populateYearsCalendar();
1969
+ if (this.year != this.curYear) {
1970
+ var $cells = this.$grid.find('td.selectable');
1971
+ this.$grid.attr('aria-activedescendant', $cells.eq(0).attr('id'));
1972
+ } else {
1973
+ this.$grid.attr('aria-activedescendant', this.$grid.find('.curYear').attr('id'));
1974
+ }
1975
+ break;
1976
+ }
1977
+ return true;
1978
+ } // end changeGrid()
1979
+
1980
+ /**
1981
+ * update() is a member function to update the target textbox.
1982
+ *
1983
+ * @return N/A
1984
+ */
1985
+ Datepicker.prototype.update = function() {
1986
+ var $curDay = $('#' + this.$grid.attr('aria-activedescendant'));
1987
+ var date = new Date(this.year, this.month, parseInt($curDay.attr('data-value')));
1988
+ this.$target.val(this.formatDate(date, this.options.outputFormat));
1989
+ this.$target.removeAttr('aria-invalid');
1990
+ this.$target.parents('.form-group').removeClass('has-error');
1991
+ this.$target.trigger('change');
1992
+ } // end update()
1993
+
1994
+ /**
1995
+ * hideObject() is a member function to hide an element of the datepicker.
1996
+ *
1997
+ * @param ($element jQuery object) the element to hide
1998
+ * @return N/A
1999
+ */
2000
+ Datepicker.prototype.hideObject = function($element) {
2001
+ $element.attr('aria-hidden', true);
2002
+ $element.hide();
2003
+ } // end hideObject()
2004
+
2005
+ /**
2006
+ * showObject() is a member function to show an element of the datepicker.
2007
+ *
2008
+ * @param ($element jQuery object) the element to show
2009
+ * @return N/A
2010
+ */
2011
+ Datepicker.prototype.showObject = function($element) {
2012
+ $element.attr('aria-hidden', false);
2013
+ $element.show();
2014
+ } // end showObject()
2015
+
2016
+ /**
2017
+ * show() is a member function to show the Datepicker and give it focus.
2018
+ *
2019
+ * @return N/A
2020
+ */
2021
+ Datepicker.prototype.show = function() {
2022
+ var self = this;
2023
+ $('.datepicker-calendar').trigger('ab.datepicker.opening', [self.id]);
2024
+ if (this.options.modal == true) {
2025
+ // Bind an event listener to the document to capture all mouse events to make dialog modal
2026
+ $(document).bind('click mousedown mouseup', function(e) {
2027
+ //ensure focus remains on the dialog
2028
+ self.$grid.focus();
2029
+ // Consume all mouse events and do nothing
2030
+ e.stopPropagation;
2031
+ return false;
2032
+ });
2033
+ this.greyOut(true);
2034
+ var zIndex = parseInt($('#datepicker-overlay').css('z-index')) || 40;
2035
+ this.$calendar.css('z-index', zIndex + 1);
2036
+ } else {
2037
+ // Bind an event listener to the document to capture only the mouse click event
2038
+ $(document).bind('click', $.proxy(this.handleDocumentClick, this));
2039
+ this.$calendar.bind('ab.datepicker.opening', function(e, id) {
2040
+ if (id != self.id) {
2041
+ self.hide();
2042
+ } else {
2043
+ //ensure focus remains on the dialog
2044
+ self.$grid.focus();
2045
+ }
2046
+ });
2047
+
2048
+ }
2049
+ this.$calendar.bind('ab.datepicker.opened', function(e, id) {
2050
+ if (id == self.id) {
2051
+ self.$grid.focus();
2052
+ }
2053
+ });
2054
+
2055
+ // adjust position of the calendar
2056
+
2057
+ // This is a fix for the null return .top and .left function calls in the default library
2058
+ // By Adam Freemer Feburary 2017
2059
+ var labelOffsetTop = this.$label;
2060
+ if (labelOffsetTop.length == 0) {
2061
+ var groupOffsetTop = Math.max(0, Math.floor(this.$group.offset().top));
2062
+ var groupOffsetLeft = Math.max(0, Math.floor(this.$group.offset().left));
2063
+ } else {
2064
+ var groupOffsetTop = Math.max(0, Math.floor(this.$group.offset().top - this.$label.offset().top));
2065
+ var groupOffsetLeft = Math.max(0, Math.floor(this.$group.offset().left - this.$label.offset().left));
2066
+ }
2067
+
2068
+ var parentPaddingLeft = parseInt(this.$calendar.parent().css('padding-left'));
2069
+ var calendarHeight = this.$calendar.outerHeight();
2070
+ var groupTop = this.$group.offset().top;
2071
+ var groupLeft = this.$group.offset().left;
2072
+ var groupHeight = this.$group.outerHeight(true);
2073
+ var roomBefore = Math.floor(groupTop - $(window).scrollTop());
2074
+ var roomAfter = Math.floor($(window).height() - (groupTop + groupHeight - $(window).scrollTop()));
2075
+ if (roomAfter < calendarHeight && roomAfter < roomBefore) {
2076
+ // show calendar above group
2077
+ this.$calendar.addClass('above');
2078
+ this.$calendar.css({
2079
+ top: (groupOffsetTop - calendarHeight) + 'px',
2080
+ left: (groupOffsetLeft + parentPaddingLeft) + 'px'
2081
+ });
2082
+ } else {
2083
+ // show calendar below group
2084
+ this.$calendar.addClass('below');
2085
+ this.$calendar.css({
2086
+ top: (groupHeight + groupOffsetTop) + 'px',
2087
+ left: (groupOffsetLeft + parentPaddingLeft) + 'px'
2088
+ });
2089
+ }
2090
+
2091
+ // show the dialog
2092
+ this.$calendar.attr('aria-hidden', 'false');
2093
+ this.$calendar.fadeIn();
2094
+ $('.datepicker-calendar').trigger('ab.datepicker.opened', [self.id]);
2095
+ } // end show()
2096
+
2097
+ /**
2098
+ * refresh() is a member function to refesh the datepicker content when an option change.
2099
+ *
2100
+ * @return N/A
2101
+ */
2102
+ Datepicker.prototype.refresh = function() {
2103
+ this.drawCalendarHeader();
2104
+ switch (this.gridType) {
2105
+ case 0:
2106
+ this.populateDaysCalendar();
2107
+ break;
2108
+ case 1:
2109
+ this.populateMonthsCalendar();
2110
+ break;
2111
+ case 2:
2112
+ this.populateYearsCalendar();
2113
+ break;
2114
+ }
2115
+ } // end refresh()
2116
+
2117
+ /**
2118
+ * handleDocumentClick() is a member function to handle click on document.
2119
+ *
2120
+ * @param (e obj) e is the event object associated with the event
2121
+ *
2122
+ * @return (boolean) false if consuming event, true if propagating
2123
+ */
2124
+ Datepicker.prototype.handleDocumentClick = function(e) {
2125
+ if ($(e.target).parents('#datepicker-calendar-' + this.id).length == 0) {
2126
+ this.hide();
2127
+ return true;
2128
+ } else {
2129
+ //ensure focus remains on the dialog
2130
+ this.$grid.focus();
2131
+ // Consume all mouse events and do nothing
2132
+ e.stopPropagation;
2133
+ return false;
2134
+ }
2135
+ } // end handleDocumentClick()
2136
+
2137
+ /**
2138
+ * hide() is a member function to hide the Datepicker and remove focus.
2139
+ *
2140
+ * @return N/A
2141
+ */
2142
+ Datepicker.prototype.hide = function() {
2143
+ if (this.options.inline == false) {
2144
+ var self = this;
2145
+ // unbind the modal event sinks
2146
+ if (this.options.modal == true) {
2147
+ $(document).unbind('click mousedown mouseup');
2148
+ this.greyOut(false);
2149
+ } else {
2150
+ $(document).unbind('click', self.handleDocumentClick);
2151
+ this.$calendar.unbind('ab.datepicker.opening');
2152
+ }
2153
+ // hide the dialog
2154
+ this.$calendar.removeClass('above below');
2155
+ this.$calendar.attr('aria-hidden', 'true');
2156
+ this.$calendar.fadeOut();
2157
+ $('.datepicker-calendar').trigger('ab.datepicker.closed', [self.id]);
2158
+ // set focus on the focus target
2159
+ // Below commented out by Adam Freemer for preventing datepickers to have focus on-load.
2160
+ // this.$target.focus();
2161
+ }
2162
+ } // end hide()
2163
+
2164
+ /**
2165
+ * greyOut() is a member function to grey out the document background.
2166
+ *
2167
+ * @return N/A
2168
+ */
2169
+ Datepicker.prototype.greyOut = function(on) {
2170
+ var $overlay = $('#datepicker-overlay');
2171
+ if ($overlay.length == 0 && on) {
2172
+ $('body').append('<div id="datepicker-overlay" class="datepicker-overlay"></div>');
2173
+ $overlay = $('#datepicker-overlay');
2174
+
2175
+ /* // compute z-index for overlay
2176
+ var zmax = 0;
2177
+ $('*').each(function() {
2178
+ var cur = parseInt($(this).css('z-index'), 10);
2179
+ if (! isNaN(cur)) zmax = Math.max(zmax, cur);
2180
+ });
2181
+ $overlay.attr('z-index', zmax + 10); */
2182
+ }
2183
+ if (on) {
2184
+ $overlay.fadeIn(500);
2185
+ } else {
2186
+ $overlay.fadeOut(500);
2187
+ }
2188
+ } // end greyOut()
2189
+
2190
+ /**
2191
+ * absolutePosition() is a member function that compute the absolute position
2192
+ * of some element within document.
2193
+ *
2194
+ * @param (element obj) the element of the document
2195
+ * @return an object containing the properties top and left.
2196
+ */
2197
+ Datepicker.prototype.absolutePosition = function (element) {
2198
+ var top = 0, left = 0;
2199
+ if (element.getBoundingClientRect) {
2200
+ var box = element.getBoundingClientRect();
2201
+ var body = document.body;
2202
+ var docElem = document.documentElement;
2203
+ var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
2204
+ var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
2205
+ var clientTop = docElem.clientTop || body.clientTop || 0;
2206
+ var clientLeft = docElem.clientLeft || body.clientLeft || 0;
2207
+ top = Math.round(box.top + scrollTop - clientTop);
2208
+ left = Math.round(box.left + scrollLeft - clientLeft);
2209
+ } else {
2210
+ while(element) {
2211
+ top = top + parseInt(element.offsetTop);
2212
+ left = left + parseInt(element.offsetLeft);
2213
+ element = element.offsetParent;
2214
+ }
2215
+ }
2216
+ return {top: top, left: left};
2217
+ } // end absolutePosition()
2218
+
2219
+ /**
2220
+ * getDaysInMonth() is a member function to calculate the number of days in a given month
2221
+ *
2222
+ * @param (year int) the year
2223
+ * @param (month int) the given month
2224
+ *
2225
+ * @return (integer) number of days
2226
+ */
2227
+ Datepicker.prototype.getDaysInMonth = function(year, month) {
2228
+ return 32 - new Date(year, month, 32).getDate();
2229
+ } // end getDaysInMonth()
2230
+
2231
+ /**
2232
+ * previousMonth() is a member function that compute the month
2233
+ * preceding a given month.
2234
+ *
2235
+ * @param (year int) the given year
2236
+ * @param (month int) the given month
2237
+ * @return an object containing the properties year and month.
2238
+ */
2239
+ Datepicker.prototype.previousMonth = function (year, month) {
2240
+ if (month == 0) {
2241
+ month = 11;
2242
+ year--;
2243
+ } else {
2244
+ month--;
2245
+ }
2246
+ return {year: year, month: month};
2247
+ } // end previousMonth()
2248
+
2249
+ /**
2250
+ * nextMonth() is a member function that compute the month
2251
+ * following a given month.
2252
+ *
2253
+ * @param (year int) the given year
2254
+ * @param (month int) the given month
2255
+ * @return an object containing the properties year and month.
2256
+ */
2257
+ Datepicker.prototype.nextMonth = function (year, month) {
2258
+ if (month == 11) {
2259
+ month = 0;
2260
+ year++;
2261
+ } else {
2262
+ month++;
2263
+ }
2264
+ return {year: year, month: month};
2265
+ } // end nextMonth()
2266
+
2267
+ /**
2268
+ * formatDate (date_object, format)
2269
+ * The format string uses the same abbreviations as in createDateFromFormat()
2270
+ *
2271
+ * @param (date date object) the given date
2272
+ * @param (format string) the given output format
2273
+ * @returns a date in the output format specified.
2274
+ */
2275
+ Datepicker.prototype.formatDate = function (date, format) {
2276
+ var zeroPad = function (x) {
2277
+ return(x < 0 || x > 9 ? "" : "0" ) + x;
2278
+ };
2279
+ var getWeekOfMonth = function(date) {
2280
+ return Math.ceil((date.getDate() - 1 - date.getDay()) / 7);
2281
+ };
2282
+ var getWeekOfYear = function(date) {
2283
+ var onejan = new Date(date.getFullYear(),0,1);
2284
+ return Math.ceil((((date - onejan) / 86400000) + onejan.getDay()+1)/7);
2285
+ };
2286
+ var getDayOfYear = function(date) {
2287
+ var start = new Date(date.getFullYear(), 0, 0);
2288
+ return Math.floor((date - start) / 86400000);
2289
+ };
2290
+ var getMillisecondsInDay = function(date) {
2291
+ var date1 = new Date(date.getTime());
2292
+ date1.setHours( 0 );
2293
+ return date - date1;
2294
+ };
2295
+ var y = date.getFullYear() + "";
2296
+ var M = date.getMonth() + 1;
2297
+ var d = date.getDate();
2298
+ var D = getDayOfYear(date);
2299
+ var E = date.getDay();
2300
+ var H = date.getHours();
2301
+ var m = date.getMinutes();
2302
+ var s = date.getSeconds();
2303
+ var w = getWeekOfYear(date);
2304
+ var W = getWeekOfMonth(date);
2305
+ var F = Math.floor( date.getDate() / 7 ) + 1;
2306
+ var Q = Math.ceil( ( date.getMonth() + 1 ) / 3 );
2307
+ var era = date.getFullYear() < 1 ? 0 : 1;
2308
+ var values = {
2309
+ "y": "" + y,
2310
+ "yyyy": y,
2311
+ "yy": y.substring(2,4),
2312
+ "L": M,
2313
+ "LL": zeroPad(M),
2314
+ "LLL": this.locales.month_names_abbreviated[M - 1],
2315
+ "LLLL": this.locales.month_names[M - 1],
2316
+ "LLLLL": this.locales.month_names_narrow[M - 1],
2317
+ "M": M,
2318
+ "MM": zeroPad(M),
2319
+ "MMM": this.locales.month_names_abbreviated[M - 1],
2320
+ "MMMM": this.locales.month_names[M - 1],
2321
+ "MMMMM": this.locales.month_names_narrow[M - 1],
2322
+ "d": d,
2323
+ "dd": zeroPad(d),
2324
+ "D": D,
2325
+ "DD": D,
2326
+ "DDD": D,
2327
+ "A": Math.round( getMillisecondsInDay(date) * Math.pow( 10, -2 ) ),
2328
+ "AA": Math.round( getMillisecondsInDay(date) * Math.pow( 10, -1 ) ),
2329
+ "AAA": Math.round( getMillisecondsInDay(date) * Math.pow( 10, 0 ) ),
2330
+ "AAAA": Math.round( getMillisecondsInDay(date) * Math.pow( 10, 1 ) ),
2331
+ "AAAAA": Math.round( getMillisecondsInDay(date) * Math.pow( 10, 2 ) ),
2332
+ "AAAAAA": Math.round( getMillisecondsInDay(date) * Math.pow( 10, 3 ) ),
2333
+ "E": this.locales.day_names_abbreviated[E],
2334
+ "EE": this.locales.day_names_abbreviated[E],
2335
+ "EEE": this.locales.day_names_abbreviated[E],
2336
+ "EEEE": this.locales.day_names[E],
2337
+ "EEEEE": this.locales.day_names_narrow[E],
2338
+ "EEEEEE": this.locales.day_names_short[E],
2339
+ "e": E,
2340
+ "ee": E,
2341
+ "eee": this.locales.day_names_abbreviated[E],
2342
+ "eeee": this.locales.day_names[E],
2343
+ "eeeee": this.locales.day_names_narrow[E],
2344
+ "eeeeee": this.locales.day_names_short[E],
2345
+ "c": E,
2346
+ "cc": E,
2347
+ "ccc": this.locales.day_names_abbreviated[E],
2348
+ "cccc": this.locales.day_names[E],
2349
+ "ccccc": this.locales.day_names_narrow[E],
2350
+ "cccccc": this.locales.day_names_short[E],
2351
+ "F": F,
2352
+ "G": this.locales.era_names_abbreviated[era],
2353
+ "GG": this.locales.era_names_abbreviated[era],
2354
+ "GGG": this.locales.era_names_abbreviated[era],
2355
+ "GGGG": this.locales.era_names[era],
2356
+ "GGGGG": this.locales.era_names_narrow[era],
2357
+ "Q": Q,
2358
+ "QQ": zeroPad(Q),
2359
+ "QQQ": this.locales.quarter_names_abbreviated[Q - 1],
2360
+ "QQQQ": this.locales.quarter_names[Q - 1],
2361
+ "QQQQQ": this.locales.quarter_names_narrow[Q - 1],
2362
+ "q": Q,
2363
+ "qq": zeroPad(Q),
2364
+ "qqq": this.locales.quarter_names_abbreviated[Q - 1],
2365
+ "qqqq": this.locales.quarter_names[Q - 1],
2366
+ "qqqqq": this.locales.quarter_names_narrow[Q - 1],
2367
+ "H": H,
2368
+ "HH": zeroPad(H),
2369
+ "h": H == 0 ? 12 : H > 12 ? H - 12 : H,
2370
+ "hh": zeroPad(H == 0 ? 12 : H > 12 ? H - 12 : H),
2371
+ "K": H > 11 ? H - 12 : H,
2372
+ "k": H + 1,
2373
+ "KK": zeroPad(H > 11 ? H - 12 : H),
2374
+ "kk": zeroPad(H + 1),
2375
+ "a": H > 11 ? this.locales.day_periods.pm : this.locales.day_periods.am,
2376
+ "m": m,
2377
+ "mm": zeroPad(m),
2378
+ "s": s,
2379
+ "ss": zeroPad(s),
2380
+ "w": w,
2381
+ "ww": zeroPad(w),
2382
+ "W": W,
2383
+ };
2384
+ return format.replace(
2385
+ /('[^']+'|y{1,4}|L{1,5}|M{1,5}|c{1,6}|d{1,2}|D{1,3}|E{1,6}|e{1,6}|F{1,1}|G{1,5}|Q{1,5}|q{1,5}|H{1,2}|h{1,2}|K{1,2}|k{1,2}|m{1,2}|s{1,2}|w{1,2}|W{1,1}|A{1,6})/g,
2386
+ function (mask) {
2387
+ return mask.charAt(0) === "'" ? mask.substr(1, mask.length - 2) : values[mask] || mask;
2388
+ }
2389
+ );
2390
+ } // end formatDate()
2391
+
2392
+ /**
2393
+ * createDateFromFormat( format_string, date_string )
2394
+ *
2395
+ * This function takes a date string and a format string. It matches
2396
+ * If the date string matches the format string, it returns the
2397
+ * the date object. If it does not match, it returns null.
2398
+ */
2399
+ Datepicker.prototype.createDateFromFormat = function(format, value) {
2400
+ var extractInteger = function(str, pos, minlength, maxlength) {
2401
+ for (var x = maxlength; x >= minlength; x--) {
2402
+ var integer = str.substring(pos, pos + x);
2403
+ if (integer.length < minlength) {
2404
+ return null;
2405
+ }
2406
+ if (/^\d+$/.test(integer)) {
2407
+ return integer;
2408
+ }
2409
+ }
2410
+ return null;
2411
+ };
2412
+ var skipName = function(names, pos) {
2413
+ for (var i = 0; i < names.length; i++) {
2414
+ var name = names[i];
2415
+ if (value.substring(pos, pos + name.length).toLowerCase() == name.toLowerCase()) {
2416
+ return name.length;
2417
+ }
2418
+ }
2419
+ return 0;
2420
+ };
2421
+ var pos = 0;
2422
+ var now = new Date();
2423
+ var year = now.getYear();
2424
+ var month = now.getMonth() + 1;
2425
+ var date = 1;
2426
+ var hh = 0;
2427
+ var mm = 0;
2428
+ var ss = 0;
2429
+ var ampm = "";
2430
+
2431
+ $.each(format.match(/(.).*?\1*/g), function(k, token) {
2432
+ // Extract contents of value based on format token
2433
+ switch (token) {
2434
+ case 'yyyy':
2435
+ year = extractInteger(value, pos, 4, 4);
2436
+ if (year != null) {
2437
+ pos += year.length;
2438
+ }
2439
+ break;
2440
+ case 'yy':
2441
+ year = extractInteger(value, pos, 2, 2);
2442
+ if (year != null) {
2443
+ pos += year.length;
2444
+ }
2445
+ break;
2446
+ case 'y':
2447
+ year = extractInteger(value, pos, 2, 4);
2448
+ if (year != null) {
2449
+ pos += year.length;
2450
+ }
2451
+ break;
2452
+ case 'MMM':
2453
+ case 'LLL':
2454
+ month = 0;
2455
+ for (var i = 0; i < this.locales.month_names_abbreviated.length; i++) {
2456
+ var month_name = this.locales.month_names_abbreviated[i];
2457
+ if (value.substring(pos, pos + month_name.length).toLowerCase() == month_name.toLowerCase()) {
2458
+ month = i + 1;
2459
+ pos += month_name.length;
2460
+ break;
2461
+ }
2462
+ }
2463
+ break;
2464
+ case 'MMMM':
2465
+ case 'LLLL':
2466
+ month = 0;
2467
+ for (var i = 0; i < this.locales.month_names.length; i++) {
2468
+ var month_name = this.locales.month_names[i];
2469
+ if (value.substring(pos, pos + month_name.length).toLowerCase() == month_name.toLowerCase()) {
2470
+ month = i + 1;
2471
+ pos += month_name.length;
2472
+ break;
2473
+ }
2474
+ }
2475
+ break;
2476
+ case 'EEE':
2477
+ case 'EE':
2478
+ case 'E':
2479
+ case 'eee':
2480
+ pos += skipName(this.locales.day_names_abbreviated, pos);
2481
+ break;
2482
+ case 'EEEE':
2483
+ case 'eeee':
2484
+ case 'cccc':
2485
+ pos += skipName(this.locales.day_names, pos);
2486
+ break;
2487
+ case 'EEEEEE':
2488
+ case 'eeeeee':
2489
+ case 'cccccc':
2490
+ pos += skipName(this.locales.day_names_short, pos);
2491
+ break;
2492
+ case 'MM':
2493
+ case 'M':
2494
+ case 'LL':
2495
+ case 'L':
2496
+ month = extractInteger(value, pos, token.length, 2);
2497
+ if (month == null || (month < 1) || (month > 12)){
2498
+ return null;
2499
+ }
2500
+ pos += month.length;
2501
+ break;
2502
+ case 'dd':
2503
+ case 'd':
2504
+ date = extractInteger(value, pos, token.length, 2);
2505
+ if (date == null || (date < 1) || (date > 31)){
2506
+ return null;
2507
+ }
2508
+ pos += date.length;
2509
+ break;
2510
+ case 'hh':
2511
+ case 'h':
2512
+ hh = extractInteger(value, pos, token.length, 2);
2513
+ if (hh == null || (hh < 1) || (hh > 12)){
2514
+ return null;
2515
+ }
2516
+ pos += hh.length;
2517
+ break;
2518
+ case 'HH':
2519
+ case 'H':
2520
+ hh = extractInteger(value, pos, token.length, 2);
2521
+ if (hh == null || (hh < 0) || (hh > 23)){
2522
+ return null;
2523
+ }
2524
+ pos += hh.length;
2525
+ break;
2526
+ case 'KK':
2527
+ case 'K':
2528
+ hh = extractInteger(value, pos, token.length, 2);
2529
+ if (hh == null || (hh < 0) || (hh > 11)){
2530
+ return null;
2531
+ }
2532
+ pos += hh.length;
2533
+ break;
2534
+ case 'kk':
2535
+ case 'k':
2536
+ hh = extractInteger(value, pos, token.length, 2);
2537
+ if (hh == null || (hh < 1) || (hh > 24)){
2538
+ return null;
2539
+ }
2540
+ pos += hh.length;
2541
+ hh--;
2542
+ break;
2543
+ case 'mm':
2544
+ case 'm':
2545
+ mm = extractInteger(value,pos,token.length,2);
2546
+ if (mm == null || (mm < 0) || (mm > 59)){
2547
+ return null;
2548
+ }
2549
+ pos += mm.length;
2550
+ break;
2551
+ case 'ss':
2552
+ case 's':
2553
+ ss = extractInteger(value, pos, token.length, 2);
2554
+ if (ss == null || (ss < 0) || (ss > 59)){
2555
+ return null;
2556
+ }
2557
+ pos += ss.length;
2558
+ break;
2559
+ case 'a':
2560
+ var amlength = this.locales.day_periods.am.length;
2561
+ var pmlength = this.locales.day_periods.pm.length;
2562
+ if (value.substring(pos, pos + amlength) == this.locales.day_periods.am) {
2563
+ ampm = "AM";
2564
+ pos += amlength;
2565
+ } else if (value.substring(pos, pos + pmlength) == this.locales.day_periods.pm) {
2566
+ ampm = "PM";
2567
+ pos += pmlength;
2568
+ } else {
2569
+ return null;
2570
+ }
2571
+ break;
2572
+ default:
2573
+ if (value.substring(pos, pos + token.length) != token) {
2574
+ return null;
2575
+ } else {
2576
+ pos += token.length;
2577
+ }
2578
+ }
2579
+ });
2580
+ // If there are any trailing characters left in the value, it doesn't match
2581
+ if (pos != value.length) {
2582
+ return null;
2583
+ }
2584
+ if (year == null) {
2585
+ return null;
2586
+ }
2587
+ if (year.length == 2) {
2588
+ if (year > 50) {
2589
+ year = 1900 + (year - 0);
2590
+ } else {
2591
+ year = 2000 + (year - 0);
2592
+ }
2593
+ }
2594
+ // Is date valid for month?
2595
+ if ((month < 1) || (month > 12)) {
2596
+ return null;
2597
+ }
2598
+ if (month == 2) {
2599
+ // Check for leap year
2600
+ if ( ( (year % 4 == 0) && (year % 100 != 0) ) || (year % 400 == 0) ) { // leap year
2601
+ if (date > 29) {
2602
+ return null;
2603
+ }
2604
+ } else {
2605
+ if (date > 28) {
2606
+ return null;
2607
+ }
2608
+ }
2609
+ }
2610
+ if ((month == 4) || (month == 6) || (month == 9) || (month==11)) {
2611
+ if (date > 30) {
2612
+ return null;
2613
+ }
2614
+ }
2615
+ // Correct hours value
2616
+ if (hh < 12 && ampm == "PM") {
2617
+ hh = hh - 0 + 12;
2618
+ } else if (hh > 11 && ampm == "AM") {
2619
+ hh -= 12;
2620
+ }
2621
+ return new Date(year, month - 1, date, hh, mm, ss);
2622
+ } // end createDateFromFormat()
2623
+
2624
+ /**
2625
+ * parseDate() is a member function which parse a date string.
2626
+ *
2627
+ * This function takes a date string and try to parse it with the input formats.
2628
+ * If the date string matches one of the format string, it returns the
2629
+ * the date object. Otherwise, it returns null.
2630
+ *
2631
+ * @param (value string) the date string
2632
+ * @return a date objet or null
2633
+ */
2634
+ Datepicker.prototype.parseDate = function(value) {
2635
+ var date = null;
2636
+ var self = this;
2637
+ $.each(this.options.inputFormat, function (i, format) {
2638
+ date = self.createDateFromFormat(format, value);
2639
+ if (date != null) {
2640
+ return false;
2641
+ }
2642
+ });
2643
+ if (date == null) { // last try with the output format
2644
+ date = self.createDateFromFormat(this.options.outputFormat, value);
2645
+ }
2646
+ return date;
2647
+ } // end parseDate()
2648
+
2649
+ /**
2650
+ * min() is a public member function which allow change the smallest selectable date.
2651
+ *
2652
+ * @param (value string) the new date
2653
+ * @return the smallest selectable date
2654
+ */
2655
+ Datepicker.prototype.min = function(value) {
2656
+ if (value != null) {
2657
+ this.options.min = value instanceof Date ? value : this.parseDate(value);
2658
+ if (this.options.min != null && this.dateObj < this.options.min) {
2659
+ this.$target.attr('aria-invalid', true);
2660
+ this.$target.parents('.form-group').addClass('has-error');
2661
+ this.dateObj = this.options.min;
2662
+ }
2663
+ if (this.options.inline != false) {
2664
+ this.refresh();
2665
+ }
2666
+ }
2667
+ return this.options.min;
2668
+ } // end min()
2669
+
2670
+ /**
2671
+ * max() is a public member function which allow change the biggest selectable date.
2672
+ *
2673
+ * @param (value string) the new date
2674
+ * @return the biggest selectable date
2675
+ */
2676
+ Datepicker.prototype.max = function(value) {
2677
+ if (value != null) {
2678
+ this.options.max = value instanceof Date ? value : this.parseDate(value);
2679
+ if (this.options.max != null && this.dateObj > this.options.max) {
2680
+ this.$target.attr('aria-invalid', true);
2681
+ this.$target.parents('.form-group').addClass('has-error');
2682
+ this.dateObj = this.options.max;
2683
+ }
2684
+ if (this.options.inline != false) {
2685
+ this.refresh();
2686
+ }
2687
+ }
2688
+ return this.options.max;
2689
+ } // end max()
2690
+
2691
+ /**
2692
+ * theme() is a public member function which allow change the datepicker theme.
2693
+ *
2694
+ * @param (value string) the new theme
2695
+ * @return the datepicker theme
2696
+ */
2697
+ Datepicker.prototype.theme = function(value) {
2698
+ if (value != null) {
2699
+ this.$button.removeClass(this.options.theme);
2700
+ this.$calendar.removeClass(this.options.theme);
2701
+ this.options.theme = value;
2702
+ this.$button.addClass(this.options.theme);
2703
+ this.$calendar.addClass(this.options.theme);
2704
+ }
2705
+ return this.options.theme;
2706
+ } // end theme()
2707
+
2708
+ /**
2709
+ * firstDayOfWeek() is a public member function which allow change the first Day Of Week.
2710
+ *
2711
+ * @param (value integer) the new first Day Of Week
2712
+ * @return the first Day Of Week
2713
+ */
2714
+ Datepicker.prototype.firstDayOfWeek = function(value) {
2715
+ if (value != null) {
2716
+ this.options.firstDayOfWeek = parseInt(value);
2717
+ if (this.options.inline == false) {
2718
+ this.drawCalendarHeader();
2719
+ } else {
2720
+ this.refresh();
2721
+ }
2722
+ }
2723
+ return this.options.firstDayOfWeek;
2724
+ } // end firstDayOfWeek()
2725
+
2726
+ /**
2727
+ * daysOfWeekDisabled() is a public member function which allow disabling of some weekdays.
2728
+ *
2729
+ * @param (value string) the new disabled week days
2730
+ * @return the disabled week days
2731
+ */
2732
+ Datepicker.prototype.daysOfWeekDisabled = function(value) {
2733
+ if (value != null) {
2734
+ this.options.daysOfWeekDisabled = [];
2735
+ if (! $.isArray(value)) {
2736
+ value = [value];
2737
+ }
2738
+ var self = this;
2739
+ $.each(value, function(i, val) {
2740
+ if (typeof val === 'number') {
2741
+ self.options.daysOfWeekDisabled.push(val);
2742
+ } else if (typeof val === 'string') {
2743
+ self.options.daysOfWeekDisabled.push(parseInt(val));
2744
+ }
2745
+ });
2746
+ }
2747
+ return this.options.daysOfWeekDisabled;
2748
+ } // end daysOfWeekDisabled()
2749
+
2750
+ /**
2751
+ * weekDayFormat() is a public member function which allow change the format of weekdays name.
2752
+ *
2753
+ * @param (value string) the new format. Allowed : 'short' or 'narrow'
2754
+ * @return the format of weekdays name
2755
+ */
2756
+ Datepicker.prototype.weekDayFormat = function(value) {
2757
+ if (value != null) {
2758
+ this.options.weekDayFormat = value;
2759
+ this.drawCalendarHeader();
2760
+ }
2761
+ return this.options.weekDayFormat;
2762
+ } // end weekDayFormat()
2763
+
2764
+ /**
2765
+ * inputFormat() is a public member function which allow change the input format.
2766
+ *
2767
+ * @param (value string) the new format
2768
+ * @return the input format
2769
+ */
2770
+ Datepicker.prototype.inputFormat = function(value) {
2771
+ if (value != null) {
2772
+ if (! $.isArray(value)) {
2773
+ value = [value];
2774
+ }
2775
+ if (this.$target.attr('placeholder') == this.options.inputFormat[0]) {
2776
+ this.$target.attr('placeholder', value[0]);
2777
+ }
2778
+ this.options.inputFormat = value;
2779
+ }
2780
+ return this.options.inputFormat;
2781
+ } // end inputFormat()
2782
+
2783
+ /**
2784
+ * outputFormat() is a public member function which allow change the output format.
2785
+ *
2786
+ * @param (value string) the new format
2787
+ * @return the output format
2788
+ */
2789
+ Datepicker.prototype.outputFormat = function(value) {
2790
+ if (value != null) {
2791
+ this.options.outputFormat = value;
2792
+ }
2793
+ return this.options.outputFormat;
2794
+ } // end outputFormat()
2795
+
2796
+ /**
2797
+ * modal() is a public member function which allow to set or unset the modal mode.
2798
+ *
2799
+ * @param (value boolean) the new modal mode
2800
+ * @return the modal mode
2801
+ */
2802
+ Datepicker.prototype.modal = function(value) {
2803
+ if (value != null) {
2804
+ this.options.modal = value;
2805
+ if (this.options.modal == true) {
2806
+ if (this.options.inline == false) {
2807
+ this.showObject(this.$calendar.find('.datepicker-close-wrap'));
2808
+ this.showObject(this.$calendar.find('.datepicker-bn-close-label'));
2809
+ }
2810
+ this.$close = this.$calendar.find('.datepicker-close');
2811
+ this.$close.html(this.options.closeButtonTitle).attr('title', this.options.closeButtonLabel);
2812
+ this.$calendar.find('.datepicker-bn-close-label').html(this.options.closeButtonLabel);
2813
+ var self = this;
2814
+ this.$close.click(function(e) {
2815
+ return self.handleCloseClick(e);
2816
+ });
2817
+ this.$close.keydown(function(e) {
2818
+ return self.handleCloseKeyDown(e);
2819
+ });
2820
+ } else {
2821
+ this.hideObject(this.$calendar.find('.datepicker-close-wrap'));
2822
+ this.hideObject(this.$calendar.find('.datepicker-bn-close-label'));
2823
+ }
2824
+ }
2825
+ return this.options.modal;
2826
+ } // end modal()
2827
+
2828
+ /**
2829
+ * inline() is a public member function which allow to set or unset the inline mode.
2830
+ *
2831
+ * @param (value string or false) the id or jquery object of the datepicker container, false otherwise (not inline)
2832
+ * @return the given value
2833
+ */
2834
+ Datepicker.prototype.inline = function(value) {
2835
+ if (value != null) {
2836
+ if (value != false) {
2837
+ this.hideObject(this.$button);
2838
+ this.hideObject(this.$calendar.find('.datepicker-close-wrap'));
2839
+ this.hideObject(this.$calendar.find('.datepicker-bn-close-label'));
2840
+ var $container = typeof value === 'string' ? $('#' + value) : value;
2841
+ $container.append(this.$calendar);
2842
+ this.$calendar.css({position: 'relative', left: '0px', top: '0px'});
2843
+ this.options.inline = value;
2844
+ this.initializeDate();
2845
+ this.showObject(this.$calendar);
2846
+ } else {
2847
+ this.$target.parent().after(this.$calendar);
2848
+ this.showObject(this.$button);
2849
+ if (this.options.modal == true) {
2850
+ this.showObject(this.$calendar.find('.datepicker-close-wrap'));
2851
+ this.showObject(this.$calendar.find('.datepicker-bn-close-label'));
2852
+ }
2853
+ this.$calendar.css({position: 'absolute'});
2854
+ this.options.inline = value;
2855
+ this.hide();
2856
+ }
2857
+ }
2858
+ return this.options.inline;
2859
+ } // end inline()
2860
+
2861
+
2862
+ /**
2863
+ * format() is a public member function to format a date according the output format.
2864
+ *
2865
+ * @param (value date object) the date
2866
+ * @return formatted date string
2867
+ */
2868
+ Datepicker.prototype.format = function(date) {
2869
+ return this.formatDate(date, this.options.outputFormat);
2870
+ } // end format()
2871
+
2872
+ /**
2873
+ * setLocales() is a public member function which allow change the locales.
2874
+ *
2875
+ * @param (value obj) the new locales
2876
+ * @return N/A
2877
+ */
2878
+ Datepicker.prototype.setLocales = function(value) {
2879
+ this.locales = value;
2880
+ this.options.inputFormat = [this.locales.short_format];
2881
+ this.options.outputFormat = this.locales.short_format;
2882
+ this.options.titleFormat = this.locales.full_format,
2883
+ this.options.firstDayOfWeek = this.locales.firstday_of_week;
2884
+ this.options.buttonTitle = this.locales.texts.buttonTitle;
2885
+ this.$button.find('span').attr('title', this.options.buttonTitle);
2886
+ this.options.buttonLabel = this.locales.texts.buttonLabel;
2887
+ this.options.prevButtonLabel = this.locales.texts.prevButtonLabel;
2888
+ this.options.prevMonthButtonLabel = this.locales.texts.prevMonthButtonLabel;
2889
+ this.options.prevYearButtonLabel = this.locales.texts.prevYearButtonLabel;
2890
+ this.options.nextButtonLabel = this.locales.texts.nextButtonLabel;
2891
+ this.options.nextMonthButtonLabel = this.locales.texts.nextMonthButtonLabel;
2892
+ this.options.nextYearButtonLabel = this.locales.texts.nextYearButtonLabel;
2893
+ this.options.changeMonthButtonLabel = this.locales.texts.changeMonthButtonLabel;
2894
+ this.options.changeYearButtonLabel = this.locales.texts.changeYearButtonLabel;
2895
+ this.options.changeRangeButtonLabel = this.locales.texts.changeRangeButtonLabel;
2896
+ this.options.closeButtonTitle = this.locales.texts.closeButtonTitle;
2897
+ this.options.closeButtonLabel = this.locales.texts.closeButtonLabel;
2898
+ this.options.calendarHelp = this.locales.texts.calendarHelp;
2899
+ this.drawCalendarHeader();
2900
+ if (this.locales.directionality === 'RTL') {
2901
+ this.$grid.addClass('rtl');
2902
+ } else {
2903
+ this.$grid.removeClass('rtl');
2904
+ }
2905
+ } // end outputFormat()
2906
+
2907
+ // DATEPICKER PLUGIN DEFINITION
2908
+ // ==========================
2909
+
2910
+ var old = $.fn.datepicker
2911
+
2912
+ $.fn.datepicker = function (option, value) {
2913
+ if (typeof option == 'string' && $(this).length == 1) {
2914
+ var data = $(this).eq(0).data('ab.datepicker');
2915
+ if (data) return data[option](value);
2916
+ } else {
2917
+ return this.each(function () {
2918
+ var $this = $(this);
2919
+ var data = $this.data('ab.datepicker');
2920
+ var options = $.extend({}, Datepicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
2921
+ if (!data) $this.data('ab.datepicker', (data = new Datepicker(this, options)));
2922
+ if (typeof option == 'string') data[option](value);
2923
+ });
2924
+ }
2925
+ }
2926
+
2927
+ $.fn.datepicker.Constructor = Datepicker
2928
+
2929
+ // DATEPICKER NO CONFLICT
2930
+ // ====================
2931
+
2932
+ $.fn.datepicker.noConflict = function () {
2933
+ $.fn.datepicker = old
2934
+ return this
2935
+ }
2936
+
2937
+ }));