volt-datepicker 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,1080 @@
1
+ /*!
2
+ * Pikaday
3
+ *
4
+ * Copyright © 2014 David Bushell | BSD & MIT license | https://github.com/dbushell/Pikaday
5
+ */
6
+
7
+ (function (root, factory)
8
+ {
9
+ 'use strict';
10
+
11
+ var moment;
12
+ if (typeof exports === 'object') {
13
+ // CommonJS module
14
+ // Load moment.js as an optional dependency
15
+ try { moment = require('moment'); } catch (e) {}
16
+ module.exports = factory(moment);
17
+ } else if (typeof define === 'function' && define.amd) {
18
+ // AMD. Register as an anonymous module.
19
+ define(function (req)
20
+ {
21
+ // Load moment.js as an optional dependency
22
+ var id = 'moment';
23
+ try { moment = req(id); } catch (e) {}
24
+ return factory(moment);
25
+ });
26
+ } else {
27
+ root.Pikaday = factory(root.moment);
28
+ }
29
+ }(this, function (moment)
30
+ {
31
+ 'use strict';
32
+
33
+ /**
34
+ * feature detection and helper functions
35
+ */
36
+ var hasMoment = typeof moment === 'function',
37
+
38
+ hasEventListeners = !!window.addEventListener,
39
+
40
+ document = window.document,
41
+
42
+ sto = window.setTimeout,
43
+
44
+ addEvent = function(el, e, callback, capture)
45
+ {
46
+ if (hasEventListeners) {
47
+ el.addEventListener(e, callback, !!capture);
48
+ } else {
49
+ el.attachEvent('on' + e, callback);
50
+ }
51
+ },
52
+
53
+ removeEvent = function(el, e, callback, capture)
54
+ {
55
+ if (hasEventListeners) {
56
+ el.removeEventListener(e, callback, !!capture);
57
+ } else {
58
+ el.detachEvent('on' + e, callback);
59
+ }
60
+ },
61
+
62
+ fireEvent = function(el, eventName, data)
63
+ {
64
+ var ev;
65
+
66
+ if (document.createEvent) {
67
+ ev = document.createEvent('HTMLEvents');
68
+ ev.initEvent(eventName, true, false);
69
+ ev = extend(ev, data);
70
+ el.dispatchEvent(ev);
71
+ } else if (document.createEventObject) {
72
+ ev = document.createEventObject();
73
+ ev = extend(ev, data);
74
+ el.fireEvent('on' + eventName, ev);
75
+ }
76
+ },
77
+
78
+ trim = function(str)
79
+ {
80
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,'');
81
+ },
82
+
83
+ hasClass = function(el, cn)
84
+ {
85
+ return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
86
+ },
87
+
88
+ addClass = function(el, cn)
89
+ {
90
+ if (!hasClass(el, cn)) {
91
+ el.className = (el.className === '') ? cn : el.className + ' ' + cn;
92
+ }
93
+ },
94
+
95
+ removeClass = function(el, cn)
96
+ {
97
+ el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' '));
98
+ },
99
+
100
+ isArray = function(obj)
101
+ {
102
+ return (/Array/).test(Object.prototype.toString.call(obj));
103
+ },
104
+
105
+ isDate = function(obj)
106
+ {
107
+ return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
108
+ },
109
+
110
+ isWeekend = function(date)
111
+ {
112
+ var day = date.getDay();
113
+ return day === 0 || day === 6;
114
+ },
115
+
116
+ isLeapYear = function(year)
117
+ {
118
+ // solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
119
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
120
+ },
121
+
122
+ getDaysInMonth = function(year, month)
123
+ {
124
+ return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
125
+ },
126
+
127
+ setToStartOfDay = function(date)
128
+ {
129
+ if (isDate(date)) date.setHours(0,0,0,0);
130
+ },
131
+
132
+ compareDates = function(a,b)
133
+ {
134
+ // weak date comparison (use setToStartOfDay(date) to ensure correct result)
135
+ return a.getTime() === b.getTime();
136
+ },
137
+
138
+ extend = function(to, from, overwrite)
139
+ {
140
+ var prop, hasProp;
141
+ for (prop in from) {
142
+ hasProp = to[prop] !== undefined;
143
+ if (hasProp && typeof from[prop] === 'object' && from[prop] !== null && from[prop].nodeName === undefined) {
144
+ if (isDate(from[prop])) {
145
+ if (overwrite) {
146
+ to[prop] = new Date(from[prop].getTime());
147
+ }
148
+ }
149
+ else if (isArray(from[prop])) {
150
+ if (overwrite) {
151
+ to[prop] = from[prop].slice(0);
152
+ }
153
+ } else {
154
+ to[prop] = extend({}, from[prop], overwrite);
155
+ }
156
+ } else if (overwrite || !hasProp) {
157
+ to[prop] = from[prop];
158
+ }
159
+ }
160
+ return to;
161
+ },
162
+
163
+ adjustCalendar = function(calendar) {
164
+ if (calendar.month < 0) {
165
+ calendar.year -= Math.ceil(Math.abs(calendar.month)/12);
166
+ calendar.month += 12;
167
+ }
168
+ if (calendar.month > 11) {
169
+ calendar.year += Math.floor(Math.abs(calendar.month)/12);
170
+ calendar.month -= 12;
171
+ }
172
+ return calendar;
173
+ },
174
+
175
+ /**
176
+ * defaults and localisation
177
+ */
178
+ defaults = {
179
+
180
+ // bind the picker to a form field
181
+ field: null,
182
+
183
+ // automatically show/hide the picker on `field` focus (default `true` if `field` is set)
184
+ bound: undefined,
185
+
186
+ // position of the datepicker, relative to the field (default to bottom & left)
187
+ // ('bottom' & 'left' keywords are not used, 'top' & 'right' are modifier on the bottom/left position)
188
+ position: 'bottom left',
189
+
190
+ // automatically fit in the viewport even if it means repositioning from the position option
191
+ reposition: true,
192
+
193
+ // the default output format for `.toString()` and `field` value
194
+ format: 'YYYY-MM-DD',
195
+
196
+ // the initial date to view when first opened
197
+ defaultDate: null,
198
+
199
+ // make the `defaultDate` the initial selected value
200
+ setDefaultDate: false,
201
+
202
+ // first day of week (0: Sunday, 1: Monday etc)
203
+ firstDay: 0,
204
+
205
+ // the minimum/earliest date that can be selected
206
+ minDate: null,
207
+ // the maximum/latest date that can be selected
208
+ maxDate: null,
209
+
210
+ // number of years either side, or array of upper/lower range
211
+ yearRange: 10,
212
+
213
+ // show week numbers at head of row
214
+ showWeekNumber: false,
215
+
216
+ // used internally (don't config outside)
217
+ minYear: 0,
218
+ maxYear: 9999,
219
+ minMonth: undefined,
220
+ maxMonth: undefined,
221
+
222
+ startRange: null,
223
+ endRange: null,
224
+
225
+ isRTL: false,
226
+
227
+ // Additional text to append to the year in the calendar title
228
+ yearSuffix: '',
229
+
230
+ // Render the month after year in the calendar title
231
+ showMonthAfterYear: false,
232
+
233
+ // how many months are visible
234
+ numberOfMonths: 1,
235
+
236
+ // when numberOfMonths is used, this will help you to choose where the main calendar will be (default `left`, can be set to `right`)
237
+ // only used for the first display or when a selected date is not visible
238
+ mainCalendar: 'left',
239
+
240
+ // Specify a DOM element to render the calendar in
241
+ container: undefined,
242
+
243
+ // internationalization
244
+ i18n: {
245
+ previousMonth : 'Previous Month',
246
+ nextMonth : 'Next Month',
247
+ months : ['January','February','March','April','May','June','July','August','September','October','November','December'],
248
+ weekdays : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
249
+ weekdaysShort : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
250
+ },
251
+
252
+ // Theme Classname
253
+ theme: null,
254
+
255
+ // callback function
256
+ onSelect: null,
257
+ onOpen: null,
258
+ onClose: null,
259
+ onDraw: null
260
+ },
261
+
262
+
263
+ /**
264
+ * templating functions to abstract HTML rendering
265
+ */
266
+ renderDayName = function(opts, day, abbr)
267
+ {
268
+ day += opts.firstDay;
269
+ while (day >= 7) {
270
+ day -= 7;
271
+ }
272
+ return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];
273
+ },
274
+
275
+ renderDay = function(opts)
276
+ {
277
+ if (opts.isEmpty) {
278
+ return '<td class="is-empty"></td>';
279
+ }
280
+ var arr = [];
281
+ if (opts.isDisabled) {
282
+ arr.push('is-disabled');
283
+ }
284
+ if (opts.isToday) {
285
+ arr.push('is-today');
286
+ }
287
+ if (opts.isSelected) {
288
+ arr.push('is-selected');
289
+ }
290
+ if (opts.isInRange) {
291
+ arr.push('is-inrange');
292
+ }
293
+ if (opts.isStartRange) {
294
+ arr.push('is-startrange');
295
+ }
296
+ if (opts.isEndRange) {
297
+ arr.push('is-endrange');
298
+ }
299
+ return '<td data-day="' + opts.day + '" class="' + arr.join(' ') + '">' +
300
+ '<button class="pika-button pika-day" type="button" ' +
301
+ 'data-pika-year="' + opts.year + '" data-pika-month="' + opts.month + '" data-pika-day="' + opts.day + '">' +
302
+ opts.day +
303
+ '</button>' +
304
+ '</td>';
305
+ },
306
+
307
+ renderWeek = function (d, m, y) {
308
+ // Lifted from http://javascript.about.com/library/blweekyear.htm, lightly modified.
309
+ var onejan = new Date(y, 0, 1),
310
+ weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getDay()+1)/7);
311
+ return '<td class="pika-week">' + weekNum + '</td>';
312
+ },
313
+
314
+ renderRow = function(days, isRTL)
315
+ {
316
+ return '<tr>' + (isRTL ? days.reverse() : days).join('') + '</tr>';
317
+ },
318
+
319
+ renderBody = function(rows)
320
+ {
321
+ return '<tbody>' + rows.join('') + '</tbody>';
322
+ },
323
+
324
+ renderHead = function(opts)
325
+ {
326
+ var i, arr = [];
327
+ if (opts.showWeekNumber) {
328
+ arr.push('<th></th>');
329
+ }
330
+ for (i = 0; i < 7; i++) {
331
+ arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>');
332
+ }
333
+ return '<thead>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</thead>';
334
+ },
335
+
336
+ renderTitle = function(instance, c, year, month, refYear)
337
+ {
338
+ var i, j, arr,
339
+ opts = instance._o,
340
+ isMinYear = year === opts.minYear,
341
+ isMaxYear = year === opts.maxYear,
342
+ html = '<div class="pika-title">',
343
+ monthHtml,
344
+ yearHtml,
345
+ prev = true,
346
+ next = true;
347
+
348
+ for (arr = [], i = 0; i < 12; i++) {
349
+ arr.push('<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' +
350
+ (i === month ? ' selected': '') +
351
+ ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? 'disabled' : '') + '>' +
352
+ opts.i18n.months[i] + '</option>');
353
+ }
354
+ monthHtml = '<div class="pika-label">' + opts.i18n.months[month] + '<select class="pika-select pika-select-month" tabindex="-1">' + arr.join('') + '</select></div>';
355
+
356
+ if (isArray(opts.yearRange)) {
357
+ i = opts.yearRange[0];
358
+ j = opts.yearRange[1] + 1;
359
+ } else {
360
+ i = year - opts.yearRange;
361
+ j = 1 + year + opts.yearRange;
362
+ }
363
+
364
+ for (arr = []; i < j && i <= opts.maxYear; i++) {
365
+ if (i >= opts.minYear) {
366
+ arr.push('<option value="' + i + '"' + (i === year ? ' selected': '') + '>' + (i) + '</option>');
367
+ }
368
+ }
369
+ yearHtml = '<div class="pika-label">' + year + opts.yearSuffix + '<select class="pika-select pika-select-year" tabindex="-1">' + arr.join('') + '</select></div>';
370
+
371
+ if (opts.showMonthAfterYear) {
372
+ html += yearHtml + monthHtml;
373
+ } else {
374
+ html += monthHtml + yearHtml;
375
+ }
376
+
377
+ if (isMinYear && (month === 0 || opts.minMonth >= month)) {
378
+ prev = false;
379
+ }
380
+
381
+ if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
382
+ next = false;
383
+ }
384
+
385
+ if (c === 0) {
386
+ html += '<button class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>';
387
+ }
388
+ if (c === (instance._o.numberOfMonths - 1) ) {
389
+ html += '<button class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>';
390
+ }
391
+
392
+ return html += '</div>';
393
+ },
394
+
395
+ renderTable = function(opts, data)
396
+ {
397
+ return '<table cellpadding="0" cellspacing="0" class="pika-table">' + renderHead(opts) + renderBody(data) + '</table>';
398
+ },
399
+
400
+
401
+ /**
402
+ * Pikaday constructor
403
+ */
404
+ Pikaday = function(options)
405
+ {
406
+ var self = this,
407
+ opts = self.config(options);
408
+
409
+ self._onMouseDown = function(e)
410
+ {
411
+ if (!self._v) {
412
+ return;
413
+ }
414
+ e = e || window.event;
415
+ var target = e.target || e.srcElement;
416
+ if (!target) {
417
+ return;
418
+ }
419
+
420
+ if (!hasClass(target.parentNode, 'is-disabled')) {
421
+ if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty')) {
422
+ self.setDate(new Date(target.getAttribute('data-pika-year'), target.getAttribute('data-pika-month'), target.getAttribute('data-pika-day')));
423
+ if (opts.bound) {
424
+ sto(function() {
425
+ self.hide();
426
+ if (opts.field) {
427
+ opts.field.blur();
428
+ }
429
+ }, 100);
430
+ }
431
+ return;
432
+ }
433
+ else if (hasClass(target, 'pika-prev')) {
434
+ self.prevMonth();
435
+ }
436
+ else if (hasClass(target, 'pika-next')) {
437
+ self.nextMonth();
438
+ }
439
+ }
440
+ if (!hasClass(target, 'pika-select')) {
441
+ if (e.preventDefault) {
442
+ e.preventDefault();
443
+ } else {
444
+ e.returnValue = false;
445
+ return false;
446
+ }
447
+ } else {
448
+ self._c = true;
449
+ }
450
+ };
451
+
452
+ self._onChange = function(e)
453
+ {
454
+ e = e || window.event;
455
+ var target = e.target || e.srcElement;
456
+ if (!target) {
457
+ return;
458
+ }
459
+ if (hasClass(target, 'pika-select-month')) {
460
+ self.gotoMonth(target.value);
461
+ }
462
+ else if (hasClass(target, 'pika-select-year')) {
463
+ self.gotoYear(target.value);
464
+ }
465
+ };
466
+
467
+ self._onInputChange = function(e)
468
+ {
469
+ var date;
470
+
471
+ if (e.firedBy === self) {
472
+ return;
473
+ }
474
+ if (hasMoment) {
475
+ date = moment(opts.field.value, opts.format);
476
+ date = (date && date.isValid()) ? date.toDate() : null;
477
+ }
478
+ else {
479
+ date = new Date(Date.parse(opts.field.value));
480
+ }
481
+ if (isDate(date)) {
482
+ self.setDate(date);
483
+ }
484
+ if (!self._v) {
485
+ self.show();
486
+ }
487
+ };
488
+
489
+ self._onInputFocus = function()
490
+ {
491
+ self.show();
492
+ };
493
+
494
+ self._onInputClick = function()
495
+ {
496
+ self.show();
497
+ };
498
+
499
+ self._onInputBlur = function()
500
+ {
501
+ // IE allows pika div to gain focus; catch blur the input field
502
+ var pEl = document.activeElement;
503
+ do {
504
+ if (hasClass(pEl, 'pika-single')) {
505
+ return;
506
+ }
507
+ }
508
+ while ((pEl = pEl.parentNode));
509
+
510
+ if (!self._c) {
511
+ self._b = sto(function() {
512
+ self.hide();
513
+ }, 50);
514
+ }
515
+ self._c = false;
516
+ };
517
+
518
+ self._onClick = function(e)
519
+ {
520
+ e = e || window.event;
521
+ var target = e.target || e.srcElement,
522
+ pEl = target;
523
+ if (!target) {
524
+ return;
525
+ }
526
+ if (!hasEventListeners && hasClass(target, 'pika-select')) {
527
+ if (!target.onchange) {
528
+ target.setAttribute('onchange', 'return;');
529
+ addEvent(target, 'change', self._onChange);
530
+ }
531
+ }
532
+ do {
533
+ if (hasClass(pEl, 'pika-single') || pEl === opts.trigger) {
534
+ return;
535
+ }
536
+ }
537
+ while ((pEl = pEl.parentNode));
538
+ if (self._v && target !== opts.trigger && pEl !== opts.trigger) {
539
+ self.hide();
540
+ }
541
+ };
542
+
543
+ self.el = document.createElement('div');
544
+ self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '') + (opts.theme ? ' ' + opts.theme : '');
545
+
546
+ addEvent(self.el, 'ontouchend' in document ? 'touchend' : 'mousedown', self._onMouseDown, true);
547
+ addEvent(self.el, 'change', self._onChange);
548
+
549
+ if (opts.field) {
550
+ if (opts.container) {
551
+ opts.container.appendChild(self.el);
552
+ } else if (opts.bound) {
553
+ document.body.appendChild(self.el);
554
+ } else {
555
+ opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);
556
+ }
557
+ addEvent(opts.field, 'change', self._onInputChange);
558
+
559
+ if (!opts.defaultDate) {
560
+ if (hasMoment && opts.field.value) {
561
+ opts.defaultDate = moment(opts.field.value, opts.format).toDate();
562
+ } else {
563
+ opts.defaultDate = new Date(Date.parse(opts.field.value));
564
+ }
565
+ opts.setDefaultDate = true;
566
+ }
567
+ }
568
+
569
+ var defDate = opts.defaultDate;
570
+
571
+ if (isDate(defDate)) {
572
+ if (opts.setDefaultDate) {
573
+ self.setDate(defDate, true);
574
+ } else {
575
+ self.gotoDate(defDate);
576
+ }
577
+ } else {
578
+ self.gotoDate(new Date());
579
+ }
580
+
581
+ if (opts.bound) {
582
+ this.hide();
583
+ self.el.className += ' is-bound';
584
+ addEvent(opts.trigger, 'click', self._onInputClick);
585
+ addEvent(opts.trigger, 'focus', self._onInputFocus);
586
+ addEvent(opts.trigger, 'blur', self._onInputBlur);
587
+ } else {
588
+ this.show();
589
+ }
590
+ };
591
+
592
+
593
+ /**
594
+ * public Pikaday API
595
+ */
596
+ Pikaday.prototype = {
597
+
598
+
599
+ /**
600
+ * configure functionality
601
+ */
602
+ config: function(options)
603
+ {
604
+ if (!this._o) {
605
+ this._o = extend({}, defaults, true);
606
+ }
607
+
608
+ var opts = extend(this._o, options, true);
609
+
610
+ opts.isRTL = !!opts.isRTL;
611
+
612
+ opts.field = (opts.field && opts.field.nodeName) ? opts.field : null;
613
+
614
+ opts.theme = (typeof opts.theme) === 'string' && opts.theme ? opts.theme : null;
615
+
616
+ opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field);
617
+
618
+ opts.trigger = (opts.trigger && opts.trigger.nodeName) ? opts.trigger : opts.field;
619
+
620
+ opts.disableWeekends = !!opts.disableWeekends;
621
+
622
+ opts.disableDayFn = (typeof opts.disableDayFn) === 'function' ? opts.disableDayFn : null;
623
+
624
+ var nom = parseInt(opts.numberOfMonths, 10) || 1;
625
+ opts.numberOfMonths = nom > 4 ? 4 : nom;
626
+
627
+ if (!isDate(opts.minDate)) {
628
+ opts.minDate = false;
629
+ }
630
+ if (!isDate(opts.maxDate)) {
631
+ opts.maxDate = false;
632
+ }
633
+ if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) {
634
+ opts.maxDate = opts.minDate = false;
635
+ }
636
+ if (opts.minDate) {
637
+ this.setMinDate(opts.minDate);
638
+ }
639
+ if (opts.maxDate) {
640
+ setToStartOfDay(opts.maxDate);
641
+ opts.maxYear = opts.maxDate.getFullYear();
642
+ opts.maxMonth = opts.maxDate.getMonth();
643
+ }
644
+
645
+ if (isArray(opts.yearRange)) {
646
+ var fallback = new Date().getFullYear() - 10;
647
+ opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;
648
+ opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;
649
+ } else {
650
+ opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;
651
+ if (opts.yearRange > 100) {
652
+ opts.yearRange = 100;
653
+ }
654
+ }
655
+
656
+ return opts;
657
+ },
658
+
659
+ /**
660
+ * return a formatted string of the current selection (using Moment.js if available)
661
+ */
662
+ toString: function(format)
663
+ {
664
+ return !isDate(this._d) ? '' : hasMoment ? moment(this._d).format(format || this._o.format) : this._d.toDateString();
665
+ },
666
+
667
+ /**
668
+ * return a Moment.js object of the current selection (if available)
669
+ */
670
+ getMoment: function()
671
+ {
672
+ return hasMoment ? moment(this._d) : null;
673
+ },
674
+
675
+ /**
676
+ * set the current selection from a Moment.js object (if available)
677
+ */
678
+ setMoment: function(date, preventOnSelect)
679
+ {
680
+ if (hasMoment && moment.isMoment(date)) {
681
+ this.setDate(date.toDate(), preventOnSelect);
682
+ }
683
+ },
684
+
685
+ /**
686
+ * return a Date object of the current selection
687
+ */
688
+ getDate: function()
689
+ {
690
+ return isDate(this._d) ? new Date(this._d.getTime()) : null;
691
+ },
692
+
693
+ /**
694
+ * set the current selection
695
+ */
696
+ setDate: function(date, preventOnSelect)
697
+ {
698
+ if (!date) {
699
+ this._d = null;
700
+
701
+ if (this._o.field) {
702
+ this._o.field.value = '';
703
+ fireEvent(this._o.field, 'change', { firedBy: this });
704
+ }
705
+
706
+ return this.draw();
707
+ }
708
+ if (typeof date === 'string') {
709
+ date = new Date(Date.parse(date));
710
+ }
711
+ if (!isDate(date)) {
712
+ return;
713
+ }
714
+
715
+ var min = this._o.minDate,
716
+ max = this._o.maxDate;
717
+
718
+ if (isDate(min) && date < min) {
719
+ date = min;
720
+ } else if (isDate(max) && date > max) {
721
+ date = max;
722
+ }
723
+
724
+ this._d = new Date(date.getTime());
725
+ setToStartOfDay(this._d);
726
+ this.gotoDate(this._d);
727
+
728
+ if (this._o.field) {
729
+ this._o.field.value = this.toString();
730
+ fireEvent(this._o.field, 'change', { firedBy: this });
731
+ }
732
+ if (!preventOnSelect && typeof this._o.onSelect === 'function') {
733
+ this._o.onSelect.call(this, this.getDate());
734
+ }
735
+ },
736
+
737
+ /**
738
+ * change view to a specific date
739
+ */
740
+ gotoDate: function(date)
741
+ {
742
+ var newCalendar = true;
743
+
744
+ if (!isDate(date)) {
745
+ return;
746
+ }
747
+
748
+ if (this.calendars) {
749
+ var firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
750
+ lastVisibleDate = new Date(this.calendars[this.calendars.length-1].year, this.calendars[this.calendars.length-1].month, 1),
751
+ visibleDate = date.getTime();
752
+ // get the end of the month
753
+ lastVisibleDate.setMonth(lastVisibleDate.getMonth()+1);
754
+ lastVisibleDate.setDate(lastVisibleDate.getDate()-1);
755
+ newCalendar = (visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate);
756
+ }
757
+
758
+ if (newCalendar) {
759
+ this.calendars = [{
760
+ month: date.getMonth(),
761
+ year: date.getFullYear()
762
+ }];
763
+ if (this._o.mainCalendar === 'right') {
764
+ this.calendars[0].month += 1 - this._o.numberOfMonths;
765
+ }
766
+ }
767
+
768
+ this.adjustCalendars();
769
+ },
770
+
771
+ adjustCalendars: function() {
772
+ this.calendars[0] = adjustCalendar(this.calendars[0]);
773
+ for (var c = 1; c < this._o.numberOfMonths; c++) {
774
+ this.calendars[c] = adjustCalendar({
775
+ month: this.calendars[0].month + c,
776
+ year: this.calendars[0].year
777
+ });
778
+ }
779
+ this.draw();
780
+ },
781
+
782
+ gotoToday: function()
783
+ {
784
+ this.gotoDate(new Date());
785
+ },
786
+
787
+ /**
788
+ * change view to a specific month (zero-index, e.g. 0: January)
789
+ */
790
+ gotoMonth: function(month)
791
+ {
792
+ if (!isNaN(month)) {
793
+ this.calendars[0].month = parseInt(month, 10);
794
+ this.adjustCalendars();
795
+ }
796
+ },
797
+
798
+ nextMonth: function()
799
+ {
800
+ this.calendars[0].month++;
801
+ this.adjustCalendars();
802
+ },
803
+
804
+ prevMonth: function()
805
+ {
806
+ this.calendars[0].month--;
807
+ this.adjustCalendars();
808
+ },
809
+
810
+ /**
811
+ * change view to a specific full year (e.g. "2012")
812
+ */
813
+ gotoYear: function(year)
814
+ {
815
+ if (!isNaN(year)) {
816
+ this.calendars[0].year = parseInt(year, 10);
817
+ this.adjustCalendars();
818
+ }
819
+ },
820
+
821
+ /**
822
+ * change the minDate
823
+ */
824
+ setMinDate: function(value)
825
+ {
826
+ setToStartOfDay(value);
827
+ this._o.minDate = value;
828
+ this._o.minYear = value.getFullYear();
829
+ this._o.minMonth = value.getMonth();
830
+ },
831
+
832
+ /**
833
+ * change the maxDate
834
+ */
835
+ setMaxDate: function(value)
836
+ {
837
+ this._o.maxDate = value;
838
+ },
839
+
840
+ setStartRange: function(value)
841
+ {
842
+ this._o.startRange = value;
843
+ },
844
+
845
+ setEndRange: function(value)
846
+ {
847
+ this._o.endRange = value;
848
+ },
849
+
850
+ /**
851
+ * refresh the HTML
852
+ */
853
+ draw: function(force)
854
+ {
855
+ if (!this._v && !force) {
856
+ return;
857
+ }
858
+ var opts = this._o,
859
+ minYear = opts.minYear,
860
+ maxYear = opts.maxYear,
861
+ minMonth = opts.minMonth,
862
+ maxMonth = opts.maxMonth,
863
+ html = '';
864
+
865
+ if (this._y <= minYear) {
866
+ this._y = minYear;
867
+ if (!isNaN(minMonth) && this._m < minMonth) {
868
+ this._m = minMonth;
869
+ }
870
+ }
871
+ if (this._y >= maxYear) {
872
+ this._y = maxYear;
873
+ if (!isNaN(maxMonth) && this._m > maxMonth) {
874
+ this._m = maxMonth;
875
+ }
876
+ }
877
+
878
+ for (var c = 0; c < opts.numberOfMonths; c++) {
879
+ html += '<div class="pika-lendar">' + renderTitle(this, c, this.calendars[c].year, this.calendars[c].month, this.calendars[0].year) + this.render(this.calendars[c].year, this.calendars[c].month) + '</div>';
880
+ }
881
+
882
+ this.el.innerHTML = html;
883
+
884
+ if (opts.bound) {
885
+ if(opts.field.type !== 'hidden') {
886
+ sto(function() {
887
+ opts.trigger.focus();
888
+ }, 1);
889
+ }
890
+ }
891
+
892
+ if (typeof this._o.onDraw === 'function') {
893
+ var self = this;
894
+ sto(function() {
895
+ self._o.onDraw.call(self);
896
+ }, 0);
897
+ }
898
+ },
899
+
900
+ adjustPosition: function()
901
+ {
902
+ var field, pEl, width, height, viewportWidth, viewportHeight, scrollTop, left, top, clientRect;
903
+
904
+ if (this._o.container) return;
905
+
906
+ this.el.style.position = 'absolute';
907
+
908
+ field = this._o.trigger;
909
+ pEl = field;
910
+ width = this.el.offsetWidth;
911
+ height = this.el.offsetHeight;
912
+ viewportWidth = window.innerWidth || document.documentElement.clientWidth;
913
+ viewportHeight = window.innerHeight || document.documentElement.clientHeight;
914
+ scrollTop = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
915
+
916
+ if (typeof field.getBoundingClientRect === 'function') {
917
+ clientRect = field.getBoundingClientRect();
918
+ left = clientRect.left + window.pageXOffset;
919
+ top = clientRect.bottom + window.pageYOffset;
920
+ } else {
921
+ left = pEl.offsetLeft;
922
+ top = pEl.offsetTop + pEl.offsetHeight;
923
+ while((pEl = pEl.offsetParent)) {
924
+ left += pEl.offsetLeft;
925
+ top += pEl.offsetTop;
926
+ }
927
+ }
928
+
929
+ // default position is bottom & left
930
+ if ((this._o.reposition && left + width > viewportWidth) ||
931
+ (
932
+ this._o.position.indexOf('right') > -1 &&
933
+ left - width + field.offsetWidth > 0
934
+ )
935
+ ) {
936
+ left = left - width + field.offsetWidth;
937
+ }
938
+ if ((this._o.reposition && top + height > viewportHeight + scrollTop) ||
939
+ (
940
+ this._o.position.indexOf('top') > -1 &&
941
+ top - height - field.offsetHeight > 0
942
+ )
943
+ ) {
944
+ top = top - height - field.offsetHeight;
945
+ }
946
+
947
+ this.el.style.left = left + 'px';
948
+ this.el.style.top = top + 'px';
949
+ },
950
+
951
+ /**
952
+ * render HTML for a particular month
953
+ */
954
+ render: function(year, month)
955
+ {
956
+ var opts = this._o,
957
+ now = new Date(),
958
+ days = getDaysInMonth(year, month),
959
+ before = new Date(year, month, 1).getDay(),
960
+ data = [],
961
+ row = [];
962
+ setToStartOfDay(now);
963
+ if (opts.firstDay > 0) {
964
+ before -= opts.firstDay;
965
+ if (before < 0) {
966
+ before += 7;
967
+ }
968
+ }
969
+ var cells = days + before,
970
+ after = cells;
971
+ while(after > 7) {
972
+ after -= 7;
973
+ }
974
+ cells += 7 - after;
975
+ for (var i = 0, r = 0; i < cells; i++)
976
+ {
977
+ var dayConfig,
978
+ day = new Date(year, month, 1 + (i - before)),
979
+ isSelected = isDate(this._d) ? compareDates(day, this._d) : false,
980
+ isToday = compareDates(day, now),
981
+ isEmpty = i < before || i >= (days + before),
982
+ isStartRange = opts.startRange && compareDates(opts.startRange, day),
983
+ isEndRange = opts.endRange && compareDates(opts.endRange, day),
984
+ isInRange = opts.startRange && opts.endRange && opts.startRange < day && day < opts.endRange,
985
+ isDisabled = (opts.minDate && day < opts.minDate) ||
986
+ (opts.maxDate && day > opts.maxDate) ||
987
+ (opts.disableWeekends && isWeekend(day)) ||
988
+ (opts.disableDayFn && opts.disableDayFn(day)),
989
+ dayConfig = {
990
+ day: 1 + (i - before),
991
+ month: month,
992
+ year: year,
993
+ isSelected: isSelected,
994
+ isToday: isToday,
995
+ isDisabled: isDisabled,
996
+ isEmpty: isEmpty,
997
+ isStartRange: isStartRange,
998
+ isEndRange: isEndRange,
999
+ isInRange: isInRange
1000
+ };
1001
+
1002
+ row.push(renderDay(dayConfig));
1003
+
1004
+ if (++r === 7) {
1005
+ if (opts.showWeekNumber) {
1006
+ row.unshift(renderWeek(i - before, month, year));
1007
+ }
1008
+ data.push(renderRow(row, opts.isRTL));
1009
+ row = [];
1010
+ r = 0;
1011
+ }
1012
+ }
1013
+ return renderTable(opts, data);
1014
+ },
1015
+
1016
+ isVisible: function()
1017
+ {
1018
+ return this._v;
1019
+ },
1020
+
1021
+ show: function()
1022
+ {
1023
+ if (!this._v) {
1024
+ removeClass(this.el, 'is-hidden');
1025
+ this._v = true;
1026
+ this.draw();
1027
+ if (this._o.bound) {
1028
+ addEvent(document, 'click', this._onClick);
1029
+ this.adjustPosition();
1030
+ }
1031
+ if (typeof this._o.onOpen === 'function') {
1032
+ this._o.onOpen.call(this);
1033
+ }
1034
+ }
1035
+ },
1036
+
1037
+ hide: function()
1038
+ {
1039
+ var v = this._v;
1040
+ if (v !== false) {
1041
+ if (this._o.bound) {
1042
+ removeEvent(document, 'click', this._onClick);
1043
+ }
1044
+ this.el.style.position = 'static'; // reset
1045
+ this.el.style.left = 'auto';
1046
+ this.el.style.top = 'auto';
1047
+ addClass(this.el, 'is-hidden');
1048
+ this._v = false;
1049
+ if (v !== undefined && typeof this._o.onClose === 'function') {
1050
+ this._o.onClose.call(this);
1051
+ }
1052
+ }
1053
+ },
1054
+
1055
+ /**
1056
+ * GAME OVER
1057
+ */
1058
+ destroy: function()
1059
+ {
1060
+ this.hide();
1061
+ removeEvent(this.el, 'mousedown', this._onMouseDown, true);
1062
+ removeEvent(this.el, 'change', this._onChange);
1063
+ if (this._o.field) {
1064
+ removeEvent(this._o.field, 'change', this._onInputChange);
1065
+ if (this._o.bound) {
1066
+ removeEvent(this._o.trigger, 'click', this._onInputClick);
1067
+ removeEvent(this._o.trigger, 'focus', this._onInputFocus);
1068
+ removeEvent(this._o.trigger, 'blur', this._onInputBlur);
1069
+ }
1070
+ }
1071
+ if (this.el.parentNode) {
1072
+ this.el.parentNode.removeChild(this.el);
1073
+ }
1074
+ }
1075
+
1076
+ };
1077
+
1078
+ return Pikaday;
1079
+
1080
+ }));