source: main/trunk/model-interfaces-dev/heritage-nz/iframe/heritage-nz-dl_files/bootstrap-datepicker.js@ 32796

Last change on this file since 32796 was 32796, checked in by davidb, 5 years ago

Initial set of files to provide look and feel of Heritage NZ site, plus SVN clickable map in an iframe

  • Property svn:executable set to *
File size: 42.5 KB
Line 
1/* =========================================================
2 * bootstrap-datepicker.js
3 * http://www.eyecon.ro/bootstrap-datepicker
4 * =========================================================
5 * Copyright 2012 Stefan Petre
6 * Improvements by Andrew Rowls
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ========================================================= */
20
21!function ($) {
22
23 function UTCDate() {
24 return new Date(Date.UTC.apply(Date, arguments));
25 }
26 function UTCToday() {
27 var today = new Date();
28 return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
29 }
30
31 // Picker object
32
33 var Datepicker = function (element, options) {
34 var that = this;
35
36 this.element = $(element);
37 this.language = options.language || this.element.data('date-language') || "en";
38 this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de"
39 this.language = this.language in dates ? this.language : "en";
40 this.isRTL = dates[this.language].rtl || false;
41 this.format = DPGlobal.parseFormat(options.format || this.element.data('date-format') || dates[this.language].format || 'mm/dd/yyyy');
42 this.isInline = false;
43 this.isInput = this.element.is('input');
44 this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
45 this.hasInput = this.component && this.element.find('input').length;
46 if (this.component && this.component.length === 0)
47 this.component = false;
48
49 this.forceParse = true;
50 if ('forceParse' in options) {
51 this.forceParse = options.forceParse;
52 } else if ('dateForceParse' in this.element.data()) {
53 this.forceParse = this.element.data('date-force-parse');
54 }
55
56 this.picker = $(DPGlobal.template);
57 this._buildEvents();
58 this._attachEvents();
59
60 if (this.isInline) {
61 this.picker.addClass('datepicker-inline').appendTo(this.element);
62 } else {
63 this.picker.addClass('datepicker-dropdown dropdown-menu');
64 }
65 if (this.isRTL) {
66 this.picker.addClass('datepicker-rtl');
67 this.picker.find('.prev i, .next i')
68 .toggleClass('icon-arrow-left icon-arrow-right');
69 }
70
71 this.autoclose = false;
72 if ('autoclose' in options) {
73 this.autoclose = options.autoclose;
74 } else if ('dateAutoclose' in this.element.data()) {
75 this.autoclose = this.element.data('date-autoclose');
76 }
77
78 this.keyboardNavigation = true;
79 if ('keyboardNavigation' in options) {
80 this.keyboardNavigation = options.keyboardNavigation;
81 } else if ('dateKeyboardNavigation' in this.element.data()) {
82 this.keyboardNavigation = this.element.data('date-keyboard-navigation');
83 }
84
85 this.viewMode = this.startViewMode = 0;
86 switch (options.startView || this.element.data('date-start-view')) {
87 case 2:
88 case 'decade':
89 this.viewMode = this.startViewMode = 2;
90 break;
91 case 1:
92 case 'year':
93 this.viewMode = this.startViewMode = 1;
94 break;
95 }
96
97 this.minViewMode = options.minViewMode || this.element.data('date-min-view-mode') || 0;
98 if (typeof this.minViewMode === 'string') {
99 switch (this.minViewMode) {
100 case 'months':
101 this.minViewMode = 1;
102 break;
103 case 'years':
104 this.minViewMode = 2;
105 break;
106 default:
107 this.minViewMode = 0;
108 break;
109 }
110 }
111
112 this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode);
113
114 this.todayBtn = (options.todayBtn || this.element.data('date-today-btn') || false);
115 this.todayHighlight = (options.todayHighlight || this.element.data('date-today-highlight') || false);
116
117 this.calendarWeeks = false;
118 if ('calendarWeeks' in options) {
119 this.calendarWeeks = options.calendarWeeks;
120 } else if ('dateCalendarWeeks' in this.element.data()) {
121 this.calendarWeeks = this.element.data('date-calendar-weeks');
122 }
123 if (this.calendarWeeks)
124 this.picker.find('tfoot th.today')
125 .attr('colspan', function (i, val) {
126 return parseInt(val) + 1;
127 });
128
129 this._allow_update = false;
130
131 this.weekStart = ((options.weekStart || this.element.data('date-weekstart') || dates[this.language].weekStart || 0) % 7);
132 this.weekEnd = ((this.weekStart + 6) % 7);
133 this.startDate = -Infinity;
134 this.endDate = Infinity;
135 this.daysOfWeekDisabled = [];
136 this.setStartDate(options.startDate || this.element.data('date-startdate'));
137 this.setEndDate(options.endDate || this.element.data('date-enddate'));
138 this.setDaysOfWeekDisabled(options.daysOfWeekDisabled || this.element.data('date-days-of-week-disabled'));
139 this.fillDow();
140 this.fillMonths();
141
142 this._allow_update = true;
143
144 this.update();
145 this.showMode();
146
147 if (this.isInline) {
148 this.show();
149 }
150 };
151
152 Datepicker.prototype = {
153 constructor: Datepicker,
154
155 _events: [],
156 _secondaryEvents: [],
157 _applyEvents: function (evs) {
158 for (var i = 0, el, ev; i < evs.length; i++) {
159 el = evs[i][0];
160 ev = evs[i][1];
161 el.on(ev);
162 }
163 },
164 _unapplyEvents: function (evs) {
165 for (var i = 0, el, ev; i < evs.length; i++) {
166 el = evs[i][0];
167 ev = evs[i][1];
168 el.off(ev);
169 }
170 },
171 _buildEvents: function () {
172 if (this.isInput) { // single input
173 this._events = [
174 [this.element, {
175 focus: $.proxy(this.show, this),
176 keyup: $.proxy(this.update, this),
177 keydown: $.proxy(this.keydown, this)
178 }]
179 ];
180 }
181 else if (this.component && this.hasInput) { // component: input + button
182 this._events = [
183 // For components that are not readonly, allow keyboard nav
184 [this.element.find('input'), {
185 focus: $.proxy(this.show, this),
186 keyup: $.proxy(this.update, this),
187 keydown: $.proxy(this.keydown, this)
188 }],
189 [this.component, {
190 click: $.proxy(this.show, this)
191 }]
192 ];
193 }
194 else if (this.element.is('div')) { // inline datepicker
195 this.isInline = true;
196 }
197 else {
198 this._events = [
199 [this.element, {
200 click: $.proxy(this.show, this)
201 }]
202 ];
203 }
204
205 this._secondaryEvents = [
206 [this.picker, {
207 click: $.proxy(this.click, this)
208 }],
209 [$(window), {
210 resize: $.proxy(this.place, this)
211 }],
212 [$(document), {
213 mousedown: $.proxy(function (e) {
214 // Clicked outside the datepicker, hide it
215 if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) {
216 this.hide();
217 }
218 }, this)
219 }]
220 ];
221 },
222 _attachEvents: function () {
223 this._detachEvents();
224 this._applyEvents(this._events);
225 },
226 _detachEvents: function () {
227 this._unapplyEvents(this._events);
228 },
229 _attachSecondaryEvents: function () {
230 this._detachSecondaryEvents();
231 this._applyEvents(this._secondaryEvents);
232 },
233 _detachSecondaryEvents: function () {
234 this._unapplyEvents(this._secondaryEvents);
235 },
236
237 show: function (e) {
238 if (!this.isInline)
239 this.picker.appendTo('body');
240 this.picker.show();
241 this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
242 this.place();
243 this._attachSecondaryEvents();
244 if (e) {
245 e.preventDefault();
246 }
247 this.element.trigger({
248 type: 'show',
249 date: this.date
250 });
251 },
252
253 hide: function (e) {
254 if (this.isInline) return;
255 if (!this.picker.is(':visible')) return;
256 this.picker.hide().detach();
257 this._detachSecondaryEvents();
258 this.viewMode = this.startViewMode;
259 this.showMode();
260
261 if (
262 this.forceParse &&
263 (
264 this.isInput && this.element.val() ||
265 this.hasInput && this.element.find('input').val()
266 )
267 )
268 this.setValue();
269 this.element.trigger({
270 type: 'hide',
271 date: this.date
272 });
273 },
274
275 remove: function () {
276 this.hide();
277 this._detachEvents();
278 this._detachSecondaryEvents();
279 this.picker.remove();
280 delete this.element.data().datepicker;
281 if (!this.isInput) {
282 delete this.element.data().date;
283 }
284 },
285
286 getDate: function () {
287 var d = this.getUTCDate();
288 return new Date(d.getTime() + (d.getTimezoneOffset() * 60000));
289 },
290
291 getUTCDate: function () {
292 return this.date;
293 },
294
295 setDate: function (d) {
296 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset() * 60000)));
297 },
298
299 setUTCDate: function (d) {
300 this.date = d;
301 this.setValue();
302 },
303
304 setValue: function () {
305 var formatted = this.getFormattedDate();
306 if (!this.isInput) {
307 if (this.component) {
308 this.element.find('input').val(formatted);
309 }
310 this.element.data('date', formatted);
311 } else {
312 this.element.val(formatted);
313 }
314 },
315
316 getFormattedDate: function (format) {
317 if (format === undefined)
318 format = this.format;
319 return DPGlobal.formatDate(this.date, format, this.language);
320 },
321
322 setStartDate: function (startDate) {
323 this.startDate = startDate || -Infinity;
324 if (this.startDate !== -Infinity) {
325 this.startDate = DPGlobal.parseDate(this.startDate, this.format, this.language);
326 }
327 this.update();
328 this.updateNavArrows();
329 },
330
331 setEndDate: function (endDate) {
332 this.endDate = endDate || Infinity;
333 if (this.endDate !== Infinity) {
334 this.endDate = DPGlobal.parseDate(this.endDate, this.format, this.language);
335 }
336 this.update();
337 this.updateNavArrows();
338 },
339
340 setDaysOfWeekDisabled: function (daysOfWeekDisabled) {
341 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
342 if (!$.isArray(this.daysOfWeekDisabled)) {
343 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
344 }
345 this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
346 return parseInt(d, 10);
347 });
348 this.update();
349 this.updateNavArrows();
350 },
351
352 place: function () {
353 if (this.isInline) return;
354 var zIndex = parseInt(this.element.parents().filter(function () {
355 return $(this).css('z-index') != 'auto';
356 }).first().css('z-index')) + 10;
357 var offset = this.component ? this.component.parent().offset() : this.element.offset();
358 var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
359 this.picker.css({
360 top: offset.top + height,
361 left: offset.left,
362 zIndex: zIndex
363 });
364 },
365
366 _allow_update: true,
367 update: function () {
368 if (!this._allow_update) return;
369
370 var date, fromArgs = false;
371 if (arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
372 date = arguments[0];
373 fromArgs = true;
374 } else {
375 date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
376 }
377
378 this.date = DPGlobal.parseDate(date, this.format, this.language);
379
380 if (fromArgs) this.setValue();
381
382 if (this.date < this.startDate) {
383 this.viewDate = new Date(this.startDate);
384 } else if (this.date > this.endDate) {
385 this.viewDate = new Date(this.endDate);
386 } else {
387 this.viewDate = new Date(this.date);
388 }
389 this.fill();
390 },
391
392 fillDow: function () {
393 var dowCnt = this.weekStart,
394 html = '<tr>';
395 if (this.calendarWeeks) {
396 var cell = '<th class="cw">&nbsp;</th>';
397 html += cell;
398 this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
399 }
400 while (dowCnt < this.weekStart + 7) {
401 html += '<th class="dow">' + dates[this.language].daysMin[(dowCnt++) % 7] + '</th>';
402 }
403 html += '</tr>';
404 this.picker.find('.datepicker-days thead').append(html);
405 },
406
407 fillMonths: function () {
408 var html = '',
409 i = 0;
410 while (i < 12) {
411 html += '<span class="month">' + dates[this.language].monthsShort[i++] + '</span>';
412 }
413 this.picker.find('.datepicker-months td').html(html);
414 },
415
416 fill: function () {
417 var d = new Date(this.viewDate),
418 year = d.getUTCFullYear(),
419 month = d.getUTCMonth(),
420 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
421 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
422 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
423 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
424 currentDate = this.date && this.date.valueOf(),
425 today = new Date();
426 this.picker.find('.datepicker-days thead th.switch')
427 .text(dates[this.language].months[month] + ' ' + year);
428 this.picker.find('tfoot th.today')
429 .text(dates[this.language].today)
430 .toggle(this.todayBtn !== false);
431 this.updateNavArrows();
432 this.fillMonths();
433 var prevMonth = UTCDate(year, month - 1, 28, 0, 0, 0, 0),
434 day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
435 prevMonth.setUTCDate(day);
436 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7) % 7);
437 var nextMonth = new Date(prevMonth);
438 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
439 nextMonth = nextMonth.valueOf();
440 var html = [];
441 var clsName;
442 while (prevMonth.valueOf() < nextMonth) {
443 if (prevMonth.getUTCDay() == this.weekStart) {
444 html.push('<tr>');
445 if (this.calendarWeeks) {
446 // ISO 8601: First week contains first thursday.
447 // ISO also states week starts on Monday, but we can be more abstract here.
448 var
449 // Start of current week: based on weekstart/current date
450 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
451 // Thursday of this week
452 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
453 // First Thursday of year, year from thursday
454 yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay()) % 7 * 864e5),
455 // Calendar week: ms between thursdays, div ms per day, div 7 days
456 calWeek = (th - yth) / 864e5 / 7 + 1;
457 html.push('<td class="cw">' + calWeek + '</td>');
458
459 }
460 }
461 clsName = '';
462 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
463 clsName += ' old';
464 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
465 clsName += ' new';
466 }
467 // Compare internal UTC date with local today, not UTC today
468 if (this.todayHighlight &&
469 prevMonth.getUTCFullYear() == today.getFullYear() &&
470 prevMonth.getUTCMonth() == today.getMonth() &&
471 prevMonth.getUTCDate() == today.getDate()) {
472 clsName += ' today';
473 }
474 if (currentDate && prevMonth.valueOf() == currentDate) {
475 clsName += ' active';
476 }
477 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
478 $.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
479 clsName += ' disabled';
480 }
481 html.push('<td class="day' + clsName + '">' + prevMonth.getUTCDate() + '</td>');
482 if (prevMonth.getUTCDay() == this.weekEnd) {
483 html.push('</tr>');
484 }
485 prevMonth.setUTCDate(prevMonth.getUTCDate() + 1);
486 }
487 this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
488 var currentYear = this.date && this.date.getUTCFullYear();
489
490 var months = this.picker.find('.datepicker-months')
491 .find('th:eq(1)')
492 .text(year)
493 .end()
494 .find('span').removeClass('active');
495 if (currentYear && currentYear == year) {
496 months.eq(this.date.getUTCMonth()).addClass('active');
497 }
498 if (year < startYear || year > endYear) {
499 months.addClass('disabled');
500 }
501 if (year == startYear) {
502 months.slice(0, startMonth).addClass('disabled');
503 }
504 if (year == endYear) {
505 months.slice(endMonth + 1).addClass('disabled');
506 }
507
508 html = '';
509 year = parseInt(year / 10, 10) * 10;
510 var yearCont = this.picker.find('.datepicker-years')
511 .find('th:eq(1)')
512 .text(year + '-' + (year + 9))
513 .end()
514 .find('td');
515 year -= 1;
516 for (var i = -1; i < 11; i++) {
517 html += '<span class="year' + (i == -1 || i == 10 ? ' old' : '') + (currentYear == year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : '') + '">' + year + '</span>';
518 year += 1;
519 }
520 yearCont.html(html);
521 },
522
523 updateNavArrows: function () {
524 if (!this._allow_update) return;
525
526 var d = new Date(this.viewDate),
527 year = d.getUTCFullYear(),
528 month = d.getUTCMonth();
529 switch (this.viewMode) {
530 case 0:
531 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
532 this.picker.find('.prev').css({ visibility: 'hidden' });
533 } else {
534 this.picker.find('.prev').css({ visibility: 'visible' });
535 }
536 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
537 this.picker.find('.next').css({ visibility: 'hidden' });
538 } else {
539 this.picker.find('.next').css({ visibility: 'visible' });
540 }
541 break;
542 case 1:
543 case 2:
544 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
545 this.picker.find('.prev').css({ visibility: 'hidden' });
546 } else {
547 this.picker.find('.prev').css({ visibility: 'visible' });
548 }
549 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
550 this.picker.find('.next').css({ visibility: 'hidden' });
551 } else {
552 this.picker.find('.next').css({ visibility: 'visible' });
553 }
554 break;
555 }
556 },
557
558 click: function (e) {
559 e.preventDefault();
560 var target = $(e.target).closest('span, td, th');
561 if (target.length == 1) {
562 switch (target[0].nodeName.toLowerCase()) {
563 case 'th':
564 switch (target[0].className) {
565 case 'switch':
566 this.showMode(1);
567 break;
568 case 'prev':
569 case 'next':
570 var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
571 switch (this.viewMode) {
572 case 0:
573 this.viewDate = this.moveMonth(this.viewDate, dir);
574 break;
575 case 1:
576 case 2:
577 this.viewDate = this.moveYear(this.viewDate, dir);
578 break;
579 }
580 this.fill();
581 break;
582 case 'today':
583 var date = new Date();
584 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
585
586 this.showMode(-2);
587 var which = this.todayBtn == 'linked' ? null : 'view';
588 this._setDate(date, which);
589 break;
590 }
591 break;
592 case 'span':
593 if (!target.is('.disabled')) {
594 this.viewDate.setUTCDate(1);
595 if (target.is('.month')) {
596 var day = 1;
597 var month = target.parent().find('span').index(target);
598 var year = this.viewDate.getUTCFullYear();
599 this.viewDate.setUTCMonth(month);
600 this.element.trigger({
601 type: 'changeMonth',
602 date: this.viewDate
603 });
604 if (this.minViewMode == 1) {
605 this._setDate(UTCDate(year, month, day, 0, 0, 0, 0));
606 }
607 } else {
608 var year = parseInt(target.text(), 10) || 0;
609 var day = 1;
610 var month = 0;
611 this.viewDate.setUTCFullYear(year);
612 this.element.trigger({
613 type: 'changeYear',
614 date: this.viewDate
615 });
616 if (this.minViewMode == 2) {
617 this._setDate(UTCDate(year, month, day, 0, 0, 0, 0));
618 }
619 }
620 this.showMode(-1);
621 this.fill();
622 }
623 break;
624 case 'td':
625 if (target.is('.day') && !target.is('.disabled')) {
626 var day = parseInt(target.text(), 10) || 1;
627 var year = this.viewDate.getUTCFullYear(),
628 month = this.viewDate.getUTCMonth();
629 if (target.is('.old')) {
630 if (month === 0) {
631 month = 11;
632 year -= 1;
633 } else {
634 month -= 1;
635 }
636 } else if (target.is('.new')) {
637 if (month == 11) {
638 month = 0;
639 year += 1;
640 } else {
641 month += 1;
642 }
643 }
644 this._setDate(UTCDate(year, month, day, 0, 0, 0, 0));
645 }
646 break;
647 }
648 }
649 },
650
651 _setDate: function (date, which) {
652 if (!which || which == 'date')
653 this.date = date;
654 if (!which || which == 'view')
655 this.viewDate = date;
656 this.fill();
657 this.setValue();
658 this.element.trigger({
659 type: 'changeDate',
660 date: this.date
661 });
662 var element;
663 if (this.isInput) {
664 element = this.element;
665 } else if (this.component) {
666 element = this.element.find('input');
667 }
668 if (element) {
669 element.change();
670 if (this.autoclose && (!which || which == 'date')) {
671 this.hide();
672 }
673 }
674 },
675
676 moveMonth: function (date, dir) {
677 if (!dir) return date;
678 var new_date = new Date(date.valueOf()),
679 day = new_date.getUTCDate(),
680 month = new_date.getUTCMonth(),
681 mag = Math.abs(dir),
682 new_month, test;
683 dir = dir > 0 ? 1 : -1;
684 if (mag == 1) {
685 test = dir == -1
686 // If going back one month, make sure month is not current month
687 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
688 ? function () { return new_date.getUTCMonth() == month; }
689 // If going forward one month, make sure month is as expected
690 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
691 : function () { return new_date.getUTCMonth() != new_month; };
692 new_month = month + dir;
693 new_date.setUTCMonth(new_month);
694 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
695 if (new_month < 0 || new_month > 11)
696 new_month = (new_month + 12) % 12;
697 } else {
698 // For magnitudes >1, move one month at a time...
699 for (var i = 0; i < mag; i++)
700 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
701 new_date = this.moveMonth(new_date, dir);
702 // ...then reset the day, keeping it in the new month
703 new_month = new_date.getUTCMonth();
704 new_date.setUTCDate(day);
705 test = function () { return new_month != new_date.getUTCMonth(); };
706 }
707 // Common date-resetting loop -- if date is beyond end of month, make it
708 // end of month
709 while (test()) {
710 new_date.setUTCDate(--day);
711 new_date.setUTCMonth(new_month);
712 }
713 return new_date;
714 },
715
716 moveYear: function (date, dir) {
717 return this.moveMonth(date, dir * 12);
718 },
719
720 dateWithinRange: function (date) {
721 return date >= this.startDate && date <= this.endDate;
722 },
723
724 keydown: function (e) {
725 if (this.picker.is(':not(:visible)')) {
726 if (e.keyCode == 27) // allow escape to hide and re-show picker
727 this.show();
728 return;
729 }
730 var dateChanged = false,
731 dir, day, month,
732 newDate, newViewDate;
733 switch (e.keyCode) {
734 case 27: // escape
735 this.hide();
736 e.preventDefault();
737 break;
738 case 37: // left
739 case 39: // right
740 if (!this.keyboardNavigation) break;
741 dir = e.keyCode == 37 ? -1 : 1;
742 if (e.ctrlKey) {
743 newDate = this.moveYear(this.date, dir);
744 newViewDate = this.moveYear(this.viewDate, dir);
745 } else if (e.shiftKey) {
746 newDate = this.moveMonth(this.date, dir);
747 newViewDate = this.moveMonth(this.viewDate, dir);
748 } else {
749 newDate = new Date(this.date);
750 newDate.setUTCDate(this.date.getUTCDate() + dir);
751 newViewDate = new Date(this.viewDate);
752 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
753 }
754 if (this.dateWithinRange(newDate)) {
755 this.date = newDate;
756 this.viewDate = newViewDate;
757 this.setValue();
758 this.update();
759 e.preventDefault();
760 dateChanged = true;
761 }
762 break;
763 case 38: // up
764 case 40: // down
765 if (!this.keyboardNavigation) break;
766 dir = e.keyCode == 38 ? -1 : 1;
767 if (e.ctrlKey) {
768 newDate = this.moveYear(this.date, dir);
769 newViewDate = this.moveYear(this.viewDate, dir);
770 } else if (e.shiftKey) {
771 newDate = this.moveMonth(this.date, dir);
772 newViewDate = this.moveMonth(this.viewDate, dir);
773 } else {
774 newDate = new Date(this.date);
775 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
776 newViewDate = new Date(this.viewDate);
777 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
778 }
779 if (this.dateWithinRange(newDate)) {
780 this.date = newDate;
781 this.viewDate = newViewDate;
782 this.setValue();
783 this.update();
784 e.preventDefault();
785 dateChanged = true;
786 }
787 break;
788 case 13: // enter
789 this.hide();
790 e.preventDefault();
791 break;
792 case 9: // tab
793 this.hide();
794 break;
795 }
796 if (dateChanged) {
797 this.element.trigger({
798 type: 'changeDate',
799 date: this.date
800 });
801 var element;
802 if (this.isInput) {
803 element = this.element;
804 } else if (this.component) {
805 element = this.element.find('input');
806 }
807 if (element) {
808 element.change();
809 }
810 }
811 },
812
813 showMode: function (dir) {
814 if (dir) {
815 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
816 }
817 /*
818 vitalets: fixing bug of very special conditions:
819 jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
820 Method show() does not set display css correctly and datepicker is not shown.
821 Changed to .css('display', 'block') solve the problem.
822 See https://github.com/vitalets/x-editable/issues/37
823
824 In jquery 1.7.2+ everything works fine.
825 */
826 //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
827 this.picker.find('>div').hide().filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
828 this.updateNavArrows();
829 }
830 };
831
832 $.fn.datepicker = function (option) {
833 var args = Array.apply(null, arguments);
834 args.shift();
835 return this.each(function () {
836 var $this = $(this),
837 data = $this.data('datepicker'),
838 options = typeof option == 'object' && option;
839 if (!data) {
840 $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults, options))));
841 }
842 if (typeof option == 'string' && typeof data[option] == 'function') {
843 data[option].apply(data, args);
844 }
845 });
846 };
847
848 $.fn.datepicker.defaults = {
849 };
850 $.fn.datepicker.Constructor = Datepicker;
851 var dates = $.fn.datepicker.dates = {
852 en: {
853 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
854 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
855 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
856 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
857 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
858 today: "Today"
859 }
860 };
861
862 var DPGlobal = {
863 modes: [
864 {
865 clsName: 'days',
866 navFnc: 'Month',
867 navStep: 1
868 },
869 {
870 clsName: 'months',
871 navFnc: 'FullYear',
872 navStep: 1
873 },
874 {
875 clsName: 'years',
876 navFnc: 'FullYear',
877 navStep: 10
878 }],
879 isLeapYear: function (year) {
880 return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
881 },
882 getDaysInMonth: function (year, month) {
883 return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
884 },
885 validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
886 nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
887 parseFormat: function (format) {
888 // IE treats \0 as a string end in inputs (truncating the value),
889 // so it's a bad format delimiter, anyway
890 var separators = format.replace(this.validParts, '\0').split('\0'),
891 parts = format.match(this.validParts);
892 if (!separators || !separators.length || !parts || parts.length === 0) {
893 throw new Error("Invalid date format.");
894 }
895 return { separators: separators, parts: parts };
896 },
897 parseDate: function (date, format, language) {
898 if (date instanceof Date) return date;
899 if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
900 var part_re = /([\-+]\d+)([dmwy])/,
901 parts = date.match(/([\-+]\d+)([dmwy])/g),
902 part, dir;
903 date = new Date();
904 for (var i = 0; i < parts.length; i++) {
905 part = part_re.exec(parts[i]);
906 dir = parseInt(part[1]);
907 switch (part[2]) {
908 case 'd':
909 date.setUTCDate(date.getUTCDate() + dir);
910 break;
911 case 'm':
912 date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
913 break;
914 case 'w':
915 date.setUTCDate(date.getUTCDate() + dir * 7);
916 break;
917 case 'y':
918 date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
919 break;
920 }
921 }
922 return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
923 }
924 var parts = date && date.match(this.nonpunctuation) || [],
925 date = new Date(),
926 parsed = {},
927 setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
928 setters_map = {
929 yyyy: function (d, v) { return d.setUTCFullYear(v); },
930 yy: function (d, v) { return d.setUTCFullYear(2000 + v); },
931 m: function (d, v) {
932 v -= 1;
933 while (v < 0) v += 12;
934 v %= 12;
935 d.setUTCMonth(v);
936 while (d.getUTCMonth() != v)
937 d.setUTCDate(d.getUTCDate() - 1);
938 return d;
939 },
940 d: function (d, v) { return d.setUTCDate(v); }
941 },
942 val, filtered, part;
943 setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
944 setters_map['dd'] = setters_map['d'];
945 date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
946 var fparts = format.parts.slice();
947 // Remove noop parts
948 if (parts.length != fparts.length) {
949 fparts = $(fparts).filter(function (i, p) {
950 return $.inArray(p, setters_order) !== -1;
951 }).toArray();
952 }
953 // Process remainder
954 if (parts.length == fparts.length) {
955 for (var i = 0, cnt = fparts.length; i < cnt; i++) {
956 val = parseInt(parts[i], 10);
957 part = fparts[i];
958 if (isNaN(val)) {
959 switch (part) {
960 case 'MM':
961 filtered = $(dates[language].months).filter(function () {
962 var m = this.slice(0, parts[i].length),
963 p = parts[i].slice(0, m.length);
964 return m == p;
965 });
966 val = $.inArray(filtered[0], dates[language].months) + 1;
967 break;
968 case 'M':
969 filtered = $(dates[language].monthsShort).filter(function () {
970 var m = this.slice(0, parts[i].length),
971 p = parts[i].slice(0, m.length);
972 return m == p;
973 });
974 val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
975 break;
976 }
977 }
978 parsed[part] = val;
979 }
980 for (var i = 0, s; i < setters_order.length; i++) {
981 s = setters_order[i];
982 if (s in parsed && !isNaN(parsed[s]))
983 setters_map[s](date, parsed[s]);
984 }
985 }
986 return date;
987 },
988 formatDate: function (date, format, language) {
989 var val = {
990 d: date.getUTCDate(),
991 D: dates[language].daysShort[date.getUTCDay()],
992 DD: dates[language].days[date.getUTCDay()],
993 m: date.getUTCMonth() + 1,
994 M: dates[language].monthsShort[date.getUTCMonth()],
995 MM: dates[language].months[date.getUTCMonth()],
996 yy: date.getUTCFullYear().toString().substring(2),
997 yyyy: date.getUTCFullYear()
998 };
999 val.dd = (val.d < 10 ? '0' : '') + val.d;
1000 val.mm = (val.m < 10 ? '0' : '') + val.m;
1001 var date = [],
1002 seps = $.extend([], format.separators);
1003 for (var i = 0, cnt = format.parts.length; i < cnt; i++) {
1004 if (seps.length)
1005 date.push(seps.shift());
1006 date.push(val[format.parts[i]]);
1007 }
1008 return date.join('');
1009 },
1010 headTemplate: '<thead>' +
1011 '<tr>' +
1012 '<th class="prev"><i class="icon-arrow-left"/></th>' +
1013 '<th colspan="5" class="switch"></th>' +
1014 '<th class="next"><i class="icon-arrow-right"/></th>' +
1015 '</tr>' +
1016 '</thead>',
1017 contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1018 footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr></tfoot>'
1019 };
1020 DPGlobal.template = '<div class="datepicker">' +
1021 '<div class="datepicker-days">' +
1022 '<table class=" table-condensed">' +
1023 DPGlobal.headTemplate +
1024 '<tbody></tbody>' +
1025 DPGlobal.footTemplate +
1026 '</table>' +
1027 '</div>' +
1028 '<div class="datepicker-months">' +
1029 '<table class="table-condensed">' +
1030 DPGlobal.headTemplate +
1031 DPGlobal.contTemplate +
1032 DPGlobal.footTemplate +
1033 '</table>' +
1034 '</div>' +
1035 '<div class="datepicker-years">' +
1036 '<table class="table-condensed">' +
1037 DPGlobal.headTemplate +
1038 DPGlobal.contTemplate +
1039 DPGlobal.footTemplate +
1040 '</table>' +
1041 '</div>' +
1042 '</div>';
1043
1044 $.fn.datepicker.DPGlobal = DPGlobal;
1045
1046}(window.jQuery);
Note: See TracBrowser for help on using the repository browser.