pikaday-gem 1.0.0.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,816 @@
1
+ /*!
2
+ * Pikaday
3
+ * Copyright © 2012 David Bushell | BSD & MIT license | http://dbushell.com/
4
+ */
5
+
6
+ (function(window, document, undefined)
7
+ {
8
+ 'use strict';
9
+
10
+ /**
11
+ * feature detection and helper functions
12
+ */
13
+ var hasMoment = typeof window.moment === 'function',
14
+
15
+ hasEventListeners = !!window.addEventListener,
16
+
17
+ sto = window.setTimeout,
18
+
19
+ addEvent = function(el, e, callback, capture)
20
+ {
21
+ if (hasEventListeners) {
22
+ el.addEventListener(e, callback, !!capture);
23
+ } else {
24
+ el.attachEvent('on' + e, callback);
25
+ }
26
+ },
27
+
28
+ removeEvent = function(el, e, callback, capture)
29
+ {
30
+ if (hasEventListeners) {
31
+ el.removeEventListener(e, callback, !!capture);
32
+ } else {
33
+ el.detachEvent('on' + e, callback);
34
+ }
35
+ },
36
+
37
+ fireEvent = function(el, eventName, data)
38
+ {
39
+ var ev;
40
+
41
+ if (document.createEvent) {
42
+ ev = document.createEvent('HTMLEvents');
43
+ ev.initEvent(eventName, true, false);
44
+ ev = extend(ev, data);
45
+ el.dispatchEvent(ev);
46
+ } else if (document.createEventObject) {
47
+ ev = document.createEventObject();
48
+ ev = extend(ev, data);
49
+ el.fireEvent('on' + eventName, ev);
50
+ }
51
+ },
52
+
53
+ trim = function(str)
54
+ {
55
+ return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g,'');
56
+ },
57
+
58
+ hasClass = function(el, cn)
59
+ {
60
+ return (' ' + el.className + ' ').indexOf(' ' + cn + ' ') !== -1;
61
+ },
62
+
63
+ addClass = function(el, cn)
64
+ {
65
+ if (!hasClass(el, cn)) {
66
+ el.className = (el.className === '') ? cn : el.className + ' ' + cn;
67
+ }
68
+ },
69
+
70
+ removeClass = function(el, cn)
71
+ {
72
+ el.className = trim((' ' + el.className + ' ').replace(' ' + cn + ' ', ' '));
73
+ },
74
+
75
+ isArray = function(obj)
76
+ {
77
+ return (/Array/).test(Object.prototype.toString.call(obj));
78
+ },
79
+
80
+ isDate = function(obj)
81
+ {
82
+ return (/Date/).test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
83
+ },
84
+
85
+ isLeapYear = function(year)
86
+ {
87
+ // solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
88
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
89
+ },
90
+
91
+ getDaysInMonth = function(year, month)
92
+ {
93
+ return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
94
+ },
95
+
96
+ setToStartOfDay = function(date)
97
+ {
98
+ if (isDate(date)) date.setHours(0,0,0,0);
99
+ },
100
+
101
+ compareDates = function(a,b)
102
+ {
103
+ // weak date comparison (use setToStartOfDay(date) to ensure correct result)
104
+ return a.getTime() === b.getTime();
105
+ },
106
+
107
+ extend = function(to, from, overwrite)
108
+ {
109
+ var prop, hasProp;
110
+ for (prop in from) {
111
+ hasProp = to[prop] !== undefined;
112
+ if (hasProp && typeof from[prop] === 'object' && from[prop].nodeName === undefined) {
113
+ if (isDate(from[prop])) {
114
+ if (overwrite) {
115
+ to[prop] = new Date(from[prop].getTime());
116
+ }
117
+ }
118
+ else if (isArray(from[prop])) {
119
+ if (overwrite) {
120
+ to[prop] = from[prop].slice(0);
121
+ }
122
+ } else {
123
+ to[prop] = extend({}, from[prop], overwrite);
124
+ }
125
+ } else if (overwrite || !hasProp) {
126
+ to[prop] = from[prop];
127
+ }
128
+ }
129
+ return to;
130
+ },
131
+
132
+
133
+ /**
134
+ * defaults and localisation
135
+ */
136
+ defaults = {
137
+
138
+ // bind the picker to a form field
139
+ field: null,
140
+
141
+ // automatically show/hide the picker on `field` focus (default `true` if `field` is set)
142
+ bound: undefined,
143
+
144
+ // the default output format for `.toString()` and `field` value
145
+ format: 'YYYY-MM-DD',
146
+
147
+ // the initial date to view when first opened
148
+ defaultDate: null,
149
+
150
+ // make the `defaultDate` the initial selected value
151
+ setDefaultDate: false,
152
+
153
+ // first day of week (0: Sunday, 1: Monday etc)
154
+ firstDay: 0,
155
+
156
+ // the minimum/earliest date that can be selected
157
+ minDate: null,
158
+ // the maximum/latest date that can be selected
159
+ maxDate: null,
160
+
161
+ // number of years either side, or array of upper/lower range
162
+ yearRange: 10,
163
+
164
+ // used internally (don't config outside)
165
+ minYear: 0,
166
+ maxYear: 9999,
167
+ minMonth: undefined,
168
+ maxMonth: undefined,
169
+
170
+ isRTL: false,
171
+
172
+ // how many months are visible (not implemented yet)
173
+ numberOfMonths: 1,
174
+
175
+ // internationalization
176
+ i18n: {
177
+ previousMonth : 'Previous Month',
178
+ nextMonth : 'Next Month',
179
+ months : ['January','February','March','April','May','June','July','August','September','October','November','December'],
180
+ //monthsShort : ['Jan_Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
181
+ weekdays : ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
182
+ weekdaysShort : ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
183
+ },
184
+
185
+ // callback function
186
+ onSelect: null,
187
+ onOpen: null,
188
+ onClose: null,
189
+ onDraw: null
190
+ },
191
+
192
+
193
+ /**
194
+ * templating functions to abstract HTML rendering
195
+ */
196
+ renderDayName = function(opts, day, abbr)
197
+ {
198
+ day += opts.firstDay;
199
+ while (day >= 7) {
200
+ day -= 7;
201
+ }
202
+ return abbr ? opts.i18n.weekdaysShort[day] : opts.i18n.weekdays[day];
203
+ },
204
+
205
+ renderDay = function(i, isSelected, isToday, isDisabled, isEmpty)
206
+ {
207
+ if (isEmpty) {
208
+ return '<td class="is-empty"></td>';
209
+ }
210
+ var arr = [];
211
+ if (isDisabled) {
212
+ arr.push('is-disabled');
213
+ }
214
+ if (isToday) {
215
+ arr.push('is-today');
216
+ }
217
+ if (isSelected) {
218
+ arr.push('is-selected');
219
+ }
220
+ return '<td data-day="' + i + '" class="' + arr.join(' ') + '"><button class="pika-button" type="button">' + i + '</button>' + '</td>';
221
+ },
222
+
223
+ renderRow = function(days, isRTL)
224
+ {
225
+ return '<tr>' + (isRTL ? days.reverse() : days).join('') + '</tr>';
226
+ },
227
+
228
+ renderBody = function(rows)
229
+ {
230
+ return '<tbody>' + rows.join('') + '</tbody>';
231
+ },
232
+
233
+ renderHead = function(opts)
234
+ {
235
+ var i, arr = [];
236
+ for (i = 0; i < 7; i++) {
237
+ arr.push('<th scope="col"><abbr title="' + renderDayName(opts, i) + '">' + renderDayName(opts, i, true) + '</abbr></th>');
238
+ }
239
+ return '<thead>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</thead>';
240
+ },
241
+
242
+ renderTitle = function(instance)
243
+ {
244
+ var i, j, arr,
245
+ opts = instance._o,
246
+ month = instance._m,
247
+ year = instance._y,
248
+ isMinYear = year === opts.minYear,
249
+ isMaxYear = year === opts.maxYear,
250
+ html = '<div class="pika-title">',
251
+ prev = true,
252
+ next = true;
253
+
254
+ for (arr = [], i = 0; i < 12; i++) {
255
+ arr.push('<option value="' + i + '"' +
256
+ (i === month ? ' selected': '') +
257
+ ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth) ? 'disabled' : '') + '>' +
258
+ opts.i18n.months[i] + '</option>');
259
+ }
260
+ html += '<div class="pika-label">' + opts.i18n.months[month] + '<select class="pika-select pika-select-month">' + arr.join('') + '</select></div>';
261
+
262
+ if (isArray(opts.yearRange)) {
263
+ i = opts.yearRange[0];
264
+ j = opts.yearRange[1] + 1;
265
+ } else {
266
+ i = year - opts.yearRange;
267
+ j = 1 + year + opts.yearRange;
268
+ }
269
+
270
+ for (arr = []; i < j && i <= opts.maxYear; i++) {
271
+ if (i >= opts.minYear) {
272
+ arr.push('<option value="' + i + '"' + (i === year ? ' selected': '') + '>' + (i) + '</option>');
273
+ }
274
+ }
275
+ html += '<div class="pika-label">' + year + '<select class="pika-select pika-select-year">' + arr.join('') + '</select></div>';
276
+
277
+ if (isMinYear && (month === 0 || opts.minMonth >= month)) {
278
+ prev = false;
279
+ }
280
+
281
+ if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
282
+ next = false;
283
+ }
284
+
285
+ html += '<button class="pika-prev' + (prev ? '' : ' is-disabled') + '" type="button">' + opts.i18n.previousMonth + '</button>';
286
+ html += '<button class="pika-next' + (next ? '' : ' is-disabled') + '" type="button">' + opts.i18n.nextMonth + '</button>';
287
+
288
+ return html += '</div>';
289
+ },
290
+
291
+ renderTable = function(opts, data)
292
+ {
293
+ return '<table cellpadding="0" cellspacing="0" class="pika-table">' + renderHead(opts) + renderBody(data) + '</table>';
294
+ };
295
+
296
+
297
+ /**
298
+ * Pikaday constructor
299
+ */
300
+ window.Pikaday = function(options)
301
+ {
302
+ var self = this,
303
+ opts = self.config(options);
304
+
305
+ self._onMouseDown = function(e)
306
+ {
307
+ if (!self._v) {
308
+ return;
309
+ }
310
+ e = e || window.event;
311
+ var target = e.target || e.srcElement;
312
+ if (!target) {
313
+ return;
314
+ }
315
+
316
+ if (!hasClass(target, 'is-disabled')) {
317
+ if (hasClass(target, 'pika-button') && !hasClass(target, 'is-empty')) {
318
+ self.setDate(new Date(self._y, self._m, parseInt(target.innerHTML, 10)));
319
+ if (opts.bound) {
320
+ sto(function() {
321
+ self.hide();
322
+ }, 100);
323
+ }
324
+ return;
325
+ }
326
+ else if (hasClass(target, 'pika-prev')) {
327
+ self.prevMonth();
328
+ }
329
+ else if (hasClass(target, 'pika-next')) {
330
+ self.nextMonth();
331
+ }
332
+ }
333
+ if (!hasClass(target, 'pika-select')) {
334
+ if (e.preventDefault) {
335
+ e.preventDefault();
336
+ } else {
337
+ return e.returnValue = false;
338
+ }
339
+ } else {
340
+ self._c = true;
341
+ }
342
+ };
343
+
344
+ self._onChange = function(e)
345
+ {
346
+ e = e || window.event;
347
+ var target = e.target || e.srcElement;
348
+ if (!target) {
349
+ return;
350
+ }
351
+ if (hasClass(target, 'pika-select-month')) {
352
+ self.gotoMonth(target.value);
353
+ }
354
+ else if (hasClass(target, 'pika-select-year')) {
355
+ self.gotoYear(target.value);
356
+ }
357
+ };
358
+
359
+ self._onInputChange = function(e)
360
+ {
361
+ var date;
362
+
363
+ if (e.firedBy === self) {
364
+ return;
365
+ }
366
+ if (hasMoment) {
367
+ date = window.moment(opts.field.value, opts.format);
368
+ date = date ? date.toDate() : null;
369
+ }
370
+ else {
371
+ date = new Date(Date.parse(opts.field.value));
372
+ }
373
+ self.setDate(isDate(date) ? date : null);
374
+ if (!self._v) {
375
+ self.show();
376
+ }
377
+ };
378
+
379
+ self._onInputFocus = function(e)
380
+ {
381
+ self.show();
382
+ };
383
+
384
+ self._onInputClick = function(e)
385
+ {
386
+ self.show();
387
+ };
388
+
389
+ self._onInputBlur = function(e)
390
+ {
391
+ if (!self._c) {
392
+ self._b = sto(function() {
393
+ self.hide();
394
+ }, 50);
395
+ }
396
+ self._c = false;
397
+ };
398
+
399
+ self._onClick = function(e)
400
+ {
401
+ e = e || window.event;
402
+ var target = e.target || e.srcElement,
403
+ pEl = target;
404
+ if (!target) {
405
+ return;
406
+ }
407
+ if (!hasEventListeners && hasClass(target, 'pika-select')) {
408
+ if (!target.onchange) {
409
+ target.setAttribute('onchange', 'return;');
410
+ addEvent(target, 'change', self._onChange);
411
+ }
412
+ }
413
+ do {
414
+ if (hasClass(pEl, 'pika-single')) {
415
+ return;
416
+ }
417
+ }
418
+ while ((pEl = pEl.parentNode));
419
+ if (self._v && target !== opts.field) {
420
+ self.hide();
421
+ }
422
+ };
423
+
424
+ self.el = document.createElement('div');
425
+ self.el.className = 'pika-single' + (opts.isRTL ? ' is-rtl' : '');
426
+
427
+ addEvent(self.el, 'mousedown', self._onMouseDown, true);
428
+ addEvent(self.el, 'change', self._onChange);
429
+
430
+ if (opts.field) {
431
+ if (opts.bound) {
432
+ document.body.appendChild(self.el);
433
+ } else {
434
+ opts.field.parentNode.insertBefore(self.el, opts.field.nextSibling);
435
+ }
436
+ addEvent(opts.field, 'change', self._onInputChange);
437
+
438
+ if (!opts.defaultDate) {
439
+ if (hasMoment && opts.field.value) {
440
+ opts.defaultDate = window.moment(opts.field.value, opts.format).toDate();
441
+ } else {
442
+ opts.defaultDate = new Date(Date.parse(opts.field.value));
443
+ }
444
+ opts.setDefaultDate = true;
445
+ }
446
+ }
447
+
448
+ var defDate = opts.defaultDate;
449
+
450
+ if (isDate(defDate)) {
451
+ if (opts.setDefaultDate) {
452
+ self.setDate(defDate, true);
453
+ } else {
454
+ self.gotoDate(defDate);
455
+ }
456
+ } else {
457
+ self.gotoDate(new Date());
458
+ }
459
+
460
+ if (opts.bound) {
461
+ this.hide();
462
+ self.el.className += ' is-bound';
463
+ addEvent(opts.field, 'click', self._onInputClick);
464
+ addEvent(opts.field, 'focus', self._onInputFocus);
465
+ addEvent(opts.field, 'blur', self._onInputBlur);
466
+ } else {
467
+ this.show();
468
+ }
469
+
470
+ };
471
+
472
+
473
+ /**
474
+ * public Pikaday API
475
+ */
476
+ window.Pikaday.prototype = {
477
+
478
+
479
+ /**
480
+ * configure functionality
481
+ */
482
+ config: function(options)
483
+ {
484
+ if (!this._o) {
485
+ this._o = extend({}, defaults, true);
486
+ }
487
+
488
+ var opts = extend(this._o, options, true);
489
+
490
+ opts.isRTL = !!opts.isRTL;
491
+
492
+ opts.field = (opts.field && opts.field.nodeName) ? opts.field : null;
493
+
494
+ opts.bound = !!(opts.bound !== undefined ? opts.field && opts.bound : opts.field);
495
+
496
+ var nom = parseInt(opts.numberOfMonths, 10) || 1;
497
+ opts.numberOfMonths = nom > 4 ? 4 : nom;
498
+
499
+ if (!isDate(opts.minDate)) {
500
+ opts.minDate = false;
501
+ }
502
+ if (!isDate(opts.maxDate)) {
503
+ opts.maxDate = false;
504
+ }
505
+ if ((opts.minDate && opts.maxDate) && opts.maxDate < opts.minDate) {
506
+ opts.maxDate = opts.minDate = false;
507
+ }
508
+ if (opts.minDate) {
509
+ setToStartOfDay(opts.minDate);
510
+ opts.minYear = opts.minDate.getFullYear();
511
+ opts.minMonth = opts.minDate.getMonth();
512
+ }
513
+ if (opts.maxDate) {
514
+ setToStartOfDay(opts.maxDate);
515
+ opts.maxYear = opts.maxDate.getFullYear();
516
+ opts.maxMonth = opts.maxDate.getMonth();
517
+ }
518
+
519
+ if (isArray(opts.yearRange)) {
520
+ var fallback = new Date().getFullYear() - 10;
521
+ opts.yearRange[0] = parseInt(opts.yearRange[0], 10) || fallback;
522
+ opts.yearRange[1] = parseInt(opts.yearRange[1], 10) || fallback;
523
+ } else {
524
+ opts.yearRange = Math.abs(parseInt(opts.yearRange, 10)) || defaults.yearRange;
525
+ if (opts.yearRange > 100) {
526
+ opts.yearRange = 100;
527
+ }
528
+ }
529
+
530
+ return opts;
531
+ },
532
+
533
+ /**
534
+ * return a formatted string of the current selection (using Moment.js if available)
535
+ */
536
+ toString: function(format)
537
+ {
538
+ return !isDate(this._d) ? '' : hasMoment ? window.moment(this._d).format(format || this._o.format) : this._d.toDateString();
539
+ },
540
+
541
+ /**
542
+ * return a Moment.js object of the current selection (if available)
543
+ */
544
+ getMoment: function()
545
+ {
546
+ return hasMoment ? window.moment(this._d) : null;
547
+ },
548
+
549
+ /**
550
+ * set the current selection from a Moment.js object (if available)
551
+ */
552
+ setMoment: function(date)
553
+ {
554
+ if (hasMoment && window.moment.isMoment(date)) {
555
+ this.setDate(date.toDate());
556
+ }
557
+ },
558
+
559
+ /**
560
+ * return a Date object of the current selection
561
+ */
562
+ getDate: function()
563
+ {
564
+ return isDate(this._d) ? new Date(this._d.getTime()) : null;
565
+ },
566
+
567
+ /**
568
+ * set the current selection
569
+ */
570
+ setDate: function(date, preventOnSelect)
571
+ {
572
+ if (!date) {
573
+ this._d = null;
574
+ return this.draw();
575
+ }
576
+ if (typeof date === 'string') {
577
+ date = new Date(Date.parse(date));
578
+ }
579
+ if (!isDate(date)) {
580
+ return;
581
+ }
582
+
583
+ var min = this._o.minDate,
584
+ max = this._o.maxDate;
585
+
586
+ if (isDate(min) && date < min) {
587
+ date = min;
588
+ } else if (isDate(max) && date > max) {
589
+ date = max;
590
+ }
591
+
592
+ this._d = new Date(date.getTime());
593
+ setToStartOfDay(this._d);
594
+ this.gotoDate(this._d);
595
+
596
+ if (this._o.field) {
597
+ this._o.field.value = this.toString();
598
+ fireEvent(this._o.field, "change", { firedBy: this });
599
+ }
600
+ if (!preventOnSelect && typeof this._o.onSelect === 'function') {
601
+ this._o.onSelect.call(this, this.getDate());
602
+ }
603
+ },
604
+
605
+ /**
606
+ * change view to a specific date
607
+ */
608
+ gotoDate: function(date)
609
+ {
610
+ if (!isDate(date)) {
611
+ return;
612
+ }
613
+ this._y = date.getFullYear();
614
+ this._m = date.getMonth();
615
+ this.draw();
616
+ },
617
+
618
+ gotoToday: function()
619
+ {
620
+ this.gotoDate(new Date());
621
+ },
622
+
623
+ /**
624
+ * change view to a specific month (zero-index, e.g. 0: January)
625
+ */
626
+ gotoMonth: function(month)
627
+ {
628
+ if (!isNaN( (month = parseInt(month, 10)) )) {
629
+ this._m = month < 0 ? 0 : month > 11 ? 11 : month;
630
+ this.draw();
631
+ }
632
+ },
633
+
634
+ nextMonth: function()
635
+ {
636
+ if (++this._m > 11) {
637
+ this._m = 0;
638
+ this._y++;
639
+ }
640
+ this.draw();
641
+ },
642
+
643
+ prevMonth: function()
644
+ {
645
+ if (--this._m < 0) {
646
+ this._m = 11;
647
+ this._y--;
648
+ }
649
+ this.draw();
650
+ },
651
+
652
+ /**
653
+ * change view to a specific full year (e.g. "2012")
654
+ */
655
+ gotoYear: function(year)
656
+ {
657
+ if (!isNaN(year)) {
658
+ this._y = parseInt(year, 10);
659
+ this.draw();
660
+ }
661
+ },
662
+
663
+ /**
664
+ * refresh the HTML
665
+ */
666
+ draw: function(force)
667
+ {
668
+ if (!this._v && !force) {
669
+ return;
670
+ }
671
+ var opts = this._o,
672
+ minYear = opts.minYear,
673
+ maxYear = opts.maxYear,
674
+ minMonth = opts.minMonth,
675
+ maxMonth = opts.maxMonth;
676
+
677
+ if (this._y <= minYear) {
678
+ this._y = minYear;
679
+ if (!isNaN(minMonth) && this._m < minMonth) {
680
+ this._m = minMonth;
681
+ }
682
+ }
683
+ if (this._y >= maxYear) {
684
+ this._y = maxYear;
685
+ if (!isNaN(maxMonth) && this._m > maxMonth) {
686
+ this._m = maxMonth;
687
+ }
688
+ }
689
+
690
+ this.el.innerHTML = renderTitle(this) + this.render(this._y, this._m);
691
+
692
+ if (opts.bound) {
693
+ var pEl = opts.field,
694
+ left = pEl.offsetLeft,
695
+ top = pEl.offsetTop + pEl.offsetHeight;
696
+ while((pEl = pEl.offsetParent)) {
697
+ left += pEl.offsetLeft;
698
+ top += pEl.offsetTop;
699
+ }
700
+ this.el.style.cssText = 'position:absolute;left:' + left + 'px;top:' + top + 'px;';
701
+ sto(function() {
702
+ opts.field.focus();
703
+ }, 1);
704
+ }
705
+
706
+ if (typeof this._o.onDraw === 'function') {
707
+ var self = this;
708
+ sto(function() {
709
+ self._o.onDraw.call(self);
710
+ }, 0);
711
+ }
712
+ },
713
+
714
+ /**
715
+ * render HTML for a particular month
716
+ */
717
+ render: function(year, month)
718
+ {
719
+ var opts = this._o,
720
+ now = new Date(),
721
+ days = getDaysInMonth(year, month),
722
+ before = new Date(year, month, 1).getDay(),
723
+ data = [],
724
+ row = [];
725
+ setToStartOfDay(now);
726
+ if (opts.firstDay > 0) {
727
+ before -= opts.firstDay;
728
+ if (before < 0) {
729
+ before += 7;
730
+ }
731
+ }
732
+ var cells = days + before,
733
+ after = cells;
734
+ while(after > 7) {
735
+ after -= 7;
736
+ }
737
+ cells += 7 - after;
738
+ for (var i = 0, r = 0; i < cells; i++)
739
+ {
740
+ var day = new Date(year, month, 1 + (i - before)),
741
+ isDisabled = (opts.minDate && day < opts.minDate) || (opts.maxDate && day > opts.maxDate),
742
+ isSelected = isDate(this._d) ? compareDates(day, this._d) : false,
743
+ isToday = compareDates(day, now),
744
+ isEmpty = i < before || i >= (days + before);
745
+
746
+ row.push(renderDay(1 + (i - before), isSelected, isToday, isDisabled, isEmpty));
747
+
748
+ if (++r === 7) {
749
+ data.push(renderRow(row, opts.isRTL));
750
+ row = [];
751
+ r = 0;
752
+ }
753
+ }
754
+ return renderTable(opts, data);
755
+ },
756
+
757
+ isVisible: function()
758
+ {
759
+ return this._v;
760
+ },
761
+
762
+ show: function()
763
+ {
764
+ if (!this._v) {
765
+ if (this._o.bound) {
766
+ addEvent(document, 'click', this._onClick);
767
+ }
768
+ removeClass(this.el, 'is-hidden');
769
+ this._v = true;
770
+ this.draw();
771
+ if (typeof this._o.onOpen === 'function') {
772
+ this._o.onOpen.call(this);
773
+ }
774
+ }
775
+ },
776
+
777
+ hide: function()
778
+ {
779
+ var v = this._v;
780
+ if (v !== false) {
781
+ if (this._o.bound) {
782
+ removeEvent(document, 'click', this._onClick);
783
+ }
784
+ this.el.style.cssText = '';
785
+ addClass(this.el, 'is-hidden');
786
+ this._v = false;
787
+ if (v !== undefined && typeof this._o.onClose === 'function') {
788
+ this._o.onClose.call(this);
789
+ }
790
+ }
791
+ },
792
+
793
+ /**
794
+ * GAME OVER
795
+ */
796
+ destroy: function()
797
+ {
798
+ this.hide();
799
+ removeEvent(this.el, 'mousedown', this._onMouseDown, true);
800
+ removeEvent(this.el, 'change', this._onChange);
801
+ if (this._o.field) {
802
+ removeEvent(this._o.field, 'change', this._onInputChange);
803
+ if (this._o.bound) {
804
+ removeEvent(this._o.field, 'click', this._onInputClick);
805
+ removeEvent(this._o.field, 'focus', this._onInputFocus);
806
+ removeEvent(this._o.field, 'blur', this._onInputBlur);
807
+ }
808
+ }
809
+ if (this.el.parentNode) {
810
+ this.el.parentNode.removeChild(this.el);
811
+ }
812
+ }
813
+
814
+ };
815
+
816
+ })(window, window.document);