statements 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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +22 -0
  4. data/bin/statements +8 -0
  5. data/lib/html/css/main.css +115 -0
  6. data/lib/html/css/main.css.map +7 -0
  7. data/lib/html/css/main.scss +173 -0
  8. data/lib/html/index.html +94 -0
  9. data/lib/html/js/main.coffee +146 -0
  10. data/lib/html/js/main.js +174 -0
  11. data/lib/html/js/main.js.map +10 -0
  12. data/lib/html/vendor/bootstrap/css/bootstrap-theme.css +469 -0
  13. data/lib/html/vendor/bootstrap/css/bootstrap.css +6331 -0
  14. data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  15. data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.svg +229 -0
  16. data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  17. data/lib/html/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  18. data/lib/html/vendor/bootstrap/js/bootstrap.js +2320 -0
  19. data/lib/html/vendor/bootstrap-datepicker.js +1681 -0
  20. data/lib/html/vendor/datepicker3.css +786 -0
  21. data/lib/html/vendor/jquery-2.1.3.js +9205 -0
  22. data/lib/html/vendor/underscore.js +1416 -0
  23. data/lib/statements/cli.rb +17 -0
  24. data/lib/statements/database.rb +22 -0
  25. data/lib/statements/migrations/00_alpha.rb +43 -0
  26. data/lib/statements/models/account.rb +9 -0
  27. data/lib/statements/models/document.rb +29 -0
  28. data/lib/statements/models/transaction.rb +43 -0
  29. data/lib/statements/pdf_reader.rb +34 -0
  30. data/lib/statements/reader/common/st_george.rb +31 -0
  31. data/lib/statements/reader/st_george_credit_card.rb +53 -0
  32. data/lib/statements/reader/st_george_savings.rb +95 -0
  33. data/lib/statements/reader.rb +76 -0
  34. data/lib/statements/search.rb +48 -0
  35. data/lib/statements/server.rb +69 -0
  36. data/lib/statements/version.rb +3 -0
  37. data/lib/statements/views/footer.erb +6 -0
  38. data/lib/statements/views/row.erb +16 -0
  39. data/lib/statements/views/search.erb +25 -0
  40. data/lib/statements.rb +17 -0
  41. metadata +141 -0
