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