@@ -0,0 +1,1681 @@
1
+ /* =========================================================
2
+ * bootstrap-datepicker.js
3
+ * Repo: https://github.com/eternicode/bootstrap-datepicker/
4
+ * Demo: http://eternicode.github.io/bootstrap-datepicker/
5
+ * Docs: http://bootstrap-datepicker.readthedocs.org/
6
+ * Forked from http://www.eyecon.ro/bootstrap-datepicker
7
+ * =========================================================
8
+ * Started by Stefan Petre; improvements by Andrew Rowls + contributors
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * http://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ * ========================================================= */
22
+
23
+ (function($, undefined){
24
+
25
+ var $window = $(window);
26
+
27
+ function UTCDate(){
28
+ return new Date(Date.UTC.apply(Date, arguments));
29
+ }
30
+ function UTCToday(){
31
+ var today = new Date();
32
+ return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
33
+ }
34
+ function alias(method){
35
+ return function(){
36
+ return this[method].apply(this, arguments);
37
+ };
38
+ }
39
+
40
+ var DateArray = (function(){
41
+ var extras = {
42
+ get: function(i){
43
+ return this.slice(i)[0];
44
+ },
45
+ contains: function(d){
46
+ // Array.indexOf is not cross-browser;
47
+ // $.inArray doesn't work with Dates
48
+ var val = d && d.valueOf();
49
+ for (var i=0, l=this.length; i < l; i++)
50
+ if (this[i].valueOf() === val)
51
+ return i;
52
+ return -1;
53
+ },
54
+ remove: function(i){
55
+ this.splice(i,1);
56
+ },
57
+ replace: function(new_array){
58
+ if (!new_array)
59
+ return;
60
+ if (!$.isArray(new_array))
61
+ new_array = [new_array];
62
+ this.clear();
63
+ this.push.apply(this, new_array);
64
+ },
65
+ clear: function(){
66
+ this.length = 0;
67
+ },
68
+ copy: function(){
69
+ var a = new DateArray();
70
+ a.replace(this);
71
+ return a;
72
+ }
73
+ };
74
+
75
+ return function(){
76
+ var a = [];
77
+ a.push.apply(a, arguments);
78
+ $.extend(a, extras);
79
+ return a;
80
+ };
81
+ })();
82
+
83
+
84
+ // Picker object
85
+
86
+ var Datepicker = function(element, options){
87
+ this.dates = new DateArray();
88
+ this.viewDate = UTCToday();
89
+ this.focusDate = null;
90
+
91
+ this._process_options(options);
92
+
93
+ this.element = $(element);
94
+ this.isInline = false;
95
+ this.isInput = this.element.is('input');
96
+ this.component = this.element.is('.date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
97
+ this.hasInput = this.component && this.element.find('input').length;
98
+ if (this.component && this.component.length === 0)
99
+ this.component = false;
100
+
101
+ this.picker = $(DPGlobal.template);
102
+ this._buildEvents();
103
+ this._attachEvents();
104
+
105
+ if (this.isInline){
106
+ this.picker.addClass('datepicker-inline').appendTo(this.element);
107
+ }
108
+ else {
109
+ this.picker.addClass('datepicker-dropdown dropdown-menu');
110
+ }
111
+
112
+ if (this.o.rtl){
113
+ this.picker.addClass('datepicker-rtl');
114
+ }
115
+
116
+ this.viewMode = this.o.startView;
117
+
118
+ if (this.o.calendarWeeks)
119
+ this.picker.find('tfoot th.today, tfoot th.clear')
120
+ .attr('colspan', function(i, val){
121
+ return parseInt(val) + 1;
122
+ });
123
+
124
+ this._allow_update = false;
125
+
126
+ this.setStartDate(this._o.startDate);
127
+ this.setEndDate(this._o.endDate);
128
+ this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
129
+
130
+ this.fillDow();
131
+ this.fillMonths();
132
+
133
+ this._allow_update = true;
134
+
135
+ this.update();
136
+ this.showMode();
137
+
138
+ if (this.isInline){
139
+ this.show();
140
+ }
141
+ };
142
+
143
+ Datepicker.prototype = {
144
+ constructor: Datepicker,
145
+
146
+ _process_options: function(opts){
147
+ // Store raw options for reference
148
+ this._o = $.extend({}, this._o, opts);
149
+ // Processed options
150
+ var o = this.o = $.extend({}, this._o);
151
+
152
+ // Check if "de-DE" style date is available, if not language should
153
+ // fallback to 2 letter code eg "de"
154
+ var lang = o.language;
155
+ if (!dates[lang]){
156
+ lang = lang.split('-')[0];
157
+ if (!dates[lang])
158
+ lang = defaults.language;
159
+ }
160
+ o.language = lang;
161
+
162
+ switch (o.startView){
163
+ case 2:
164
+ case 'decade':
165
+ o.startView = 2;
166
+ break;
167
+ case 1:
168
+ case 'year':
169
+ o.startView = 1;
170
+ break;
171
+ default:
172
+ o.startView = 0;
173
+ }
174
+
175
+ switch (o.minViewMode){
176
+ case 1:
177
+ case 'months':
178
+ o.minViewMode = 1;
179
+ break;
180
+ case 2:
181
+ case 'years':
182
+ o.minViewMode = 2;
183
+ break;
184
+ default:
185
+ o.minViewMode = 0;
186
+ }
187
+
188
+ o.startView = Math.max(o.startView, o.minViewMode);
189
+
190
+ // true, false, or Number > 0
191
+ if (o.multidate !== true){
192
+ o.multidate = Number(o.multidate) || false;
193
+ if (o.multidate !== false)
194
+ o.multidate = Math.max(0, o.multidate);
195
+ else
196
+ o.multidate = 1;
197
+ }
198
+ o.multidateSeparator = String(o.multidateSeparator);
199
+
200
+ o.weekStart %= 7;
201
+ o.weekEnd = ((o.weekStart + 6) % 7);
202
+
203
+ var format = DPGlobal.parseFormat(o.format);
204
+ if (o.startDate !== -Infinity){
205
+ if (!!o.startDate){
206
+ if (o.startDate instanceof Date)
207
+ o.startDate = this._local_to_utc(this._zero_time(o.startDate));
208
+ else
209
+ o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
210
+ }
211
+ else {
212
+ o.startDate = -Infinity;
213
+ }
214
+ }
215
+ if (o.endDate !== Infinity){
216
+ if (!!o.endDate){
217
+ if (o.endDate instanceof Date)
218
+ o.endDate = this._local_to_utc(this._zero_time(o.endDate));
219
+ else
220
+ o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
221
+ }
222
+ else {
223
+ o.endDate = Infinity;
224
+ }
225
+ }
226
+
227
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
228
+ if (!$.isArray(o.daysOfWeekDisabled))
229
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
230
+ o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){
231
+ return parseInt(d, 10);
232
+ });
233
+
234
+ var plc = String(o.orientation).toLowerCase().split(/\s+/g),
235
+ _plc = o.orientation.toLowerCase();
236
+ plc = $.grep(plc, function(word){
237
+ return (/^auto|left|right|top|bottom$/).test(word);
238
+ });
239
+ o.orientation = {x: 'auto', y: 'auto'};
240
+ if (!_plc || _plc === 'auto')
241
+ ; // no action
242
+ else if (plc.length === 1){
243
+ switch (plc[0]){
244
+ case 'top':
245
+ case 'bottom':
246
+ o.orientation.y = plc[0];
247
+ break;
248
+ case 'left':
249
+ case 'right':
250
+ o.orientation.x = plc[0];
251
+ break;
252
+ }
253
+ }
254
+ else {
255
+ _plc = $.grep(plc, function(word){
256
+ return (/^left|right$/).test(word);
257
+ });
258
+ o.orientation.x = _plc[0] || 'auto';
259
+
260
+ _plc = $.grep(plc, function(word){
261
+ return (/^top|bottom$/).test(word);
262
+ });
263
+ o.orientation.y = _plc[0] || 'auto';
264
+ }
265
+ },
266
+ _events: [],
267
+ _secondaryEvents: [],
268
+ _applyEvents: function(evs){
269
+ for (var i=0, el, ch, ev; i < evs.length; i++){
270
+ el = evs[i][0];
271
+ if (evs[i].length === 2){
272
+ ch = undefined;
273
+ ev = evs[i][1];
274
+ }
275
+ else if (evs[i].length === 3){
276
+ ch = evs[i][1];
277
+ ev = evs[i][2];
278
+ }
279
+ el.on(ev, ch);
280
+ }
281
+ },
282
+ _unapplyEvents: function(evs){
283
+ for (var i=0, el, ev, ch; i < evs.length; i++){
284
+ el = evs[i][0];
285
+ if (evs[i].length === 2){
286
+ ch = undefined;
287
+ ev = evs[i][1];
288
+ }
289
+ else if (evs[i].length === 3){
290
+ ch = evs[i][1];
291
+ ev = evs[i][2];
292
+ }
293
+ el.off(ev, ch);
294
+ }
295
+ },
296
+ _buildEvents: function(){
297
+ if (this.isInput){ // single input
298
+ this._events = [
299
+ [this.element, {
300
+ focus: $.proxy(this.show, this),
301
+ keyup: $.proxy(function(e){
302
+ if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
303
+ this.update();
304
+ }, this),
305
+ keydown: $.proxy(this.keydown, this)
306
+ }]
307
+ ];
308
+ }
309
+ else if (this.component && this.hasInput){ // component: input + button
310
+ this._events = [
311
+ // For components that are not readonly, allow keyboard nav
312
+ [this.element.find('input'), {
313
+ focus: $.proxy(this.show, this),
314
+ keyup: $.proxy(function(e){
315
+ if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
316
+ this.update();
317
+ }, this),
318
+ keydown: $.proxy(this.keydown, this)
319
+ }],
320
+ [this.component, {
321
+ click: $.proxy(this.show, this)
322
+ }]
323
+ ];
324
+ }
325
+ else if (this.element.is('div')){ // inline datepicker
326
+ this.isInline = true;
327
+ }
328
+ else {
329
+ this._events = [
330
+ [this.element, {
331
+ click: $.proxy(this.show, this)
332
+ }]
333
+ ];
334
+ }
335
+ this._events.push(
336
+ // Component: listen for blur on element descendants
337
+ [this.element, '*', {
338
+ blur: $.proxy(function(e){
339
+ this._focused_from = e.target;
340
+ }, this)
341
+ }],
342
+ // Input: listen for blur on element
343
+ [this.element, {
344
+ blur: $.proxy(function(e){
345
+ this._focused_from = e.target;
346
+ }, this)
347
+ }]
348
+ );
349
+
350
+ this._secondaryEvents = [
351
+ [this.picker, {
352
+ click: $.proxy(this.click, this)
353
+ }],
354
+ [$(window), {
355
+ resize: $.proxy(this.place, this)
356
+ }],
357
+ [$(document), {
358
+ 'mousedown touchstart': $.proxy(function(e){
359
+ // Clicked outside the datepicker, hide it
360
+ if (!(
361
+ this.element.is(e.target) ||
362
+ this.element.find(e.target).length ||
363
+ this.picker.is(e.target) ||
364
+ this.picker.find(e.target).length
365
+ )){
366
+ this.hide();
367
+ }
368
+ }, this)
369
+ }]
370
+ ];
371
+ },
372
+ _attachEvents: function(){
373
+ this._detachEvents();
374
+ this._applyEvents(this._events);
375
+ },
376
+ _detachEvents: function(){
377
+ this._unapplyEvents(this._events);
378
+ },
379
+ _attachSecondaryEvents: function(){
380
+ this._detachSecondaryEvents();
381
+ this._applyEvents(this._secondaryEvents);
382
+ },
383
+ _detachSecondaryEvents: function(){
384
+ this._unapplyEvents(this._secondaryEvents);
385
+ },
386
+ _trigger: function(event, altdate){
387
+ var date = altdate || this.dates.get(-1),
388
+ local_date = this._utc_to_local(date);
389
+
390
+ this.element.trigger({
391
+ type: event,
392
+ date: local_date,
393
+ dates: $.map(this.dates, this._utc_to_local),
394
+ format: $.proxy(function(ix, format){
395
+ if (arguments.length === 0){
396
+ ix = this.dates.length - 1;
397
+ format = this.o.format;
398
+ }
399
+ else if (typeof ix === 'string'){
400
+ format = ix;
401
+ ix = this.dates.length - 1;
402
+ }
403
+ format = format || this.o.format;
404
+ var date = this.dates.get(ix);
405
+ return DPGlobal.formatDate(date, format, this.o.language);
406
+ }, this)
407
+ });
408
+ },
409
+
410
+ show: function(){
411
+ if (!this.isInline)
412
+ this.picker.appendTo('body');
413
+ this.picker.show();
414
+ this.place();
415
+ this._attachSecondaryEvents();
416
+ this._trigger('show');
417
+ },
418
+
419
+ hide: function(){
420
+ if (this.isInline)
421
+ return;
422
+ if (!this.picker.is(':visible'))
423
+ return;
424
+ this.focusDate = null;
425
+ this.picker.hide().detach();
426
+ this._detachSecondaryEvents();
427
+ this.viewMode = this.o.startView;
428
+ this.showMode();
429
+
430
+ if (
431
+ this.o.forceParse &&
432
+ (
433
+ this.isInput && this.element.val() ||
434
+ this.hasInput && this.element.find('input').val()
435
+ )
436
+ )
437
+ this.setValue();
438
+ this._trigger('hide');
439
+ },
440
+
441
+ remove: function(){
442
+ this.hide();
443
+ this._detachEvents();
444
+ this._detachSecondaryEvents();
445
+ this.picker.remove();
446
+ delete this.element.data().datepicker;
447
+ if (!this.isInput){
448
+ delete this.element.data().date;
449
+ }
450
+ },
451
+
452
+ _utc_to_local: function(utc){
453
+ return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000));
454
+ },
455
+ _local_to_utc: function(local){
456
+ return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000));
457
+ },
458
+ _zero_time: function(local){
459
+ return local && new Date(local.getFullYear(), local.getMonth(), local.getDate());
460
+ },
461
+ _zero_utc_time: function(utc){
462
+ return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate()));
463
+ },
464
+
465
+ getDates: function(){
466
+ return $.map(this.dates, this._utc_to_local);
467
+ },
468
+
469
+ getUTCDates: function(){
470
+ return $.map(this.dates, function(d){
471
+ return new Date(d);
472
+ });
473
+ },
474
+
475
+ getDate: function(){
476
+ return this._utc_to_local(this.getUTCDate());
477
+ },
478
+
479
+ getUTCDate: function(){
480
+ return new Date(this.dates.get(-1));
481
+ },
482
+
483
+ setDates: function(){
484
+ var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
485
+ this.update.apply(this, args);
486
+ this._trigger('changeDate');
487
+ this.setValue();
488
+ },
489
+
490
+ setUTCDates: function(){
491
+ var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
492
+ this.update.apply(this, $.map(args, this._utc_to_local));
493
+ this._trigger('changeDate');
494
+ this.setValue();
495
+ },
496
+
497
+ setDate: alias('setDates'),
498
+ setUTCDate: alias('setUTCDates'),
499
+
500
+ setValue: function(){
501
+ var formatted = this.getFormattedDate();
502
+ if (!this.isInput){
503
+ if (this.component){
504
+ this.element.find('input').val(formatted).change();
505
+ }
506
+ }
507
+ else {
508
+ this.element.val(formatted).change();
509
+ }
510
+ },
511
+
512
+ getFormattedDate: function(format){
513
+ if (format === undefined)
514
+ format = this.o.format;
515
+
516
+ var lang = this.o.language;
517
+ return $.map(this.dates, function(d){
518
+ return DPGlobal.formatDate(d, format, lang);
519
+ }).join(this.o.multidateSeparator);
520
+ },
521
+
522
+ setStartDate: function(startDate){
523
+ this._process_options({startDate: startDate});
524
+ this.update();
525
+ this.updateNavArrows();
526
+ },
527
+
528
+ setEndDate: function(endDate){
529
+ this._process_options({endDate: endDate});
530
+ this.update();
531
+ this.updateNavArrows();
532
+ },
533
+
534
+ setDaysOfWeekDisabled: function(daysOfWeekDisabled){
535
+ this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
536
+ this.update();
537
+ this.updateNavArrows();
538
+ },
539
+
540
+ place: function(){
541
+ if (this.isInline)
542
+ return;
543
+ var calendarWidth = this.picker.outerWidth(),
544
+ calendarHeight = this.picker.outerHeight(),
545
+ visualPadding = 10,
546
+ windowWidth = $window.width(),
547
+ windowHeight = $window.height(),
548
+ scrollTop = $window.scrollTop();
549
+
550
+ var parentsZindex = [];
551
+ this.element.parents().each(function() {
552
+ var itemZIndex = $(this).css('z-index');
553
+ if ( itemZIndex !== 'auto' && itemZIndex !== 0 ) parentsZindex.push( parseInt( itemZIndex ) );
554
+ });
555
+ var zIndex = Math.max.apply( Math, parentsZindex ) + 10;
556
+ var offset = this.component ? this.component.parent().offset() : this.element.offset();
557
+ var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
558
+ var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
559
+ var left = offset.left,
560
+ top = offset.top;
561
+
562
+ this.picker.removeClass(
563
+ 'datepicker-orient-top datepicker-orient-bottom '+
564
+ 'datepicker-orient-right datepicker-orient-left'
565
+ );
566
+
567
+ if (this.o.orientation.x !== 'auto'){
568
+ this.picker.addClass('datepicker-orient-' + this.o.orientation.x);
569
+ if (this.o.orientation.x === 'right')
570
+ left -= calendarWidth - width;
571
+ }
572
+ // auto x orientation is best-placement: if it crosses a window
573
+ // edge, fudge it sideways
574
+ else {
575
+ // Default to left
576
+ this.picker.addClass('datepicker-orient-left');
577
+ if (offset.left < 0)
578
+ left -= offset.left - visualPadding;
579
+ else if (offset.left + calendarWidth > windowWidth)
580
+ left = windowWidth - calendarWidth - visualPadding;
581
+ }
582
+
583
+ // auto y orientation is best-situation: top or bottom, no fudging,
584
+ // decision based on which shows more of the calendar
585
+ var yorient = this.o.orientation.y,
586
+ top_overflow, bottom_overflow;
587
+ if (yorient === 'auto'){
588
+ top_overflow = -scrollTop + offset.top - calendarHeight;
589
+ bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
590
+ if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
591
+ yorient = 'top';
592
+ else
593
+ yorient = 'bottom';
594
+ }
595
+ this.picker.addClass('datepicker-orient-' + yorient);
596
+ if (yorient === 'top')
597
+ top += height;
598
+ else
599
+ top -= calendarHeight + parseInt(this.picker.css('padding-top'));
600
+
601
+ this.picker.css({
602
+ top: top,
603
+ left: left,
604
+ zIndex: zIndex
605
+ });
606
+ },
607
+
608
+ _allow_update: true,
609
+ update: function(){
610
+ if (!this._allow_update)
611
+ return;
612
+
613
+ var oldDates = this.dates.copy(),
614
+ dates = [],
615
+ fromArgs = false;
616
+ if (arguments.length){
617
+ $.each(arguments, $.proxy(function(i, date){
618
+ if (date instanceof Date)
619
+ date = this._local_to_utc(date);
620
+ dates.push(date);
621
+ }, this));
622
+ fromArgs = true;
623
+ }
624
+ else {
625
+ dates = this.isInput
626
+ ? this.element.val()
627
+ : this.element.data('date') || this.element.find('input').val();
628
+ if (dates && this.o.multidate)
629
+ dates = dates.split(this.o.multidateSeparator);
630
+ else
631
+ dates = [dates];
632
+ delete this.element.data().date;
633
+ }
634
+
635
+ dates = $.map(dates, $.proxy(function(date){
636
+ return DPGlobal.parseDate(date, this.o.format, this.o.language);
637
+ }, this));
638
+ dates = $.grep(dates, $.proxy(function(date){
639
+ return (
640
+ date < this.o.startDate ||
641
+ date > this.o.endDate ||
642
+ !date
643
+ );
644
+ }, this), true);
645
+ this.dates.replace(dates);
646
+
647
+ if (this.dates.length)
648
+ this.viewDate = new Date(this.dates.get(-1));
649
+ else if (this.viewDate < this.o.startDate)
650
+ this.viewDate = new Date(this.o.startDate);
651
+ else if (this.viewDate > this.o.endDate)
652
+ this.viewDate = new Date(this.o.endDate);
653
+
654
+ if (fromArgs){
655
+ // setting date by clicking
656
+ this.setValue();
657
+ }
658
+ else if (dates.length){
659
+ // setting date by typing
660
+ if (String(oldDates) !== String(this.dates))
661
+ this._trigger('changeDate');
662
+ }
663
+ if (!this.dates.length && oldDates.length)
664
+ this._trigger('clearDate');
665
+
666
+ this.fill();
667
+ },
668
+
669
+ fillDow: function(){
670
+ var dowCnt = this.o.weekStart,
671
+ html = '<tr>';
672
+ if (this.o.calendarWeeks){
673
+ var cell = '<th class="cw">&nbsp;</th>';
674
+ html += cell;
675
+ this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
676
+ }
677
+ while (dowCnt < this.o.weekStart + 7){
678
+ html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
679
+ }
680
+ html += '</tr>';
681
+ this.picker.find('.datepicker-days thead').append(html);
682
+ },
683
+
684
+ fillMonths: function(){
685
+ var html = '',
686
+ i = 0;
687
+ while (i < 12){
688
+ html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
689
+ }
690
+ this.picker.find('.datepicker-months td').html(html);
691
+ },
692
+
693
+ setRange: function(range){
694
+ if (!range || !range.length)
695
+ delete this.range;
696
+ else
697
+ this.range = $.map(range, function(d){
698
+ return d.valueOf();
699
+ });
700
+ this.fill();
701
+ },
702
+
703
+ getClassNames: function(date){
704
+ var cls = [],
705
+ year = this.viewDate.getUTCFullYear(),
706
+ month = this.viewDate.getUTCMonth(),
707
+ today = new Date();
708
+ if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
709
+ cls.push('old');
710
+ }
711
+ else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
712
+ cls.push('new');
713
+ }
714
+ if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
715
+ cls.push('focused');
716
+ // Compare internal UTC date with local today, not UTC today
717
+ if (this.o.todayHighlight &&
718
+ date.getUTCFullYear() === today.getFullYear() &&
719
+ date.getUTCMonth() === today.getMonth() &&
720
+ date.getUTCDate() === today.getDate()){
721
+ cls.push('today');
722
+ }
723
+ if (this.dates.contains(date) !== -1)
724
+ cls.push('active');
725
+ if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
726
+ $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
727
+ cls.push('disabled');
728
+ }
729
+ if (this.range){
730
+ if (date > this.range[0] && date < this.range[this.range.length-1]){
731
+ cls.push('range');
732
+ }
733
+ if ($.inArray(date.valueOf(), this.range) !== -1){
734
+ cls.push('selected');
735
+ }
736
+ }
737
+ return cls;
738
+ },
739
+
740
+ fill: function(){
741
+ var d = new Date(this.viewDate),
742
+ year = d.getUTCFullYear(),
743
+ month = d.getUTCMonth(),
744
+ startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
745
+ startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
746
+ endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
747
+ endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
748
+ todaytxt = dates[this.o.language].today || dates['en'].today || '',
749
+ cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
750
+ tooltip;
751
+ if (isNaN(year) || isNaN(month)) return;
752
+ this.picker.find('.datepicker-days thead th.datepicker-switch')
753
+ .text(dates[this.o.language].months[month]+' '+year);
754
+ this.picker.find('tfoot th.today')
755
+ .text(todaytxt)
756
+ .toggle(this.o.todayBtn !== false);
757
+ this.picker.find('tfoot th.clear')
758
+ .text(cleartxt)
759
+ .toggle(this.o.clearBtn !== false);
760
+ this.updateNavArrows();
761
+ this.fillMonths();
762
+ var prevMonth = UTCDate(year, month-1, 28),
763
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
764
+ prevMonth.setUTCDate(day);
765
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
766
+ var nextMonth = new Date(prevMonth);
767
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
768
+ nextMonth = nextMonth.valueOf();
769
+ var html = [];
770
+ var clsName;
771
+ while (prevMonth.valueOf() < nextMonth){
772
+ if (prevMonth.getUTCDay() === this.o.weekStart){
773
+ html.push('<tr>');
774
+ if (this.o.calendarWeeks){
775
+ // ISO 8601: First week contains first thursday.
776
+ // ISO also states week starts on Monday, but we can be more abstract here.
777
+ var
778
+ // Start of current week: based on weekstart/current date
779
+ ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
780
+ // Thursday of this week
781
+ th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
782
+ // First Thursday of year, year from thursday
783
+ yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
784
+ // Calendar week: ms between thursdays, div ms per day, div 7 days
785
+ calWeek = (th - yth) / 864e5 / 7 + 1;
786
+ html.push('<td class="cw">'+ calWeek +'</td>');
787
+
788
+ }
789
+ }
790
+ clsName = this.getClassNames(prevMonth);
791
+ clsName.push('day');
792
+
793
+ if (this.o.beforeShowDay !== $.noop){
794
+ var before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
795
+ if (before === undefined)
796
+ before = {};
797
+ else if (typeof(before) === 'boolean')
798
+ before = {enabled: before};
799
+ else if (typeof(before) === 'string')
800
+ before = {classes: before};
801
+ if (before.enabled === false)
802
+ clsName.push('disabled');
803
+ if (before.classes)
804
+ clsName = clsName.concat(before.classes.split(/\s+/));
805
+ if (before.tooltip)
806
+ tooltip = before.tooltip;
807
+ }
808
+
809
+ clsName = $.unique(clsName);
810
+ html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
811
+ tooltip = null;
812
+ if (prevMonth.getUTCDay() === this.o.weekEnd){
813
+ html.push('</tr>');
814
+ }
815
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
816
+ }
817
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
818
+
819
+ var months = this.picker.find('.datepicker-months')
820
+ .find('th:eq(1)')
821
+ .text(year)
822
+ .end()
823
+ .find('span').removeClass('active');
824
+
825
+ $.each(this.dates, function(i, d){
826
+ if (d.getUTCFullYear() === year)
827
+ months.eq(d.getUTCMonth()).addClass('active');
828
+ });
829
+
830
+ if (year < startYear || year > endYear){
831
+ months.addClass('disabled');
832
+ }
833
+ if (year === startYear){
834
+ months.slice(0, startMonth).addClass('disabled');
835
+ }
836
+ if (year === endYear){
837
+ months.slice(endMonth+1).addClass('disabled');
838
+ }
839
+
840
+ html = '';
841
+ year = parseInt(year/10, 10) * 10;
842
+ var yearCont = this.picker.find('.datepicker-years')
843
+ .find('th:eq(1)')
844
+ .text(year + '-' + (year + 9))
845
+ .end()
846
+ .find('td');
847
+ year -= 1;
848
+ var years = $.map(this.dates, function(d){
849
+ return d.getUTCFullYear();
850
+ }),
851
+ classes;
852
+ for (var i = -1; i < 11; i++){
853
+ classes = ['year'];
854
+ if (i === -1)
855
+ classes.push('old');
856
+ else if (i === 10)
857
+ classes.push('new');
858
+ if ($.inArray(year, years) !== -1)
859
+ classes.push('active');
860
+ if (year < startYear || year > endYear)
861
+ classes.push('disabled');
862
+ html += '<span class="' + classes.join(' ') + '">'+year+'</span>';
863
+ year += 1;
864
+ }
865
+ yearCont.html(html);
866
+ },
867
+
868
+ updateNavArrows: function(){
869
+ if (!this._allow_update)
870
+ return;
871
+
872
+ var d = new Date(this.viewDate),
873
+ year = d.getUTCFullYear(),
874
+ month = d.getUTCMonth();
875
+ switch (this.viewMode){
876
+ case 0:
877
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){
878
+ this.picker.find('.prev').css({visibility: 'hidden'});
879
+ }
880
+ else {
881
+ this.picker.find('.prev').css({visibility: 'visible'});
882
+ }
883
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){
884
+ this.picker.find('.next').css({visibility: 'hidden'});
885
+ }
886
+ else {
887
+ this.picker.find('.next').css({visibility: 'visible'});
888
+ }
889
+ break;
890
+ case 1:
891
+ case 2:
892
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){
893
+ this.picker.find('.prev').css({visibility: 'hidden'});
894
+ }
895
+ else {
896
+ this.picker.find('.prev').css({visibility: 'visible'});
897
+ }
898
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){
899
+ this.picker.find('.next').css({visibility: 'hidden'});
900
+ }
901
+ else {
902
+ this.picker.find('.next').css({visibility: 'visible'});
903
+ }
904
+ break;
905
+ }
906
+ },
907
+
908
+ click: function(e){
909
+ e.preventDefault();
910
+ var target = $(e.target).closest('span, td, th'),
911
+ year, month, day;
912
+ if (target.length === 1){
913
+ switch (target[0].nodeName.toLowerCase()){
914
+ case 'th':
915
+ switch (target[0].className){
916
+ case 'datepicker-switch':
917
+ this.showMode(1);
918
+ break;
919
+ case 'prev':
920
+ case 'next':
921
+ var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1);
922
+ switch (this.viewMode){
923
+ case 0:
924
+ this.viewDate = this.moveMonth(this.viewDate, dir);
925
+ this._trigger('changeMonth', this.viewDate);
926
+ break;
927
+ case 1:
928
+ case 2:
929
+ this.viewDate = this.moveYear(this.viewDate, dir);
930
+ if (this.viewMode === 1)
931
+ this._trigger('changeYear', this.viewDate);
932
+ break;
933
+ }
934
+ this.fill();
935
+ break;
936
+ case 'today':
937
+ var date = new Date();
938
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
939
+
940
+ this.showMode(-2);
941
+ var which = this.o.todayBtn === 'linked' ? null : 'view';
942
+ this._setDate(date, which);
943
+ break;
944
+ case 'clear':
945
+ var element;
946
+ if (this.isInput)
947
+ element = this.element;
948
+ else if (this.component)
949
+ element = this.element.find('input');
950
+ if (element)
951
+ element.val("").change();
952
+ this.update();
953
+ this._trigger('changeDate');
954
+ if (this.o.autoclose)
955
+ this.hide();
956
+ break;
957
+ }
958
+ break;
959
+ case 'span':
960
+ if (!target.is('.disabled')){
961
+ this.viewDate.setUTCDate(1);
962
+ if (target.is('.month')){
963
+ day = 1;
964
+ month = target.parent().find('span').index(target);
965
+ year = this.viewDate.getUTCFullYear();
966
+ this.viewDate.setUTCMonth(month);
967
+ this._trigger('changeMonth', this.viewDate);
968
+ if (this.o.minViewMode === 1){
969
+ this._setDate(UTCDate(year, month, day));
970
+ }
971
+ }
972
+ else {
973
+ day = 1;
974
+ month = 0;
975
+ year = parseInt(target.text(), 10)||0;
976
+ this.viewDate.setUTCFullYear(year);
977
+ this._trigger('changeYear', this.viewDate);
978
+ if (this.o.minViewMode === 2){
979
+ this._setDate(UTCDate(year, month, day));
980
+ }
981
+ }
982
+ this.showMode(-1);
983
+ this.fill();
984
+ }
985
+ break;
986
+ case 'td':
987
+ if (target.is('.day') && !target.is('.disabled')){
988
+ day = parseInt(target.text(), 10)||1;
989
+ year = this.viewDate.getUTCFullYear();
990
+ month = this.viewDate.getUTCMonth();
991
+ if (target.is('.old')){
992
+ if (month === 0){
993
+ month = 11;
994
+ year -= 1;
995
+ }
996
+ else {
997
+ month -= 1;
998
+ }
999
+ }
1000
+ else if (target.is('.new')){
1001
+ if (month === 11){
1002
+ month = 0;
1003
+ year += 1;
1004
+ }
1005
+ else {
1006
+ month += 1;
1007
+ }
1008
+ }
1009
+ this._setDate(UTCDate(year, month, day));
1010
+ }
1011
+ break;
1012
+ }
1013
+ }
1014
+ if (this.picker.is(':visible') && this._focused_from){
1015
+ $(this._focused_from).focus();
1016
+ }
1017
+ delete this._focused_from;
1018
+ },
1019
+
1020
+ _toggle_multidate: function(date){
1021
+ var ix = this.dates.contains(date);
1022
+ if (!date){
1023
+ this.dates.clear();
1024
+ }
1025
+ if (this.o.multidate === 1 && ix === 0){
1026
+ // single datepicker, don't remove selected date
1027
+ }
1028
+ else if (ix !== -1){
1029
+ this.dates.remove(ix);
1030
+ }
1031
+ else {
1032
+ this.dates.push(date);
1033
+ }
1034
+ if (typeof this.o.multidate === 'number')
1035
+ while (this.dates.length > this.o.multidate)
1036
+ this.dates.remove(0);
1037
+ },
1038
+
1039
+ _setDate: function(date, which){
1040
+ if (!which || which === 'date')
1041
+ this._toggle_multidate(date && new Date(date));
1042
+ if (!which || which === 'view')
1043
+ this.viewDate = date && new Date(date);
1044
+
1045
+ this.fill();
1046
+ this.setValue();
1047
+ this._trigger('changeDate');
1048
+ var element;
1049
+ if (this.isInput){
1050
+ element = this.element;
1051
+ }
1052
+ else if (this.component){
1053
+ element = this.element.find('input');
1054
+ }
1055
+ if (element){
1056
+ element.change();
1057
+ }
1058
+ if (this.o.autoclose && (!which || which === 'date')){
1059
+ this.hide();
1060
+ }
1061
+ },
1062
+
1063
+ moveMonth: function(date, dir){
1064
+ if (!date)
1065
+ return undefined;
1066
+ if (!dir)
1067
+ return date;
1068
+ var new_date = new Date(date.valueOf()),
1069
+ day = new_date.getUTCDate(),
1070
+ month = new_date.getUTCMonth(),
1071
+ mag = Math.abs(dir),
1072
+ new_month, test;
1073
+ dir = dir > 0 ? 1 : -1;
1074
+ if (mag === 1){
1075
+ test = dir === -1
1076
+ // If going back one month, make sure month is not current month
1077
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
1078
+ ? function(){
1079
+ return new_date.getUTCMonth() === month;
1080
+ }
1081
+ // If going forward one month, make sure month is as expected
1082
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
1083
+ : function(){
1084
+ return new_date.getUTCMonth() !== new_month;
1085
+ };
1086
+ new_month = month + dir;
1087
+ new_date.setUTCMonth(new_month);
1088
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
1089
+ if (new_month < 0 || new_month > 11)
1090
+ new_month = (new_month + 12) % 12;
1091
+ }
1092
+ else {
1093
+ // For magnitudes >1, move one month at a time...
1094
+ for (var i=0; i < mag; i++)
1095
+ // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
1096
+ new_date = this.moveMonth(new_date, dir);
1097
+ // ...then reset the day, keeping it in the new month
1098
+ new_month = new_date.getUTCMonth();
1099
+ new_date.setUTCDate(day);
1100
+ test = function(){
1101
+ return new_month !== new_date.getUTCMonth();
1102
+ };
1103
+ }
1104
+ // Common date-resetting loop -- if date is beyond end of month, make it
1105
+ // end of month
1106
+ while (test()){
1107
+ new_date.setUTCDate(--day);
1108
+ new_date.setUTCMonth(new_month);
1109
+ }
1110
+ return new_date;
1111
+ },
1112
+
1113
+ moveYear: function(date, dir){
1114
+ return this.moveMonth(date, dir*12);
1115
+ },
1116
+
1117
+ dateWithinRange: function(date){
1118
+ return date >= this.o.startDate && date <= this.o.endDate;
1119
+ },
1120
+
1121
+ keydown: function(e){
1122
+ if (this.picker.is(':not(:visible)')){
1123
+ if (e.keyCode === 27) // allow escape to hide and re-show picker
1124
+ this.show();
1125
+ return;
1126
+ }
1127
+ var dateChanged = false,
1128
+ dir, newDate, newViewDate,
1129
+ focusDate = this.focusDate || this.viewDate;
1130
+ switch (e.keyCode){
1131
+ case 27: // escape
1132
+ if (this.focusDate){
1133
+ this.focusDate = null;
1134
+ this.viewDate = this.dates.get(-1) || this.viewDate;
1135
+ this.fill();
1136
+ }
1137
+ else
1138
+ this.hide();
1139
+ e.preventDefault();
1140
+ break;
1141
+ case 37: // left
1142
+ case 39: // right
1143
+ if (!this.o.keyboardNavigation)
1144
+ break;
1145
+ dir = e.keyCode === 37 ? -1 : 1;
1146
+ if (e.ctrlKey){
1147
+ newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1148
+ newViewDate = this.moveYear(focusDate, dir);
1149
+ this._trigger('changeYear', this.viewDate);
1150
+ }
1151
+ else if (e.shiftKey){
1152
+ newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1153
+ newViewDate = this.moveMonth(focusDate, dir);
1154
+ this._trigger('changeMonth', this.viewDate);
1155
+ }
1156
+ else {
1157
+ newDate = new Date(this.dates.get(-1) || UTCToday());
1158
+ newDate.setUTCDate(newDate.getUTCDate() + dir);
1159
+ newViewDate = new Date(focusDate);
1160
+ newViewDate.setUTCDate(focusDate.getUTCDate() + dir);
1161
+ }
1162
+ if (this.dateWithinRange(newDate)){
1163
+ this.focusDate = this.viewDate = newViewDate;
1164
+ this.setValue();
1165
+ this.fill();
1166
+ e.preventDefault();
1167
+ }
1168
+ break;
1169
+ case 38: // up
1170
+ case 40: // down
1171
+ if (!this.o.keyboardNavigation)
1172
+ break;
1173
+ dir = e.keyCode === 38 ? -1 : 1;
1174
+ if (e.ctrlKey){
1175
+ newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
1176
+ newViewDate = this.moveYear(focusDate, dir);
1177
+ this._trigger('changeYear', this.viewDate);
1178
+ }
1179
+ else if (e.shiftKey){
1180
+ newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
1181
+ newViewDate = this.moveMonth(focusDate, dir);
1182
+ this._trigger('changeMonth', this.viewDate);
1183
+ }
1184
+ else {
1185
+ newDate = new Date(this.dates.get(-1) || UTCToday());
1186
+ newDate.setUTCDate(newDate.getUTCDate() + dir * 7);
1187
+ newViewDate = new Date(focusDate);
1188
+ newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7);
1189
+ }
1190
+ if (this.dateWithinRange(newDate)){
1191
+ this.focusDate = this.viewDate = newViewDate;
1192
+ this.setValue();
1193
+ this.fill();
1194
+ e.preventDefault();
1195
+ }
1196
+ break;
1197
+ case 32: // spacebar
1198
+ // Spacebar is used in manually typing dates in some formats.
1199
+ // As such, its behavior should not be hijacked.
1200
+ break;
1201
+ case 13: // enter
1202
+ focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
1203
+ if (this.o.keyboardNavigation) {
1204
+ this._toggle_multidate(focusDate);
1205
+ dateChanged = true;
1206
+ }
1207
+ this.focusDate = null;
1208
+ this.viewDate = this.dates.get(-1) || this.viewDate;
1209
+ this.setValue();
1210
+ this.fill();
1211
+ if (this.picker.is(':visible')){
1212
+ e.preventDefault();
1213
+ if (this.o.autoclose)
1214
+ this.hide();
1215
+ }
1216
+ break;
1217
+ case 9: // tab
1218
+ this.focusDate = null;
1219
+ this.viewDate = this.dates.get(-1) || this.viewDate;
1220
+ this.fill();
1221
+ this.hide();
1222
+ break;
1223
+ }
1224
+ if (dateChanged){
1225
+ if (this.dates.length)
1226
+ this._trigger('changeDate');
1227
+ else
1228
+ this._trigger('clearDate');
1229
+ var element;
1230
+ if (this.isInput){
1231
+ element = this.element;
1232
+ }
1233
+ else if (this.component){
1234
+ element = this.element.find('input');
1235
+ }
1236
+ if (element){
1237
+ element.change();
1238
+ }
1239
+ }
1240
+ },
1241
+
1242
+ showMode: function(dir){
1243
+ if (dir){
1244
+ this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
1245
+ }
1246
+ this.picker
1247
+ .find('>div')
1248
+ .hide()
1249
+ .filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName)
1250
+ .css('display', 'block');
1251
+ this.updateNavArrows();
1252
+ }
1253
+ };
1254
+
1255
+ var DateRangePicker = function(element, options){
1256
+ this.element = $(element);
1257
+ this.inputs = $.map(options.inputs, function(i){
1258
+ return i.jquery ? i[0] : i;
1259
+ });
1260
+ delete options.inputs;
1261
+
1262
+ $(this.inputs)
1263
+ .datepicker(options)
1264
+ .bind('changeDate', $.proxy(this.dateUpdated, this));
1265
+
1266
+ this.pickers = $.map(this.inputs, function(i){
1267
+ return $(i).data('datepicker');
1268
+ });
1269
+ this.updateDates();
1270
+ };
1271
+ DateRangePicker.prototype = {
1272
+ updateDates: function(){
1273
+ this.dates = $.map(this.pickers, function(i){
1274
+ return i.getUTCDate();
1275
+ });
1276
+ this.updateRanges();
1277
+ },
1278
+ updateRanges: function(){
1279
+ var range = $.map(this.dates, function(d){
1280
+ return d.valueOf();
1281
+ });
1282
+ $.each(this.pickers, function(i, p){
1283
+ p.setRange(range);
1284
+ });
1285
+ },
1286
+ dateUpdated: function(e){
1287
+ // `this.updating` is a workaround for preventing infinite recursion
1288
+ // between `changeDate` triggering and `setUTCDate` calling. Until
1289
+ // there is a better mechanism.
1290
+ if (this.updating)
1291
+ return;
1292
+ this.updating = true;
1293
+
1294
+ var dp = $(e.target).data('datepicker'),
1295
+ new_date = dp.getUTCDate(),
1296
+ i = $.inArray(e.target, this.inputs),
1297
+ l = this.inputs.length;
1298
+ if (i === -1)
1299
+ return;
1300
+
1301
+ $.each(this.pickers, function(i, p){
1302
+ if (!p.getUTCDate())
1303
+ p.setUTCDate(new_date);
1304
+ });
1305
+
1306
+ if (new_date < this.dates[i]){
1307
+ // Date being moved earlier/left
1308
+ while (i >= 0 && new_date < this.dates[i]){
1309
+ this.pickers[i--].setUTCDate(new_date);
1310
+ }
1311
+ }
1312
+ else if (new_date > this.dates[i]){
1313
+ // Date being moved later/right
1314
+ while (i < l && new_date > this.dates[i]){
1315
+ this.pickers[i++].setUTCDate(new_date);
1316
+ }
1317
+ }
1318
+ this.updateDates();
1319
+
1320
+ delete this.updating;
1321
+ },
1322
+ remove: function(){
1323
+ $.map(this.pickers, function(p){ p.remove(); });
1324
+ delete this.element.data().datepicker;
1325
+ }
1326
+ };
1327
+
1328
+ function opts_from_el(el, prefix){
1329
+ // Derive options from element data-attrs
1330
+ var data = $(el).data(),
1331
+ out = {}, inkey,
1332
+ replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
1333
+ prefix = new RegExp('^' + prefix.toLowerCase());
1334
+ function re_lower(_,a){
1335
+ return a.toLowerCase();
1336
+ }
1337
+ for (var key in data)
1338
+ if (prefix.test(key)){
1339
+ inkey = key.replace(replace, re_lower);
1340
+ out[inkey] = data[key];
1341
+ }
1342
+ return out;
1343
+ }
1344
+
1345
+ function opts_from_locale(lang){
1346
+ // Derive options from locale plugins
1347
+ var out = {};
1348
+ // Check if "de-DE" style date is available, if not language should
1349
+ // fallback to 2 letter code eg "de"
1350
+ if (!dates[lang]){
1351
+ lang = lang.split('-')[0];
1352
+ if (!dates[lang])
1353
+ return;
1354
+ }
1355
+ var d = dates[lang];
1356
+ $.each(locale_opts, function(i,k){
1357
+ if (k in d)
1358
+ out[k] = d[k];
1359
+ });
1360
+ return out;
1361
+ }
1362
+
1363
+ var old = $.fn.datepicker;
1364
+ $.fn.datepicker = function(option){
1365
+ var args = Array.apply(null, arguments);
1366
+ args.shift();
1367
+ var internal_return;
1368
+ this.each(function(){
1369
+ var $this = $(this),
1370
+ data = $this.data('datepicker'),
1371
+ options = typeof option === 'object' && option;
1372
+ if (!data){
1373
+ var elopts = opts_from_el(this, 'date'),
1374
+ // Preliminary otions
1375
+ xopts = $.extend({}, defaults, elopts, options),
1376
+ locopts = opts_from_locale(xopts.language),
1377
+ // Options priority: js args, data-attrs, locales, defaults
1378
+ opts = $.extend({}, defaults, locopts, elopts, options);
1379
+ if ($this.is('.input-daterange') || opts.inputs){
1380
+ var ropts = {
1381
+ inputs: opts.inputs || $this.find('input').toArray()
1382
+ };
1383
+ $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
1384
+ }
1385
+ else {
1386
+ $this.data('datepicker', (data = new Datepicker(this, opts)));
1387
+ }
1388
+ }
1389
+ if (typeof option === 'string' && typeof data[option] === 'function'){
1390
+ internal_return = data[option].apply(data, args);
1391
+ if (internal_return !== undefined)
1392
+ return false;
1393
+ }
1394
+ });
1395
+ if (internal_return !== undefined)
1396
+ return internal_return;
1397
+ else
1398
+ return this;
1399
+ };
1400
+
1401
+ var defaults = $.fn.datepicker.defaults = {
1402
+ autoclose: false,
1403
+ beforeShowDay: $.noop,
1404
+ calendarWeeks: false,
1405
+ clearBtn: false,
1406
+ daysOfWeekDisabled: [],
1407
+ endDate: Infinity,
1408
+ forceParse: true,
1409
+ format: 'mm/dd/yyyy',
1410
+ keyboardNavigation: true,
1411
+ language: 'en',
1412
+ minViewMode: 0,
1413
+ multidate: false,
1414
+ multidateSeparator: ',',
1415
+ orientation: "auto",
1416
+ rtl: false,
1417
+ startDate: -Infinity,
1418
+ startView: 0,
1419
+ todayBtn: false,
1420
+ todayHighlight: false,
1421
+ weekStart: 0
1422
+ };
1423
+ var locale_opts = $.fn.datepicker.locale_opts = [
1424
+ 'format',
1425
+ 'rtl',
1426
+ 'weekStart'
1427
+ ];
1428
+ $.fn.datepicker.Constructor = Datepicker;
1429
+ var dates = $.fn.datepicker.dates = {
1430
+ en: {
1431
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
1432
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1433
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
1434
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
1435
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
1436
+ today: "Today",
1437
+ clear: "Clear"
1438
+ }
1439
+ };
1440
+
1441
+ var DPGlobal = {
1442
+ modes: [
1443
+ {
1444
+ clsName: 'days',
1445
+ navFnc: 'Month',
1446
+ navStep: 1
1447
+ },
1448
+ {
1449
+ clsName: 'months',
1450
+ navFnc: 'FullYear',
1451
+ navStep: 1
1452
+ },
1453
+ {
1454
+ clsName: 'years',
1455
+ navFnc: 'FullYear',
1456
+ navStep: 10
1457
+ }],
1458
+ isLeapYear: function(year){
1459
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
1460
+ },
1461
+ getDaysInMonth: function(year, month){
1462
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
1463
+ },
1464
+ validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
1465
+ nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
1466
+ parseFormat: function(format){
1467
+ // IE treats \0 as a string end in inputs (truncating the value),
1468
+ // so it's a bad format delimiter, anyway
1469
+ var separators = format.replace(this.validParts, '\0').split('\0'),
1470
+ parts = format.match(this.validParts);
1471
+ if (!separators || !separators.length || !parts || parts.length === 0){
1472
+ throw new Error("Invalid date format.");
1473
+ }
1474
+ return {separators: separators, parts: parts};
1475
+ },
1476
+ parseDate: function(date, format, language){
1477
+ if (!date)
1478
+ return undefined;
1479
+ if (date instanceof Date)
1480
+ return date;
1481
+ if (typeof format === 'string')
1482
+ format = DPGlobal.parseFormat(format);
1483
+ var part_re = /([\-+]\d+)([dmwy])/,
1484
+ parts = date.match(/([\-+]\d+)([dmwy])/g),
1485
+ part, dir, i;
1486
+ if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){
1487
+ date = new Date();
1488
+ for (i=0; i < parts.length; i++){
1489
+ part = part_re.exec(parts[i]);
1490
+ dir = parseInt(part[1]);
1491
+ switch (part[2]){
1492
+ case 'd':
1493
+ date.setUTCDate(date.getUTCDate() + dir);
1494
+ break;
1495
+ case 'm':
1496
+ date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
1497
+ break;
1498
+ case 'w':
1499
+ date.setUTCDate(date.getUTCDate() + dir * 7);
1500
+ break;
1501
+ case 'y':
1502
+ date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
1503
+ break;
1504
+ }
1505
+ }
1506
+ return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
1507
+ }
1508
+ parts = date && date.match(this.nonpunctuation) || [];
1509
+ date = new Date();
1510
+ var parsed = {},
1511
+ setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
1512
+ setters_map = {
1513
+ yyyy: function(d,v){
1514
+ return d.setUTCFullYear(v);
1515
+ },
1516
+ yy: function(d,v){
1517
+ return d.setUTCFullYear(2000+v);
1518
+ },
1519
+ m: function(d,v){
1520
+ if (isNaN(d))
1521
+ return d;
1522
+ v -= 1;
1523
+ while (v < 0) v += 12;
1524
+ v %= 12;
1525
+ d.setUTCMonth(v);
1526
+ while (d.getUTCMonth() !== v)
1527
+ d.setUTCDate(d.getUTCDate()-1);
1528
+ return d;
1529
+ },
1530
+ d: function(d,v){
1531
+ return d.setUTCDate(v);
1532
+ }
1533
+ },
1534
+ val, filtered;
1535
+ setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
1536
+ setters_map['dd'] = setters_map['d'];
1537
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
1538
+ var fparts = format.parts.slice();
1539
+ // Remove noop parts
1540
+ if (parts.length !== fparts.length){
1541
+ fparts = $(fparts).filter(function(i,p){
1542
+ return $.inArray(p, setters_order) !== -1;
1543
+ }).toArray();
1544
+ }
1545
+ // Process remainder
1546
+ function match_part(){
1547
+ var m = this.slice(0, parts[i].length),
1548
+ p = parts[i].slice(0, m.length);
1549
+ return m === p;
1550
+ }
1551
+ if (parts.length === fparts.length){
1552
+ var cnt;
1553
+ for (i=0, cnt = fparts.length; i < cnt; i++){
1554
+ val = parseInt(parts[i], 10);
1555
+ part = fparts[i];
1556
+ if (isNaN(val)){
1557
+ switch (part){
1558
+ case 'MM':
1559
+ filtered = $(dates[language].months).filter(match_part);
1560
+ val = $.inArray(filtered[0], dates[language].months) + 1;
1561
+ break;
1562
+ case 'M':
1563
+ filtered = $(dates[language].monthsShort).filter(match_part);
1564
+ val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
1565
+ break;
1566
+ }
1567
+ }
1568
+ parsed[part] = val;
1569
+ }
1570
+ var _date, s;
1571
+ for (i=0; i < setters_order.length; i++){
1572
+ s = setters_order[i];
1573
+ if (s in parsed && !isNaN(parsed[s])){
1574
+ _date = new Date(date);
1575
+ setters_map[s](_date, parsed[s]);
1576
+ if (!isNaN(_date))
1577
+ date = _date;
1578
+ }
1579
+ }
1580
+ }
1581
+ return date;
1582
+ },
1583
+ formatDate: function(date, format, language){
1584
+ if (!date)
1585
+ return '';
1586
+ if (typeof format === 'string')
1587
+ format = DPGlobal.parseFormat(format);
1588
+ var val = {
1589
+ d: date.getUTCDate(),
1590
+ D: dates[language].daysShort[date.getUTCDay()],
1591
+ DD: dates[language].days[date.getUTCDay()],
1592
+ m: date.getUTCMonth() + 1,
1593
+ M: dates[language].monthsShort[date.getUTCMonth()],
1594
+ MM: dates[language].months[date.getUTCMonth()],
1595
+ yy: date.getUTCFullYear().toString().substring(2),
1596
+ yyyy: date.getUTCFullYear()
1597
+ };
1598
+ val.dd = (val.d < 10 ? '0' : '') + val.d;
1599
+ val.mm = (val.m < 10 ? '0' : '') + val.m;
1600
+ date = [];
1601
+ var seps = $.extend([], format.separators);
1602
+ for (var i=0, cnt = format.parts.length; i <= cnt; i++){
1603
+ if (seps.length)
1604
+ date.push(seps.shift());
1605
+ date.push(val[format.parts[i]]);
1606
+ }
1607
+ return date.join('');
1608
+ },
1609
+ headTemplate: '<thead>'+
1610
+ '<tr>'+
1611
+ '<th class="prev">&laquo;</th>'+
1612
+ '<th colspan="5" class="datepicker-switch"></th>'+
1613
+ '<th class="next">&raquo;</th>'+
1614
+ '</tr>'+
1615
+ '</thead>',
1616
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
1617
+ footTemplate: '<tfoot>'+
1618
+ '<tr>'+
1619
+ '<th colspan="7" class="today"></th>'+
1620
+ '</tr>'+
1621
+ '<tr>'+
1622
+ '<th colspan="7" class="clear"></th>'+
1623
+ '</tr>'+
1624
+ '</tfoot>'
1625
+ };
1626
+ DPGlobal.template = '<div class="datepicker">'+
1627
+ '<div class="datepicker-days">'+
1628
+ '<table class=" table-condensed">'+
1629
+ DPGlobal.headTemplate+
1630
+ '<tbody></tbody>'+
1631
+ DPGlobal.footTemplate+
1632
+ '</table>'+
1633
+ '</div>'+
1634
+ '<div class="datepicker-months">'+
1635
+ '<table class="table-condensed">'+
1636
+ DPGlobal.headTemplate+
1637
+ DPGlobal.contTemplate+
1638
+ DPGlobal.footTemplate+
1639
+ '</table>'+
1640
+ '</div>'+
1641
+ '<div class="datepicker-years">'+
1642
+ '<table class="table-condensed">'+
1643
+ DPGlobal.headTemplate+
1644
+ DPGlobal.contTemplate+
1645
+ DPGlobal.footTemplate+
1646
+ '</table>'+
1647
+ '</div>'+
1648
+ '</div>';
1649
+
1650
+ $.fn.datepicker.DPGlobal = DPGlobal;
1651
+
1652
+
1653
+ /* DATEPICKER NO CONFLICT
1654
+ * =================== */
1655
+
1656
+ $.fn.datepicker.noConflict = function(){
1657
+ $.fn.datepicker = old;
1658
+ return this;
1659
+ };
1660
+
1661
+
1662
+ /* DATEPICKER DATA-API
1663
+ * ================== */
1664
+
1665
+ $(document).on(
1666
+ 'focus.datepicker.data-api click.datepicker.data-api',
1667
+ '[data-provide="datepicker"]',
1668
+ function(e){
1669
+ var $this = $(this);
1670
+ if ($this.data('datepicker'))
1671
+ return;
1672
+ e.preventDefault();
1673
+ // component click requires us to explicitly show it
1674
+ $this.datepicker('show');
1675
+ }
1676
+ );
1677
+ $(function(){
1678
+ $('[data-provide="datepicker-inline"]').datepicker();
1679
+ });
1680
+
1681
+ }(window.jQuery));