bootstrap3-datetimepicker-rails 3.1.3 → 4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb477959c3477490d867df063524261dfe6ec5b3
4
- data.tar.gz: 7fb0a411045b796a1b2d60b159ea554f42e7b1bb
3
+ metadata.gz: 5d6e6ae4f217127f3562db4629ff366c6ef760c4
4
+ data.tar.gz: 6f102779311ce6a65c1937ee2c64f64dc6a3ca35
5
5
  SHA512:
6
- metadata.gz: 67bc58ab7ee00ec144029516cc007308a4590980c2713d4e9a104b34f72b41108c5593280e91f47a99a7fa27153618996e0abc5464858e18e92176a747ca7f7e
7
- data.tar.gz: 8115cb38deacab25b98e9ac161493dd1491f2c64f113eeffa4a139d36e050e52bf386f12e9819fa95a604f389bbe8fe50211e1207049f8855e87466ffd0d9aec
6
+ metadata.gz: b64abe901e720c6213d783b01ca4ed3f96edbb27bbb9475e36c7ff4f9351ae2f5d9e8fd54c5e6c24d759f2589210518e5b36e912b75fa0988d351ac466407983
7
+ data.tar.gz: eed1390c3116875844a3e41bfdd3c2159ed2739f6138cee7dfca13466f74e3b04dfa395b4188dd2d941ed14971c5bdd92982e33a2b9fc6cca288188304a41ecc
data/README.md CHANGED
@@ -13,8 +13,8 @@ actively maintained and works with [Bootstrap3](http://getbootstrap.com).
13
13
 
14
14
  Add these lines to your application's Gemfile:
15
15
  ```ruby
16
- gem 'momentjs-rails', '>= 2.8.1'
17
- gem 'bootstrap3-datetimepicker-rails', '~> 3.1.3'
16
+ gem 'momentjs-rails', '>= 2.9.0'
17
+ gem 'bootstrap3-datetimepicker-rails', '~> 4.0.0'
18
18
  ```
19
19
 
20
20
  And then execute:
@@ -32,7 +32,7 @@ To start using the bootstrap-datetimepicker plugin in your Rails application, en
32
32
 
33
33
  If your server is running, you will need to **restart Rails** before using the gem.
34
34
 
35
- Add the following to your Javascript manifest file (`application.js`):
35
+ Add the following to your JavaScript manifest file (`application.js`):
36
36
  ```js
37
37
  //= require moment
38
38
  //= require bootstrap-datetimepicker
@@ -43,7 +43,7 @@ If you want to include a localization, also add:
43
43
  //= require moment/<locale>
44
44
  ```
45
45
 
46
- Add the following to your stylesheet file:
46
+ Add the following to your style sheet file:
47
47
 
48
48
  If you are using SCSS, modify your `application.css.scss`
49
49
  ```scss
@@ -63,6 +63,10 @@ If you're using plain CSS, modify your `application.css`
63
63
 
64
64
  Check out the documentation at: http://eonasdan.github.io/bootstrap-datetimepicker/
65
65
 
66
+ ## Problems
67
+
68
+ Clone https://github.com/TrevorS/bs3dp-test and try to reproduce your issue.
69
+
66
70
  ## Contributing
67
71
 
68
72
  1. Fork it
@@ -1,5 +1,5 @@
1
1
  module Bootstrap3Datetimepicker
2
2
  module Rails
3
- VERSION = '3.1.3'
3
+ VERSION = '4.0.0'
4
4
  end
5
5
  end
@@ -1,1384 +1,1700 @@
1
1
  /*
2
- //! version : 3.1.3
3
- =========================================================
4
- bootstrap-datetimepicker.js
5
- https://github.com/Eonasdan/bootstrap-datetimepicker
6
- =========================================================
7
- The MIT License (MIT)
8
-
9
- Copyright (c) 2014 Jonathan Peterson
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy
12
- of this software and associated documentation files (the "Software"), to deal
13
- in the Software without restriction, including without limitation the rights
14
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
- copies of the Software, and to permit persons to whom the Software is
16
- furnished to do so, subject to the following conditions:
17
-
18
- The above copyright notice and this permission notice shall be included in
19
- all copies or substantial portions of the Software.
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
- THE SOFTWARE.
28
- */
29
- ;(function (root, factory) {
2
+ //! version : 4.0.0
3
+ =========================================================
4
+ bootstrap-datetimejs
5
+ https://github.com/Eonasdan/bootstrap-datetimepicker
6
+ =========================================================
7
+ The MIT License (MIT)
8
+
9
+ Copyright (c) 2015 Jonathan Peterson
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in
19
+ all copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ THE SOFTWARE.
28
+ */
29
+ (function (factory) {
30
30
  'use strict';
31
31
  if (typeof define === 'function' && define.amd) {
32
32
  // AMD is used - Register as an anonymous module.
33
33
  define(['jquery', 'moment'], factory);
34
34
  } else if (typeof exports === 'object') {
35
35
  factory(require('jquery'), require('moment'));
36
- }
37
- else {
38
- // Neither AMD or CommonJS used. Use global variables.
36
+ } else {
37
+ // Neither AMD nor CommonJS used. Use global variables.
39
38
  if (!jQuery) {
40
- throw new Error('bootstrap-datetimepicker requires jQuery to be loaded first');
39
+ throw 'bootstrap-datetimepicker requires jQuery to be loaded first';
41
40
  }
42
41
  if (!moment) {
43
- throw new Error('bootstrap-datetimepicker requires moment.js to be loaded first');
42
+ throw 'bootstrap-datetimepicker requires Moment.js to be loaded first';
44
43
  }
45
- factory(root.jQuery, moment);
44
+ factory(jQuery, moment);
46
45
  }
47
- }(this, function ($, moment) {
46
+ }(function ($, moment) {
48
47
  'use strict';
49
- if (typeof moment === 'undefined') {
50
- throw new Error('momentjs is required');
48
+ if (!moment) {
49
+ throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first');
51
50
  }
52
51
 
53
- var dpgId = 0,
52
+ var dateTimePicker = function (element, options) {
53
+ var picker = {},
54
+ date = moment(),
55
+ viewDate = date.clone(),
56
+ unset = true,
57
+ input,
58
+ component = false,
59
+ widget = false,
60
+ use24Hours,
61
+ minViewModeNumber = 0,
62
+ actualFormat,
63
+ parseFormats,
64
+ currentViewMode,
65
+ datePickerModes = [
66
+ {
67
+ clsName: 'days',
68
+ navFnc: 'M',
69
+ navStep: 1
70
+ },
71
+ {
72
+ clsName: 'months',
73
+ navFnc: 'y',
74
+ navStep: 1
75
+ },
76
+ {
77
+ clsName: 'years',
78
+ navFnc: 'y',
79
+ navStep: 10
80
+ }
81
+ ],
82
+ viewModes = ['days', 'months', 'years'],
83
+ verticalModes = ['top', 'bottom', 'auto'],
84
+ horizontalModes = ['left', 'right', 'auto'],
85
+ toolbarPlacements = ['default', 'top', 'bottom'],
86
+
87
+ /********************************************************************************
88
+ *
89
+ * Private functions
90
+ *
91
+ ********************************************************************************/
92
+ isEnabled = function (granularity) {
93
+ if (typeof granularity !== 'string' || granularity.length > 1) {
94
+ throw new TypeError('isEnabled expects a single character string parameter');
95
+ }
96
+ switch (granularity) {
97
+ case 'y':
98
+ return actualFormat.indexOf('Y') !== -1;
99
+ case 'M':
100
+ return actualFormat.indexOf('M') !== -1;
101
+ case 'd':
102
+ return actualFormat.toLowerCase().indexOf('d') !== -1;
103
+ case 'h':
104
+ case 'H':
105
+ return actualFormat.toLowerCase().indexOf('h') !== -1;
106
+ case 'm':
107
+ return actualFormat.indexOf('m') !== -1;
108
+ case 's':
109
+ return actualFormat.indexOf('s') !== -1;
110
+ default:
111
+ return false;
112
+ }
113
+ },
54
114
 
55
- DateTimePicker = function (element, options) {
56
- var defaults = $.fn.datetimepicker.defaults,
115
+ hasTime = function () {
116
+ return (isEnabled('h') || isEnabled('m') || isEnabled('s'));
117
+ },
57
118
 
58
- icons = {
59
- time: 'glyphicon glyphicon-time',
60
- date: 'glyphicon glyphicon-calendar',
61
- up: 'glyphicon glyphicon-chevron-up',
62
- down: 'glyphicon glyphicon-chevron-down'
119
+ hasDate = function () {
120
+ return (isEnabled('y') || isEnabled('M') || isEnabled('d'));
63
121
  },
64
122
 
65
- picker = this,
66
- errored = false,
67
- dDate,
123
+ getDatePickerTemplate = function () {
124
+ var headTemplate = $('<thead>')
125
+ .append($('<tr>')
126
+ .append($('<th>').addClass('prev').attr('data-action', 'previous')
127
+ .append($('<span>').addClass(options.icons.previous))
128
+ )
129
+ .append($('<th>').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5')))
130
+ .append($('<th>').addClass('next').attr('data-action', 'next')
131
+ .append($('<span>').addClass(options.icons.next))
132
+ )
133
+ ),
134
+ contTemplate = $('<tbody>')
135
+ .append($('<tr>')
136
+ .append($('<td>').attr('colspan', (options.calendarWeeks ? '8' : '7')))
137
+ );
138
+
139
+ return [
140
+ $('<div>').addClass('datepicker-days')
141
+ .append($('<table>').addClass('table-condensed')
142
+ .append(headTemplate)
143
+ .append($('<tbody>'))
144
+ ),
145
+ $('<div>').addClass('datepicker-months')
146
+ .append($('<table>').addClass('table-condensed')
147
+ .append(headTemplate.clone())
148
+ .append(contTemplate.clone())
149
+ ),
150
+ $('<div>').addClass('datepicker-years')
151
+ .append($('<table>').addClass('table-condensed')
152
+ .append(headTemplate.clone())
153
+ .append(contTemplate.clone())
154
+ )
155
+ ];
156
+ },
157
+
158
+ getTimePickerMainTemplate = function () {
159
+ var topRow = $('<tr>'),
160
+ middleRow = $('<tr>'),
161
+ bottomRow = $('<tr>');
162
+
163
+ if (isEnabled('h')) {
164
+ topRow.append($('<td>')
165
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'incrementHours')
166
+ .append($('<span>').addClass(options.icons.up))));
167
+ middleRow.append($('<td>')
168
+ .append($('<span>').addClass('timepicker-hour').attr('data-time-component', 'hours').attr('data-action', 'showHours')));
169
+ bottomRow.append($('<td>')
170
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'decrementHours')
171
+ .append($('<span>').addClass(options.icons.down))));
172
+ }
173
+ if (isEnabled('m')) {
174
+ if (isEnabled('h')) {
175
+ topRow.append($('<td>').addClass('separator'));
176
+ middleRow.append($('<td>').addClass('separator').html(':'));
177
+ bottomRow.append($('<td>').addClass('separator'));
178
+ }
179
+ topRow.append($('<td>')
180
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'incrementMinutes')
181
+ .append($('<span>').addClass(options.icons.up))));
182
+ middleRow.append($('<td>')
183
+ .append($('<span>').addClass('timepicker-minute').attr('data-time-component', 'minutes').attr('data-action', 'showMinutes')));
184
+ bottomRow.append($('<td>')
185
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'decrementMinutes')
186
+ .append($('<span>').addClass(options.icons.down))));
187
+ }
188
+ if (isEnabled('s')) {
189
+ if (isEnabled('m')) {
190
+ topRow.append($('<td>').addClass('separator'));
191
+ middleRow.append($('<td>').addClass('separator').html(':'));
192
+ bottomRow.append($('<td>').addClass('separator'));
193
+ }
194
+ topRow.append($('<td>')
195
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'incrementSeconds')
196
+ .append($('<span>').addClass(options.icons.up))));
197
+ middleRow.append($('<td>')
198
+ .append($('<span>').addClass('timepicker-second').attr('data-time-component', 'seconds').attr('data-action', 'showSeconds')));
199
+ bottomRow.append($('<td>')
200
+ .append($('<a>').attr('href', '#').addClass('btn').attr('data-action', 'decrementSeconds')
201
+ .append($('<span>').addClass(options.icons.down))));
202
+ }
203
+
204
+ if (!use24Hours) {
205
+ topRow.append($('<td>').addClass('separator'));
206
+ middleRow.append($('<td>')
207
+ .append($('<button>').addClass('btn btn-primary').attr('data-action', 'togglePeriod')));
208
+ bottomRow.append($('<td>').addClass('separator'));
209
+ }
68
210
 
69
- init = function () {
70
- var icon = false, localeData, rInterval;
71
- picker.options = $.extend({}, defaults, options);
72
- picker.options.icons = $.extend({}, icons, picker.options.icons);
211
+ return $('<div>').addClass('timepicker-picker')
212
+ .append($('<table>').addClass('table-condensed')
213
+ .append([topRow, middleRow, bottomRow]));
214
+ },
73
215
 
74
- picker.element = $(element);
216
+ getTimePickerTemplate = function () {
217
+ var hoursView = $('<div>').addClass('timepicker-hours')
218
+ .append($('<table>').addClass('table-condensed')),
219
+ minutesView = $('<div>').addClass('timepicker-minutes')
220
+ .append($('<table>').addClass('table-condensed')),
221
+ secondsView = $('<div>').addClass('timepicker-seconds')
222
+ .append($('<table>').addClass('table-condensed')),
223
+ ret = [getTimePickerMainTemplate()];
224
+
225
+ if (isEnabled('h')) {
226
+ ret.push(hoursView);
227
+ }
228
+ if (isEnabled('m')) {
229
+ ret.push(minutesView);
230
+ }
231
+ if (isEnabled('s')) {
232
+ ret.push(secondsView);
233
+ }
75
234
 
76
- dataToOptions();
235
+ return ret;
236
+ },
77
237
 
78
- if (!(picker.options.pickTime || picker.options.pickDate)) {
79
- throw new Error('Must choose at least one picker');
80
- }
238
+ getToolbar = function () {
239
+ var row = [];
240
+ if (options.showTodayButton) {
241
+ row.push($('<td>').append($('<a>').attr('data-action', 'today').append($('<span>').addClass(options.icons.today))));
242
+ }
243
+ if (!options.sideBySide && hasDate() && hasTime()) {
244
+ row.push($('<td>').append($('<a>').attr('data-action', 'togglePicker').append($('<span>').addClass(options.icons.time))));
245
+ }
246
+ if (options.showClear) {
247
+ row.push($('<td>').append($('<a>').attr('data-action', 'clear').append($('<span>').addClass(options.icons.clear))));
248
+ }
249
+ return $('<table>').addClass('table-condensed').append($('<tbody>').append($('<tr>').append(row)));
250
+ },
81
251
 
82
- picker.id = dpgId++;
83
- moment.locale(picker.options.language);
84
- picker.date = moment();
85
- picker.unset = false;
86
- picker.isInput = picker.element.is('input');
87
- picker.component = false;
252
+ getTemplate = function () {
253
+ var template = $('<div>').addClass('bootstrap-datetimepicker-widget dropdown-menu'),
254
+ dateView = $('<div>').addClass('datepicker').append(getDatePickerTemplate()),
255
+ timeView = $('<div>').addClass('timepicker').append(getTimePickerTemplate()),
256
+ content = $('<ul>').addClass('list-unstyled'),
257
+ toolbar = $('<li>').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar());
88
258
 
89
- if (picker.element.hasClass('input-group')) {
90
- if (picker.element.find('.datepickerbutton').size() === 0) {//in case there is more then one 'input-group-addon' Issue #48
91
- picker.component = picker.element.find('[class^="input-group-"]');
259
+ if (use24Hours) {
260
+ template.addClass('usetwentyfour');
92
261
  }
93
- else {
94
- picker.component = picker.element.find('.datepickerbutton');
262
+ if (options.sideBySide && hasDate() && hasTime()) {
263
+ template.addClass('timepicker-sbs');
264
+ template.append(
265
+ $('<div>').addClass('row')
266
+ .append(dateView.addClass('col-sm-6'))
267
+ .append(timeView.addClass('col-sm-6'))
268
+ );
269
+ template.append(toolbar);
270
+ return template;
95
271
  }
96
- }
97
- picker.format = picker.options.format;
98
272
 
99
- localeData = moment().localeData();
273
+ if (options.toolbarPlacement === 'top') {
274
+ content.append(toolbar);
275
+ }
276
+ if (hasDate()) {
277
+ content.append($('<li>').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView));
278
+ }
279
+ if (options.toolbarPlacement === 'default') {
280
+ content.append(toolbar);
281
+ }
282
+ if (hasTime()) {
283
+ content.append($('<li>').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView));
284
+ }
285
+ if (options.toolbarPlacement === 'bottom') {
286
+ content.append(toolbar);
287
+ }
288
+ return template.append(content);
289
+ },
290
+
291
+ dataToOptions = function () {
292
+ var eData = element.data(),
293
+ dataOptions = {};
100
294
 
101
- if (!picker.format) {
102
- picker.format = (picker.options.pickDate ? localeData.longDateFormat('L') : '');
103
- if (picker.options.pickDate && picker.options.pickTime) {
104
- picker.format += ' ';
295
+ if (eData.dateOptions && eData.dateOptions instanceof Object) {
296
+ dataOptions = $.extend(true, dataOptions, eData.dateOptions);
105
297
  }
106
- picker.format += (picker.options.pickTime ? localeData.longDateFormat('LT') : '');
107
- if (picker.options.useSeconds) {
108
- if (localeData.longDateFormat('LT').indexOf(' A') !== -1) {
109
- picker.format = picker.format.split(' A')[0] + ':ss A';
298
+
299
+ $.each(options, function (key) {
300
+ var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1);
301
+ if (eData[attributeName] !== undefined) {
302
+ dataOptions[key] = eData[attributeName];
110
303
  }
111
- else {
112
- picker.format += ':ss';
304
+ });
305
+ return dataOptions;
306
+ },
307
+
308
+ place = function () {
309
+ var offset = (component || element).position(),
310
+ vertical = options.widgetPositioning.vertical,
311
+ horizontal = options.widgetPositioning.horizontal,
312
+ parent;
313
+
314
+ if (options.widgetParent) {
315
+ parent = options.widgetParent.append(widget);
316
+ } else if (element.is('input')) {
317
+ parent = element.parent().append(widget);
318
+ } else {
319
+ parent = element;
320
+ element.children().first().after(widget);
321
+ }
322
+
323
+ // Top and bottom logic
324
+ if (vertical === 'auto') {
325
+ if ((component || element).offset().top + widget.height() > $(window).height() + $(window).scrollTop() &&
326
+ widget.height() + element.outerHeight() < (component || element).offset().top) {
327
+ vertical = 'top';
328
+ } else {
329
+ vertical = 'bottom';
113
330
  }
114
331
  }
115
- }
116
- picker.use24hours = (picker.format.toLowerCase().indexOf('a') < 0 && picker.format.indexOf('h') < 0);
117
332
 
118
- if (picker.component) {
119
- icon = picker.component.find('span');
120
- }
333
+ // Left and right logic
334
+ if (horizontal === 'auto') {
335
+ if (parent.width() < offset.left + widget.outerWidth()) {
336
+ horizontal = 'right';
337
+ } else {
338
+ horizontal = 'left';
339
+ }
340
+ }
121
341
 
122
- if (picker.options.pickTime) {
123
- if (icon) {
124
- icon.addClass(picker.options.icons.time);
342
+ if (vertical === 'top') {
343
+ widget.addClass('top').removeClass('bottom');
344
+ } else {
345
+ widget.addClass('bottom').removeClass('top');
125
346
  }
126
- }
127
- if (picker.options.pickDate) {
128
- if (icon) {
129
- icon.removeClass(picker.options.icons.time);
130
- icon.addClass(picker.options.icons.date);
347
+
348
+ if (horizontal === 'right') {
349
+ widget.addClass('pull-right');
350
+ } else {
351
+ widget.removeClass('pull-right');
131
352
  }
132
- }
133
353
 
134
- picker.options.widgetParent =
135
- typeof picker.options.widgetParent === 'string' && picker.options.widgetParent ||
136
- picker.element.parents().filter(function () {
137
- return 'scroll' === $(this).css('overflow-y');
138
- }).get(0) ||
139
- 'body';
354
+ // find the first parent element that has a relative css positioning
355
+ if (parent.css('position') !== 'relative') {
356
+ parent = parent.parents().filter(function () {
357
+ return $(this).css('position') === 'relative';
358
+ }).first();
359
+ }
140
360
 
141
- picker.widget = $(getTemplate()).appendTo(picker.options.widgetParent);
361
+ if (parent.length === 0) {
362
+ throw new Error('datetimepicker component should be placed within a relative positioned container');
363
+ }
142
364
 
143
- picker.minViewMode = picker.options.minViewMode || 0;
144
- if (typeof picker.minViewMode === 'string') {
145
- switch (picker.minViewMode) {
146
- case 'months':
147
- picker.minViewMode = 1;
148
- break;
149
- case 'years':
150
- picker.minViewMode = 2;
151
- break;
152
- default:
153
- picker.minViewMode = 0;
154
- break;
155
- }
156
- }
157
- picker.viewMode = picker.options.viewMode || 0;
158
- if (typeof picker.viewMode === 'string') {
159
- switch (picker.viewMode) {
160
- case 'months':
161
- picker.viewMode = 1;
162
- break;
163
- case 'years':
164
- picker.viewMode = 2;
165
- break;
166
- default:
167
- picker.viewMode = 0;
168
- break;
365
+ widget.css({
366
+ top: vertical === 'top' ? 'auto' : offset.top + element.outerHeight(),
367
+ bottom: vertical === 'top' ? offset.top + element.outerHeight() : 'auto',
368
+ left: horizontal === 'left' ? parent.css('padding-left') : 'auto',
369
+ right: horizontal === 'left' ? 'auto' : parent.css('padding-right')
370
+ });
371
+ },
372
+
373
+ notifyEvent = function (e) {
374
+ if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) {
375
+ return;
169
376
  }
170
- }
377
+ element.trigger(e);
378
+ },
171
379
 
172
- picker.viewMode = Math.max(picker.viewMode, picker.minViewMode);
380
+ showMode = function (dir) {
381
+ if (!widget) {
382
+ return;
383
+ }
384
+ if (dir) {
385
+ currentViewMode = Math.max(minViewModeNumber, Math.min(2, currentViewMode + dir));
386
+ }
387
+ widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show();
388
+ },
173
389
 
174
- picker.options.disabledDates = indexGivenDates(picker.options.disabledDates);
175
- picker.options.enabledDates = indexGivenDates(picker.options.enabledDates);
390
+ fillDow = function () {
391
+ var row = $('<tr>'),
392
+ currentDate = viewDate.clone().startOf('w');
176
393
 
177
- picker.startViewMode = picker.viewMode;
178
- picker.setMinDate(picker.options.minDate);
179
- picker.setMaxDate(picker.options.maxDate);
180
- fillDow();
181
- fillMonths();
182
- fillHours();
183
- fillMinutes();
184
- fillSeconds();
185
- update();
186
- showMode();
187
- if (!getPickerInput().prop('disabled')) {
188
- attachDatePickerEvents();
189
- }
190
- if (picker.options.defaultDate !== '' && getPickerInput().val() === '') {
191
- picker.setValue(picker.options.defaultDate);
192
- }
193
- if (picker.options.minuteStepping !== 1) {
194
- rInterval = picker.options.minuteStepping;
195
- picker.date.minutes((Math.round(picker.date.minutes() / rInterval) * rInterval) % 60).seconds(0);
196
- }
197
- },
394
+ if (options.calendarWeeks === true) {
395
+ row.append($('<th>').addClass('cw').text('#'));
396
+ }
198
397
 
199
- getPickerInput = function () {
200
- var input;
398
+ while (currentDate.isBefore(viewDate.clone().endOf('w'))) {
399
+ row.append($('<th>').addClass('dow').text(currentDate.format('dd')));
400
+ currentDate.add(1, 'd');
401
+ }
402
+ widget.find('.datepicker-days thead').append(row);
403
+ },
201
404
 
202
- if (picker.isInput) {
203
- return picker.element;
204
- }
205
- input = picker.element.find('.datepickerinput');
206
- if (input.size() === 0) {
207
- input = picker.element.find('input');
208
- }
209
- else if (!input.is('input')) {
210
- throw new Error('CSS class "datepickerinput" cannot be applied to non input element');
211
- }
212
- return input;
213
- },
405
+ isInDisabledDates = function (date) {
406
+ if (!options.disabledDates) {
407
+ return false;
408
+ }
409
+ return options.disabledDates[date.format('YYYY-MM-DD')] === true;
410
+ },
214
411
 
215
- dataToOptions = function () {
216
- var eData;
217
- if (picker.element.is('input')) {
218
- eData = picker.element.data();
219
- }
220
- else {
221
- eData = picker.element.find('input').data();
222
- }
223
- if (eData.dateFormat !== undefined) {
224
- picker.options.format = eData.dateFormat;
225
- }
226
- if (eData.datePickdate !== undefined) {
227
- picker.options.pickDate = eData.datePickdate;
228
- }
229
- if (eData.datePicktime !== undefined) {
230
- picker.options.pickTime = eData.datePicktime;
231
- }
232
- if (eData.dateUseminutes !== undefined) {
233
- picker.options.useMinutes = eData.dateUseminutes;
234
- }
235
- if (eData.dateUseseconds !== undefined) {
236
- picker.options.useSeconds = eData.dateUseseconds;
237
- }
238
- if (eData.dateUsecurrent !== undefined) {
239
- picker.options.useCurrent = eData.dateUsecurrent;
240
- }
241
- if (eData.calendarWeeks !== undefined) {
242
- picker.options.calendarWeeks = eData.calendarWeeks;
243
- }
244
- if (eData.dateMinutestepping !== undefined) {
245
- picker.options.minuteStepping = eData.dateMinutestepping;
246
- }
247
- if (eData.dateMindate !== undefined) {
248
- picker.options.minDate = eData.dateMindate;
249
- }
250
- if (eData.dateMaxdate !== undefined) {
251
- picker.options.maxDate = eData.dateMaxdate;
252
- }
253
- if (eData.dateShowtoday !== undefined) {
254
- picker.options.showToday = eData.dateShowtoday;
255
- }
256
- if (eData.dateCollapse !== undefined) {
257
- picker.options.collapse = eData.dateCollapse;
258
- }
259
- if (eData.dateLanguage !== undefined) {
260
- picker.options.language = eData.dateLanguage;
261
- }
262
- if (eData.dateDefaultdate !== undefined) {
263
- picker.options.defaultDate = eData.dateDefaultdate;
264
- }
265
- if (eData.dateDisableddates !== undefined) {
266
- picker.options.disabledDates = eData.dateDisableddates;
267
- }
268
- if (eData.dateEnableddates !== undefined) {
269
- picker.options.enabledDates = eData.dateEnableddates;
270
- }
271
- if (eData.dateIcons !== undefined) {
272
- picker.options.icons = eData.dateIcons;
273
- }
274
- if (eData.dateUsestrict !== undefined) {
275
- picker.options.useStrict = eData.dateUsestrict;
276
- }
277
- if (eData.dateDirection !== undefined) {
278
- picker.options.direction = eData.dateDirection;
279
- }
280
- if (eData.dateSidebyside !== undefined) {
281
- picker.options.sideBySide = eData.dateSidebyside;
282
- }
283
- if (eData.dateDaysofweekdisabled !== undefined) {
284
- picker.options.daysOfWeekDisabled = eData.dateDaysofweekdisabled;
285
- }
286
- },
412
+ isInEnabledDates = function (date) {
413
+ if (!options.enabledDates) {
414
+ return false;
415
+ }
416
+ return options.enabledDates[date.format('YYYY-MM-DD')] === true;
417
+ },
287
418
 
288
- place = function () {
289
- var position = 'absolute',
290
- offset = picker.component ? picker.component.offset() : picker.element.offset(),
291
- $window = $(window),
292
- placePosition;
293
-
294
- picker.width = picker.component ? picker.component.outerWidth() : picker.element.outerWidth();
295
- offset.top = offset.top + picker.element.outerHeight();
296
-
297
- if (picker.options.direction === 'up') {
298
- placePosition = 'top';
299
- } else if (picker.options.direction === 'bottom') {
300
- placePosition = 'bottom';
301
- } else if (picker.options.direction === 'auto') {
302
- if (offset.top + picker.widget.height() > $window.height() + $window.scrollTop() && picker.widget.height() + picker.element.outerHeight() < offset.top) {
303
- placePosition = 'top';
304
- } else {
305
- placePosition = 'bottom';
419
+ isValid = function (targetMoment, granularity) {
420
+ if (!targetMoment.isValid()) {
421
+ return false;
306
422
  }
307
- }
308
- if (placePosition === 'top') {
309
- offset.bottom = $window.height() - offset.top + picker.element.outerHeight() + 3;
310
- picker.widget.addClass('top').removeClass('bottom');
311
- } else {
312
- offset.top += 1;
313
- picker.widget.addClass('bottom').removeClass('top');
314
- }
423
+ if (options.disabledDates && isInDisabledDates(targetMoment)) {
424
+ return false;
425
+ }
426
+ if (options.enabledDates && isInEnabledDates(targetMoment)) {
427
+ return true;
428
+ }
429
+ if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) {
430
+ return false;
431
+ }
432
+ if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) {
433
+ return false;
434
+ }
435
+ if (granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) {
436
+ return false;
437
+ }
438
+ return true;
439
+ },
315
440
 
316
- if (picker.options.width !== undefined) {
317
- picker.widget.width(picker.options.width);
318
- }
441
+ fillMonths = function () {
442
+ var spans = [],
443
+ monthsShort = viewDate.clone().startOf('y').hour(12); // hour is changed to avoid DST issues in some browsers
444
+ while (monthsShort.isSame(viewDate, 'y')) {
445
+ spans.push($('<span>').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM')));
446
+ monthsShort.add(1, 'M');
447
+ }
448
+ widget.find('.datepicker-months td').empty().append(spans);
449
+ },
319
450
 
320
- if (picker.options.orientation === 'left') {
321
- picker.widget.addClass('left-oriented');
322
- offset.left = offset.left - picker.widget.width() + 20;
323
- }
451
+ updateMonths = function () {
452
+ var monthsView = widget.find('.datepicker-months'),
453
+ monthsViewHeader = monthsView.find('th'),
454
+ months = monthsView.find('tbody').find('span');
324
455
 
325
- if (isInFixed()) {
326
- position = 'fixed';
327
- offset.top -= $window.scrollTop();
328
- offset.left -= $window.scrollLeft();
329
- }
456
+ monthsView.find('.disabled').removeClass('disabled');
330
457
 
331
- if ($window.width() < offset.left + picker.widget.outerWidth()) {
332
- offset.right = $window.width() - offset.left - picker.width;
333
- offset.left = 'auto';
334
- picker.widget.addClass('pull-right');
335
- } else {
336
- offset.right = 'auto';
337
- picker.widget.removeClass('pull-right');
338
- }
458
+ if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) {
459
+ monthsViewHeader.eq(0).addClass('disabled');
460
+ }
339
461
 
340
- if (placePosition === 'top') {
341
- picker.widget.css({
342
- position: position,
343
- bottom: offset.bottom,
344
- top: 'auto',
345
- left: offset.left,
346
- right: offset.right
347
- });
348
- } else {
349
- picker.widget.css({
350
- position: position,
351
- top: offset.top,
352
- bottom: 'auto',
353
- left: offset.left,
354
- right: offset.right
462
+ monthsViewHeader.eq(1).text(viewDate.year());
463
+
464
+ if (!isValid(viewDate.clone().add(1, 'y'), 'y')) {
465
+ monthsViewHeader.eq(2).addClass('disabled');
466
+ }
467
+
468
+ months.removeClass('active');
469
+ if (date.isSame(viewDate, 'y')) {
470
+ months.eq(date.month()).addClass('active');
471
+ }
472
+
473
+ months.each(function (index) {
474
+ if (!isValid(viewDate.clone().month(index), 'M')) {
475
+ $(this).addClass('disabled');
476
+ }
355
477
  });
356
- }
357
- },
478
+ },
358
479
 
359
- notifyChange = function (oldDate, eventType) {
360
- if (moment(picker.date).isSame(moment(oldDate)) && !errored) {
361
- return;
362
- }
363
- errored = false;
364
- picker.element.trigger({
365
- type: 'dp.change',
366
- date: moment(picker.date),
367
- oldDate: moment(oldDate)
368
- });
480
+ updateYears = function () {
481
+ var yearsView = widget.find('.datepicker-years'),
482
+ yearsViewHeader = yearsView.find('th'),
483
+ startYear = viewDate.clone().subtract(5, 'y'),
484
+ endYear = viewDate.clone().add(6, 'y'),
485
+ html = '';
369
486
 
370
- if (eventType !== 'change') {
371
- picker.element.change();
372
- }
373
- },
487
+ yearsView.find('.disabled').removeClass('disabled');
374
488
 
375
- notifyError = function (date) {
376
- errored = true;
377
- picker.element.trigger({
378
- type: 'dp.error',
379
- date: moment(date, picker.format, picker.options.useStrict)
380
- });
381
- },
489
+ if (options.minDate && options.minDate.isAfter(startYear, 'y')) {
490
+ yearsViewHeader.eq(0).addClass('disabled');
491
+ }
492
+
493
+ yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year());
382
494
 
383
- update = function (newDate) {
384
- moment.locale(picker.options.language);
385
- var dateStr = newDate;
386
- if (!dateStr) {
387
- dateStr = getPickerInput().val();
388
- if (dateStr) {
389
- picker.date = moment(dateStr, picker.format, picker.options.useStrict);
495
+ if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) {
496
+ yearsViewHeader.eq(2).addClass('disabled');
390
497
  }
391
- if (!picker.date) {
392
- picker.date = moment();
498
+
499
+ while (!startYear.isAfter(endYear, 'y')) {
500
+ html += '<span data-action="selectYear" class="year' + (startYear.isSame(date, 'y') ? ' active' : '') + (!isValid(startYear, 'y') ? ' disabled' : '') + '">' + startYear.year() + '</span>';
501
+ startYear.add(1, 'y');
393
502
  }
394
- }
395
- picker.viewDate = moment(picker.date).startOf('month');
396
- fillDate();
397
- fillTime();
398
- },
399
503
 
400
- fillDow = function () {
401
- moment.locale(picker.options.language);
402
- var html = $('<tr>'), weekdaysMin = moment.weekdaysMin(), i;
403
- if (picker.options.calendarWeeks === true) {
404
- html.append('<th class="cw">#</th>');
405
- }
406
- if (moment().localeData()._week.dow === 0) { // starts on Sunday
407
- for (i = 0; i < 7; i++) {
408
- html.append('<th class="dow">' + weekdaysMin[i] + '</th>');
504
+ yearsView.find('td').html(html);
505
+ },
506
+
507
+ fillDate = function () {
508
+ var daysView = widget.find('.datepicker-days'),
509
+ daysViewHeader = daysView.find('th'),
510
+ currentDate,
511
+ html = [],
512
+ row,
513
+ clsName;
514
+
515
+ if (!hasDate()) {
516
+ return;
409
517
  }
410
- } else {
411
- for (i = 1; i < 8; i++) {
412
- if (i === 7) {
413
- html.append('<th class="dow">' + weekdaysMin[0] + '</th>');
414
- } else {
415
- html.append('<th class="dow">' + weekdaysMin[i] + '</th>');
518
+
519
+ daysView.find('.disabled').removeClass('disabled');
520
+ daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat));
521
+
522
+ if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) {
523
+ daysViewHeader.eq(0).addClass('disabled');
524
+ }
525
+ if (!isValid(viewDate.clone().add(1, 'M'), 'M')) {
526
+ daysViewHeader.eq(2).addClass('disabled');
527
+ }
528
+
529
+ currentDate = viewDate.clone().startOf('M').startOf('week');
530
+
531
+ while (!viewDate.clone().endOf('M').endOf('w').isBefore(currentDate, 'd')) {
532
+ if (currentDate.weekday() === 0) {
533
+ row = $('<tr>');
534
+ if (options.calendarWeeks) {
535
+ row.append('<td class="cw">' + currentDate.week() + '</td>');
536
+ }
537
+ html.push(row);
538
+ }
539
+ clsName = '';
540
+ if (currentDate.isBefore(viewDate, 'M')) {
541
+ clsName += ' old';
542
+ }
543
+ if (currentDate.isAfter(viewDate, 'M')) {
544
+ clsName += ' new';
545
+ }
546
+ if (currentDate.isSame(date, 'd') && !unset) {
547
+ clsName += ' active';
548
+ }
549
+ if (!isValid(currentDate, 'd')) {
550
+ clsName += ' disabled';
551
+ }
552
+ if (currentDate.isSame(moment(), 'd')) {
553
+ clsName += ' today';
554
+ }
555
+ if (currentDate.day() === 0 || currentDate.day() === 6) {
556
+ clsName += ' weekend';
416
557
  }
558
+ row.append('<td data-action="selectDay" class="day' + clsName + '">' + currentDate.date() + '</td>');
559
+ currentDate.add(1, 'd');
417
560
  }
418
- }
419
- picker.widget.find('.datepicker-days thead').append(html);
420
- },
421
561
 
422
- fillMonths = function () {
423
- moment.locale(picker.options.language);
424
- var html = '', i, monthsShort = moment.monthsShort();
425
- for (i = 0; i < 12; i++) {
426
- html += '<span class="month">' + monthsShort[i] + '</span>';
427
- }
428
- picker.widget.find('.datepicker-months td').append(html);
429
- },
562
+ daysView.find('tbody').empty().append(html);
430
563
 
431
- fillDate = function () {
432
- if (!picker.options.pickDate) {
433
- return;
434
- }
435
- moment.locale(picker.options.language);
436
- var year = picker.viewDate.year(),
437
- month = picker.viewDate.month(),
438
- startYear = picker.options.minDate.year(),
439
- startMonth = picker.options.minDate.month(),
440
- endYear = picker.options.maxDate.year(),
441
- endMonth = picker.options.maxDate.month(),
442
- currentDate,
443
- prevMonth, nextMonth, html = [], row, clsName, i, days, yearCont, currentYear, months = moment.months();
564
+ updateMonths();
444
565
 
445
- picker.widget.find('.datepicker-days').find('.disabled').removeClass('disabled');
446
- picker.widget.find('.datepicker-months').find('.disabled').removeClass('disabled');
447
- picker.widget.find('.datepicker-years').find('.disabled').removeClass('disabled');
566
+ updateYears();
567
+ },
448
568
 
449
- picker.widget.find('.datepicker-days th:eq(1)').text(
450
- months[month] + ' ' + year);
569
+ fillHours = function () {
570
+ var table = widget.find('.timepicker-hours table'),
571
+ currentHour = viewDate.clone().startOf('d'),
572
+ html = [],
573
+ row = $('<tr>');
451
574
 
452
- prevMonth = moment(picker.viewDate, picker.format, picker.options.useStrict).subtract(1, 'months');
453
- days = prevMonth.daysInMonth();
454
- prevMonth.date(days).startOf('week');
455
- if ((year === startYear && month <= startMonth) || year < startYear) {
456
- picker.widget.find('.datepicker-days th:eq(0)').addClass('disabled');
457
- }
458
- if ((year === endYear && month >= endMonth) || year > endYear) {
459
- picker.widget.find('.datepicker-days th:eq(2)').addClass('disabled');
460
- }
575
+ if (viewDate.hour() > 11 && !use24Hours) {
576
+ currentHour.hour(12);
577
+ }
578
+ while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) {
579
+ if (currentHour.hour() % 4 === 0) {
580
+ row = $('<tr>');
581
+ html.push(row);
582
+ }
583
+ row.append('<td data-action="selectHour" class="hour' + (!isValid(currentHour, 'h') ? ' disabled' : '') + '">' + currentHour.format(use24Hours ? 'HH' : 'hh') + '</td>');
584
+ currentHour.add(1, 'h');
585
+ }
586
+ table.empty().append(html);
587
+ },
461
588
 
462
- nextMonth = moment(prevMonth).add(42, 'd');
463
- while (prevMonth.isBefore(nextMonth)) {
464
- if (prevMonth.weekday() === moment().startOf('week').weekday()) {
589
+ fillMinutes = function () {
590
+ var table = widget.find('.timepicker-minutes table'),
591
+ currentMinute = viewDate.clone().startOf('h'),
592
+ html = [],
593
+ row = $('<tr>'),
594
+ step = options.stepping === 1 ? 5 : options.stepping;
595
+
596
+ while (viewDate.isSame(currentMinute, 'h')) {
597
+ if (currentMinute.minute() % (step * 4) === 0) {
598
+ row = $('<tr>');
599
+ html.push(row);
600
+ }
601
+ row.append('<td data-action="selectMinute" class="minute' + (!isValid(currentMinute, 'm') ? ' disabled' : '') + '">' + currentMinute.format('mm') + '</td>');
602
+ currentMinute.add(step, 'm');
603
+ }
604
+ table.empty().append(html);
605
+ },
606
+
607
+ fillSeconds = function () {
608
+ var table = widget.find('.timepicker-seconds table'),
609
+ currentSecond = viewDate.clone().startOf('m'),
610
+ html = [],
465
611
  row = $('<tr>');
466
- html.push(row);
467
- if (picker.options.calendarWeeks === true) {
468
- row.append('<td class="cw">' + prevMonth.week() + '</td>');
612
+
613
+ while (viewDate.isSame(currentSecond, 'm')) {
614
+ if (currentSecond.second() % 20 === 0) {
615
+ row = $('<tr>');
616
+ html.push(row);
469
617
  }
618
+ row.append('<td data-action="selectSecond" class="second' + (!isValid(currentSecond, 's') ? ' disabled' : '') + '">' + currentSecond.format('ss') + '</td>');
619
+ currentSecond.add(5, 's');
620
+ }
621
+
622
+ table.empty().append(html);
623
+ },
624
+
625
+ fillTime = function () {
626
+ var timeComponents = widget.find('.timepicker span[data-time-component]');
627
+ if (!use24Hours) {
628
+ widget.find('.timepicker [data-action=togglePeriod]').text(date.format('A'));
470
629
  }
471
- clsName = '';
472
- if (prevMonth.year() < year || (prevMonth.year() === year && prevMonth.month() < month)) {
473
- clsName += ' old';
474
- } else if (prevMonth.year() > year || (prevMonth.year() === year && prevMonth.month() > month)) {
475
- clsName += ' new';
630
+ timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh'));
631
+ timeComponents.filter('[data-time-component=minutes]').text(date.format('mm'));
632
+ timeComponents.filter('[data-time-component=seconds]').text(date.format('ss'));
633
+
634
+ fillHours();
635
+ fillMinutes();
636
+ fillSeconds();
637
+ },
638
+
639
+ update = function () {
640
+ if (!widget) {
641
+ return;
476
642
  }
477
- if (prevMonth.isSame(moment({y: picker.date.year(), M: picker.date.month(), d: picker.date.date()}))) {
478
- clsName += ' active';
643
+ fillDate();
644
+ fillTime();
645
+ },
646
+
647
+ setValue = function (targetMoment) {
648
+ var oldDate = unset ? null : date;
649
+
650
+ // case of calling setValue(null or false)
651
+ if (!targetMoment) {
652
+ unset = true;
653
+ input.val('');
654
+ element.data('date', '');
655
+ notifyEvent({
656
+ type: 'dp.change',
657
+ date: null,
658
+ oldDate: oldDate
659
+ });
660
+ update();
661
+ return;
479
662
  }
480
- if (isInDisableDates(prevMonth, 'day') || !isInEnableDates(prevMonth)) {
481
- clsName += ' disabled';
663
+
664
+ targetMoment = targetMoment.clone().locale(options.locale);
665
+
666
+ if (options.stepping !== 1) {
667
+ targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping) % 60).seconds(0);
482
668
  }
483
- if (picker.options.showToday === true) {
484
- if (prevMonth.isSame(moment(), 'day')) {
485
- clsName += ' today';
669
+
670
+ if (isValid(targetMoment)) {
671
+ date = targetMoment;
672
+ viewDate = date.clone();
673
+ input.val(date.format(actualFormat));
674
+ element.data('date', date.format(actualFormat));
675
+ update();
676
+ unset = false;
677
+ notifyEvent({
678
+ type: 'dp.change',
679
+ date: date.clone(),
680
+ oldDate: oldDate
681
+ });
682
+ } else {
683
+ input.val(unset ? '' : date.format(actualFormat));
684
+ notifyEvent({
685
+ type: 'dp.error',
686
+ date: targetMoment
687
+ });
688
+ }
689
+ },
690
+
691
+ hide = function () {
692
+ var transitioning = false;
693
+ if (!widget) {
694
+ return picker;
695
+ }
696
+ // Ignore event if in the middle of a picker transition
697
+ widget.find('.collapse').each(function () {
698
+ var collapseData = $(this).data('collapse');
699
+ if (collapseData && collapseData.transitioning) {
700
+ transitioning = true;
701
+ return false;
702
+ }
703
+ });
704
+ if (transitioning) {
705
+ return picker;
706
+ }
707
+ if (component && component.hasClass('btn')) {
708
+ component.toggleClass('active');
709
+ }
710
+ widget.hide();
711
+
712
+ $(window).off('resize', place);
713
+ widget.off('click', '[data-action]');
714
+ widget.off('mousedown', false);
715
+
716
+ widget.remove();
717
+ widget = false;
718
+
719
+ notifyEvent({
720
+ type: 'dp.hide',
721
+ date: date.clone()
722
+ });
723
+ return picker;
724
+ },
725
+
726
+ /********************************************************************************
727
+ *
728
+ * Widget UI interaction functions
729
+ *
730
+ ********************************************************************************/
731
+ actions = {
732
+ next: function () {
733
+ viewDate.add(datePickerModes[currentViewMode].navStep, datePickerModes[currentViewMode].navFnc);
734
+ fillDate();
735
+ },
736
+
737
+ previous: function () {
738
+ viewDate.subtract(datePickerModes[currentViewMode].navStep, datePickerModes[currentViewMode].navFnc);
739
+ fillDate();
740
+ },
741
+
742
+ pickerSwitch: function () {
743
+ showMode(1);
744
+ },
745
+
746
+ selectMonth: function (e) {
747
+ var month = $(e.target).closest('tbody').find('span').index($(e.target));
748
+ viewDate.month(month);
749
+ if (currentViewMode === minViewModeNumber) {
750
+ setValue(date.clone().year(viewDate.year()).month(viewDate.month()));
751
+ hide();
752
+ }
753
+ showMode(-1);
754
+ fillDate();
755
+ },
756
+
757
+ selectYear: function (e) {
758
+ var year = parseInt($(e.target).text(), 10) || 0;
759
+ viewDate.year(year);
760
+ if (currentViewMode === minViewModeNumber) {
761
+ setValue(date.clone().year(viewDate.year()));
762
+ hide();
763
+ }
764
+ showMode(-1);
765
+ fillDate();
766
+ },
767
+
768
+ selectDay: function (e) {
769
+ var day = viewDate.clone();
770
+ if ($(e.target).is('.old')) {
771
+ day.subtract(1, 'M');
772
+ }
773
+ if ($(e.target).is('.new')) {
774
+ day.add(1, 'M');
775
+ }
776
+ setValue(day.date(parseInt($(e.target).text(), 10)));
777
+ if (!hasTime() && !options.keepOpen) {
778
+ hide();
779
+ }
780
+ },
781
+
782
+ incrementHours: function () {
783
+ setValue(date.clone().add(1, 'h'));
784
+ },
785
+
786
+ incrementMinutes: function () {
787
+ setValue(date.clone().add(options.stepping, 'm'));
788
+ },
789
+
790
+ incrementSeconds: function () {
791
+ setValue(date.clone().add(1, 's'));
792
+ },
793
+
794
+ decrementHours: function () {
795
+ setValue(date.clone().subtract(1, 'h'));
796
+ },
797
+
798
+ decrementMinutes: function () {
799
+ setValue(date.clone().subtract(options.stepping, 'm'));
800
+ },
801
+
802
+ decrementSeconds: function () {
803
+ setValue(date.clone().subtract(1, 's'));
804
+ },
805
+
806
+ togglePeriod: function () {
807
+ setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'));
808
+ },
809
+
810
+ togglePicker: function (e) {
811
+ var $this = $(e.target),
812
+ $parent = $this.closest('ul'),
813
+ expanded = $parent.find('.in'),
814
+ closed = $parent.find('.collapse:not(.in)'),
815
+ collapseData;
816
+
817
+ if (expanded && expanded.length) {
818
+ collapseData = expanded.data('collapse');
819
+ if (collapseData && collapseData.transitioning) {
820
+ return;
821
+ }
822
+ expanded.collapse('hide');
823
+ closed.collapse('show');
824
+ if ($this.is('span')) {
825
+ $this.toggleClass(options.icons.time + ' ' + options.icons.date);
826
+ } else {
827
+ $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
828
+ }
829
+
830
+ // NOTE: uncomment if toggled state will be restored in show()
831
+ //if (component) {
832
+ // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date);
833
+ //}
486
834
  }
835
+ },
836
+
837
+ showPicker: function () {
838
+ widget.find('.timepicker > div:not(.timepicker-picker)').hide();
839
+ widget.find('.timepicker .timepicker-picker').show();
840
+ },
841
+
842
+ showHours: function () {
843
+ widget.find('.timepicker .timepicker-picker').hide();
844
+ widget.find('.timepicker .timepicker-hours').show();
845
+ },
846
+
847
+ showMinutes: function () {
848
+ widget.find('.timepicker .timepicker-picker').hide();
849
+ widget.find('.timepicker .timepicker-minutes').show();
850
+ },
851
+
852
+ showSeconds: function () {
853
+ widget.find('.timepicker .timepicker-picker').hide();
854
+ widget.find('.timepicker .timepicker-seconds').show();
855
+ },
856
+
857
+ selectHour: function (e) {
858
+ var hour = parseInt($(e.target).text(), 10);
859
+
860
+ if (!use24Hours) {
861
+ if (date.hours() >= 12) {
862
+ if (hour !== 12) {
863
+ hour += 12;
864
+ }
865
+ } else {
866
+ if (hour === 12) {
867
+ hour = 0;
868
+ }
869
+ }
870
+ }
871
+ setValue(date.clone().hours(hour));
872
+ actions.showPicker.call(picker);
873
+ },
874
+
875
+ selectMinute: function (e) {
876
+ setValue(date.clone().minutes(parseInt($(e.target).text(), 10)));
877
+ actions.showPicker.call(picker);
878
+ },
879
+
880
+ selectSecond: function (e) {
881
+ setValue(date.clone().seconds(parseInt($(e.target).text(), 10)));
882
+ actions.showPicker.call(picker);
883
+ },
884
+
885
+ clear: function () {
886
+ setValue(null);
887
+ },
888
+
889
+ today: function () {
890
+ setValue(moment());
891
+ }
892
+ },
893
+
894
+ doAction = function (e) {
895
+ if ($(e.currentTarget).is('.disabled')) {
896
+ return false;
487
897
  }
488
- if (picker.options.daysOfWeekDisabled) {
489
- for (i = 0; i < picker.options.daysOfWeekDisabled.length; i++) {
490
- if (prevMonth.day() === picker.options.daysOfWeekDisabled[i]) {
491
- clsName += ' disabled';
492
- break;
898
+ actions[$(e.currentTarget).data('action')].apply(picker, arguments);
899
+ return false;
900
+ },
901
+
902
+ show = function () {
903
+ var currentMoment,
904
+ useCurrentGranularity = {
905
+ 'year': function (m) {
906
+ return m.month(0).date(1).hours(0).seconds(0).minutes(0);
907
+ },
908
+ 'month': function (m) {
909
+ return m.date(1).hours(0).seconds(0).minutes(0);
910
+ },
911
+ 'day': function (m) {
912
+ return m.hours(0).seconds(0).minutes(0);
913
+ },
914
+ 'hour': function (m) {
915
+ return m.seconds(0).minutes(0);
916
+ },
917
+ 'minute': function (m) {
918
+ return m.seconds(0);
493
919
  }
920
+ };
921
+
922
+ if (input.prop('disabled') || input.prop('readonly') || widget) {
923
+ return picker;
924
+ }
925
+ if (options.useCurrent && unset) { // && input.val().trim().length !== 0) { this broke the jasmine test
926
+ currentMoment = moment();
927
+ if (typeof options.useCurrent === 'string') {
928
+ currentMoment = useCurrentGranularity[options.useCurrent](currentMoment);
494
929
  }
930
+ setValue(currentMoment);
495
931
  }
496
- row.append('<td class="day' + clsName + '">' + prevMonth.date() + '</td>');
497
932
 
498
- currentDate = prevMonth.date();
499
- prevMonth.add(1, 'd');
933
+ widget = getTemplate();
934
+
935
+ fillDow();
936
+ fillMonths();
937
+
938
+ widget.find('.timepicker-hours').hide();
939
+ widget.find('.timepicker-minutes').hide();
940
+ widget.find('.timepicker-seconds').hide();
941
+
942
+ update();
943
+ showMode();
944
+
945
+ $(window).on('resize', place);
946
+ widget.on('click', '[data-action]', doAction); // this handles clicks on the widget
947
+ widget.on('mousedown', false);
948
+
949
+ if (component && component.hasClass('btn')) {
950
+ component.toggleClass('active');
951
+ }
952
+ widget.show();
953
+ place();
954
+
955
+ if (!input.is(':focus')) {
956
+ input.focus();
957
+ }
958
+
959
+ notifyEvent({
960
+ type: 'dp.show'
961
+ });
962
+ return picker;
963
+ },
964
+
965
+ toggle = function () {
966
+ return (widget ? hide() : show());
967
+ },
968
+
969
+ parseInputDate = function (date) {
970
+ if (moment.isMoment(date) || date instanceof Date) {
971
+ date = moment(date);
972
+ } else {
973
+ date = moment(date, parseFormats, options.useStrict);
974
+ }
975
+ date.locale(options.locale);
976
+ return date;
977
+ },
978
+
979
+ keydown = function (e) {
980
+ if (e.keyCode === 27) { // allow escape to hide picker
981
+ hide();
982
+ }
983
+ },
984
+
985
+ change = function (e) {
986
+ var val = $(e.target).val().trim(),
987
+ parsedDate = val ? parseInputDate(val) : null;
988
+ setValue(parsedDate);
989
+ e.stopImmediatePropagation();
990
+ return false;
991
+ },
992
+
993
+ attachDatePickerElementEvents = function () {
994
+ input.on({
995
+ 'change': change,
996
+ 'blur': hide,
997
+ 'keydown': keydown
998
+ });
999
+
1000
+ if (element.is('input')) {
1001
+ input.on({
1002
+ 'focus': show
1003
+ });
1004
+ } else if (component) {
1005
+ component.on('click', toggle);
1006
+ component.on('mousedown', false);
1007
+ }
1008
+ },
1009
+
1010
+ detachDatePickerElementEvents = function () {
1011
+ input.off({
1012
+ 'change': change,
1013
+ 'blur': hide,
1014
+ 'keydown': keydown
1015
+ });
1016
+
1017
+ if (element.is('input')) {
1018
+ input.off({
1019
+ 'focus': show
1020
+ });
1021
+ } else if (component) {
1022
+ component.off('click', toggle);
1023
+ component.off('mousedown', false);
1024
+ }
1025
+ },
1026
+
1027
+ indexGivenDates = function (givenDatesArray) {
1028
+ // Store given enabledDates and disabledDates as keys.
1029
+ // This way we can check their existence in O(1) time instead of looping through whole array.
1030
+ // (for example: options.enabledDates['2014-02-27'] === true)
1031
+ var givenDatesIndexed = {};
1032
+ $.each(givenDatesArray, function () {
1033
+ var dDate = parseInputDate(this);
1034
+ if (dDate.isValid()) {
1035
+ givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true;
1036
+ }
1037
+ });
1038
+ return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false;
1039
+ },
1040
+
1041
+ initFormatting = function () {
1042
+ var format = options.format || 'L LT';
1043
+
1044
+ actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (input) {
1045
+ return date.localeData().longDateFormat(input) || input;
1046
+ });
1047
+
1048
+ parseFormats = options.extraFormats ? options.extraFormats.slice() : [];
1049
+ if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) {
1050
+ parseFormats.push(actualFormat);
1051
+ }
1052
+
1053
+ use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.indexOf('h') < 1);
1054
+
1055
+ if (isEnabled('y')) {
1056
+ minViewModeNumber = 2;
1057
+ }
1058
+ if (isEnabled('M')) {
1059
+ minViewModeNumber = 1;
1060
+ }
1061
+ if (isEnabled('d')) {
1062
+ minViewModeNumber = 0;
1063
+ }
1064
+
1065
+ currentViewMode = Math.max(minViewModeNumber, currentViewMode);
1066
+
1067
+ if (!unset) {
1068
+ setValue(date);
1069
+ }
1070
+ };
1071
+
1072
+ /********************************************************************************
1073
+ *
1074
+ * Public API functions
1075
+ * =====================
1076
+ *
1077
+ * Important: Do not expose direct references to private objects or the options
1078
+ * object to the outer world. Always return a clone when returning values or make
1079
+ * a clone when setting a private variable.
1080
+ *
1081
+ ********************************************************************************/
1082
+ picker.destroy = function () {
1083
+ hide();
1084
+ detachDatePickerElementEvents();
1085
+ element.removeData('DateTimePicker');
1086
+ element.removeData('date');
1087
+ };
1088
+
1089
+ picker.toggle = toggle;
1090
+
1091
+ picker.show = show;
1092
+
1093
+ picker.hide = hide;
500
1094
 
501
- if (currentDate === prevMonth.date()) {
502
- prevMonth.add(1, 'd');
503
- }
504
- }
505
- picker.widget.find('.datepicker-days tbody').empty().append(html);
506
- currentYear = picker.date.year();
507
- months = picker.widget.find('.datepicker-months').find('th:eq(1)').text(year).end().find('span').removeClass('active');
508
- if (currentYear === year) {
509
- months.eq(picker.date.month()).addClass('active');
510
- }
511
- if (year - 1 < startYear) {
512
- picker.widget.find('.datepicker-months th:eq(0)').addClass('disabled');
513
- }
514
- if (year + 1 > endYear) {
515
- picker.widget.find('.datepicker-months th:eq(2)').addClass('disabled');
516
- }
517
- for (i = 0; i < 12; i++) {
518
- if ((year === startYear && startMonth > i) || (year < startYear)) {
519
- $(months[i]).addClass('disabled');
520
- } else if ((year === endYear && endMonth < i) || (year > endYear)) {
521
- $(months[i]).addClass('disabled');
522
- }
1095
+ picker.disable = function () {
1096
+ hide();
1097
+ if (component && component.hasClass('btn')) {
1098
+ component.addClass('disabled');
523
1099
  }
1100
+ input.prop('disabled', true);
1101
+ return picker;
1102
+ };
524
1103
 
525
- html = '';
526
- year = parseInt(year / 10, 10) * 10;
527
- yearCont = picker.widget.find('.datepicker-years').find(
528
- 'th:eq(1)').text(year + '-' + (year + 9)).parents('table').find('td');
529
- picker.widget.find('.datepicker-years').find('th').removeClass('disabled');
530
- if (startYear > year) {
531
- picker.widget.find('.datepicker-years').find('th:eq(0)').addClass('disabled');
532
- }
533
- if (endYear < year + 9) {
534
- picker.widget.find('.datepicker-years').find('th:eq(2)').addClass('disabled');
1104
+ picker.enable = function () {
1105
+ if (component && component.hasClass('btn')) {
1106
+ component.removeClass('disabled');
535
1107
  }
536
- year -= 1;
537
- for (i = -1; i < 11; i++) {
538
- html += '<span class="year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + ((year < startYear || year > endYear) ? ' disabled' : '') + '">' + year + '</span>';
539
- year += 1;
1108
+ input.prop('disabled', false);
1109
+ return picker;
1110
+ };
1111
+
1112
+ picker.options = function (newOptions) {
1113
+ if (arguments.length === 0) {
1114
+ return $.extend(true, {}, options);
540
1115
  }
541
- yearCont.html(html);
542
- },
543
1116
 
544
- fillHours = function () {
545
- moment.locale(picker.options.language);
546
- var table = picker.widget.find('.timepicker .timepicker-hours table'), html = '', current, i, j;
547
- table.parent().hide();
548
- if (picker.use24hours) {
549
- current = 0;
550
- for (i = 0; i < 6; i += 1) {
551
- html += '<tr>';
552
- for (j = 0; j < 4; j += 1) {
553
- html += '<td class="hour">' + padLeft(current.toString()) + '</td>';
554
- current++;
555
- }
556
- html += '</tr>';
557
- }
1117
+ if (!(newOptions instanceof Object)) {
1118
+ throw new TypeError('options() options parameter should be an object');
558
1119
  }
559
- else {
560
- current = 1;
561
- for (i = 0; i < 3; i += 1) {
562
- html += '<tr>';
563
- for (j = 0; j < 4; j += 1) {
564
- html += '<td class="hour">' + padLeft(current.toString()) + '</td>';
565
- current++;
566
- }
567
- html += '</tr>';
1120
+ $.extend(true, options, newOptions);
1121
+ $.each(options, function (key, value) {
1122
+ if (picker[key] !== undefined) {
1123
+ picker[key](value);
1124
+ } else {
1125
+ throw new TypeError('option ' + key + ' is not recognized!');
568
1126
  }
569
- }
570
- table.html(html);
571
- },
1127
+ });
1128
+ return picker;
1129
+ };
572
1130
 
573
- fillMinutes = function () {
574
- var table = picker.widget.find('.timepicker .timepicker-minutes table'), html = '', current = 0, i, j, step = picker.options.minuteStepping;
575
- table.parent().hide();
576
- if (step === 1) {
577
- step = 5;
578
- }
579
- for (i = 0; i < Math.ceil(60 / step / 4) ; i++) {
580
- html += '<tr>';
581
- for (j = 0; j < 4; j += 1) {
582
- if (current < 60) {
583
- html += '<td class="minute">' + padLeft(current.toString()) + '</td>';
584
- current += step;
585
- } else {
586
- html += '<td></td>';
587
- }
1131
+ picker.date = function (newDate) {
1132
+ if (arguments.length === 0) {
1133
+ if (unset) {
1134
+ return null;
588
1135
  }
589
- html += '</tr>';
1136
+ return date.clone();
590
1137
  }
591
- table.html(html);
592
- },
593
1138
 
594
- fillSeconds = function () {
595
- var table = picker.widget.find('.timepicker .timepicker-seconds table'), html = '', current = 0, i, j;
596
- table.parent().hide();
597
- for (i = 0; i < 3; i++) {
598
- html += '<tr>';
599
- for (j = 0; j < 4; j += 1) {
600
- html += '<td class="second">' + padLeft(current.toString()) + '</td>';
601
- current += 5;
602
- }
603
- html += '</tr>';
1139
+ if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) {
1140
+ throw new TypeError('date() parameter must be one of [null, string, moment or Date]');
604
1141
  }
605
- table.html(html);
606
- },
607
1142
 
608
- fillTime = function () {
609
- if (!picker.date) {
610
- return;
1143
+ setValue(newDate === null ? null : parseInputDate(newDate));
1144
+ return picker;
1145
+ };
1146
+
1147
+ picker.format = function (newFormat) {
1148
+ if (arguments.length === 0) {
1149
+ return options.format;
611
1150
  }
612
- var timeComponents = picker.widget.find('.timepicker span[data-time-component]'),
613
- hour = picker.date.hours(),
614
- period = picker.date.format('A');
615
- if (!picker.use24hours) {
616
- if (hour === 0) {
617
- hour = 12;
618
- } else if (hour !== 12) {
619
- hour = hour % 12;
620
- }
621
- picker.widget.find('.timepicker [data-action=togglePeriod]').text(period);
1151
+
1152
+ if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) {
1153
+ throw new TypeError('format() expects a sting or boolean:false parameter ' + newFormat);
622
1154
  }
623
- timeComponents.filter('[data-time-component=hours]').text(padLeft(hour));
624
- timeComponents.filter('[data-time-component=minutes]').text(padLeft(picker.date.minutes()));
625
- timeComponents.filter('[data-time-component=seconds]').text(padLeft(picker.date.second()));
626
- },
627
1155
 
628
- click = function (e) {
629
- e.stopPropagation();
630
- e.preventDefault();
631
- picker.unset = false;
632
- var target = $(e.target).closest('span, td, th'), month, year, step, day, oldDate = moment(picker.date);
633
- if (target.length === 1) {
634
- if (!target.is('.disabled')) {
635
- switch (target[0].nodeName.toLowerCase()) {
636
- case 'th':
637
- switch (target[0].className) {
638
- case 'picker-switch':
639
- showMode(1);
640
- break;
641
- case 'prev':
642
- case 'next':
643
- step = dpGlobal.modes[picker.viewMode].navStep;
644
- if (target[0].className === 'prev') {
645
- step = step * -1;
646
- }
647
- picker.viewDate.add(step, dpGlobal.modes[picker.viewMode].navFnc);
648
- fillDate();
649
- break;
650
- }
651
- break;
652
- case 'span':
653
- if (target.is('.month')) {
654
- month = target.parent().find('span').index(target);
655
- picker.viewDate.month(month);
656
- } else {
657
- year = parseInt(target.text(), 10) || 0;
658
- picker.viewDate.year(year);
659
- }
660
- if (picker.viewMode === picker.minViewMode) {
661
- picker.date = moment({
662
- y: picker.viewDate.year(),
663
- M: picker.viewDate.month(),
664
- d: picker.viewDate.date(),
665
- h: picker.date.hours(),
666
- m: picker.date.minutes(),
667
- s: picker.date.seconds()
668
- });
669
- set();
670
- notifyChange(oldDate, e.type);
671
- }
672
- showMode(-1);
673
- fillDate();
674
- break;
675
- case 'td':
676
- if (target.is('.day')) {
677
- day = parseInt(target.text(), 10) || 1;
678
- month = picker.viewDate.month();
679
- year = picker.viewDate.year();
680
- if (target.is('.old')) {
681
- if (month === 0) {
682
- month = 11;
683
- year -= 1;
684
- } else {
685
- month -= 1;
686
- }
687
- } else if (target.is('.new')) {
688
- if (month === 11) {
689
- month = 0;
690
- year += 1;
691
- } else {
692
- month += 1;
693
- }
694
- }
695
- picker.date = moment({
696
- y: year,
697
- M: month,
698
- d: day,
699
- h: picker.date.hours(),
700
- m: picker.date.minutes(),
701
- s: picker.date.seconds()
702
- }
703
- );
704
- picker.viewDate = moment({
705
- y: year, M: month, d: Math.min(28, day)
706
- });
707
- fillDate();
708
- set();
709
- notifyChange(oldDate, e.type);
710
- }
711
- break;
712
- }
713
- }
1156
+ options.format = newFormat;
1157
+ if (actualFormat) {
1158
+ initFormatting(); // reinit formatting
714
1159
  }
715
- },
1160
+ return picker;
1161
+ };
716
1162
 
717
- actions = {
718
- incrementHours: function () {
719
- checkDate('add', 'hours', 1);
720
- },
1163
+ picker.dayViewHeaderFormat = function (newFormat) {
1164
+ if (arguments.length === 0) {
1165
+ return options.dayViewHeaderFormat;
1166
+ }
721
1167
 
722
- incrementMinutes: function () {
723
- checkDate('add', 'minutes', picker.options.minuteStepping);
724
- },
1168
+ if (typeof newFormat !== 'string') {
1169
+ throw new TypeError('dayViewHeaderFormat() expects a string parameter');
1170
+ }
725
1171
 
726
- incrementSeconds: function () {
727
- checkDate('add', 'seconds', 1);
728
- },
1172
+ options.dayViewHeaderFormat = newFormat;
1173
+ return picker;
1174
+ };
729
1175
 
730
- decrementHours: function () {
731
- checkDate('subtract', 'hours', 1);
732
- },
1176
+ picker.extraFormats = function (formats) {
1177
+ if (arguments.length === 0) {
1178
+ return options.extraFormats;
1179
+ }
733
1180
 
734
- decrementMinutes: function () {
735
- checkDate('subtract', 'minutes', picker.options.minuteStepping);
736
- },
1181
+ if (formats !== false && !(formats instanceof Array)) {
1182
+ throw new TypeError('extraFormats() expects an array or false parameter');
1183
+ }
737
1184
 
738
- decrementSeconds: function () {
739
- checkDate('subtract', 'seconds', 1);
740
- },
1185
+ options.extraFormats = formats;
1186
+ if (parseFormats) {
1187
+ initFormatting(); // reinit formatting
1188
+ }
1189
+ return picker;
1190
+ };
741
1191
 
742
- togglePeriod: function () {
743
- var hour = picker.date.hours();
744
- if (hour >= 12) {
745
- hour -= 12;
746
- } else {
747
- hour += 12;
748
- }
749
- picker.date.hours(hour);
750
- },
1192
+ picker.disabledDates = function (dates) {
1193
+ if (arguments.length === 0) {
1194
+ return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates);
1195
+ }
751
1196
 
752
- showPicker: function () {
753
- picker.widget.find('.timepicker > div:not(.timepicker-picker)').hide();
754
- picker.widget.find('.timepicker .timepicker-picker').show();
755
- },
1197
+ if (!dates) {
1198
+ options.disabledDates = false;
1199
+ update();
1200
+ return picker;
1201
+ }
1202
+ if (!(dates instanceof Array)) {
1203
+ throw new TypeError('disabledDates() expects an array parameter');
1204
+ }
1205
+ options.disabledDates = indexGivenDates(dates);
1206
+ options.enabledDates = false;
1207
+ update();
1208
+ return picker;
1209
+ };
756
1210
 
757
- showHours: function () {
758
- picker.widget.find('.timepicker .timepicker-picker').hide();
759
- picker.widget.find('.timepicker .timepicker-hours').show();
760
- },
1211
+ picker.enabledDates = function (dates) {
1212
+ if (arguments.length === 0) {
1213
+ return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates);
1214
+ }
761
1215
 
762
- showMinutes: function () {
763
- picker.widget.find('.timepicker .timepicker-picker').hide();
764
- picker.widget.find('.timepicker .timepicker-minutes').show();
765
- },
1216
+ if (!dates) {
1217
+ options.enabledDates = false;
1218
+ update();
1219
+ return picker;
1220
+ }
1221
+ if (!(dates instanceof Array)) {
1222
+ throw new TypeError('enabledDates() expects an array parameter');
1223
+ }
1224
+ options.enabledDates = indexGivenDates(dates);
1225
+ options.disabledDates = false;
1226
+ update();
1227
+ return picker;
1228
+ };
766
1229
 
767
- showSeconds: function () {
768
- picker.widget.find('.timepicker .timepicker-picker').hide();
769
- picker.widget.find('.timepicker .timepicker-seconds').show();
770
- },
1230
+ picker.daysOfWeekDisabled = function (daysOfWeekDisabled) {
1231
+ if (arguments.length === 0) {
1232
+ return options.daysOfWeekDisabled.splice(0);
1233
+ }
771
1234
 
772
- selectHour: function (e) {
773
- var hour = parseInt($(e.target).text(), 10);
774
- if (!picker.use24hours) {
775
- if (picker.date.hours() >= 12) {
776
- if (hour !== 12) {
777
- hour += 12;
778
- }
779
- } else {
780
- if (hour === 12) {
781
- hour = 0;
782
- }
783
- }
1235
+ if (!(daysOfWeekDisabled instanceof Array)) {
1236
+ throw new TypeError('daysOfWeekDisabled() expects an array parameter');
1237
+ }
1238
+ options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) {
1239
+ currentValue = parseInt(currentValue, 10);
1240
+ if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) {
1241
+ return previousValue;
784
1242
  }
785
- picker.date.hours(hour);
786
- actions.showPicker.call(picker);
787
- },
1243
+ if (previousValue.indexOf(currentValue) === -1) {
1244
+ previousValue.push(currentValue);
1245
+ }
1246
+ return previousValue;
1247
+ }, []).sort();
1248
+ update();
1249
+ return picker;
1250
+ };
788
1251
 
789
- selectMinute: function (e) {
790
- picker.date.minutes(parseInt($(e.target).text(), 10));
791
- actions.showPicker.call(picker);
792
- },
1252
+ picker.maxDate = function (date) {
1253
+ if (arguments.length === 0) {
1254
+ return options.maxDate ? options.maxDate.clone() : options.maxDate;
1255
+ }
793
1256
 
794
- selectSecond: function (e) {
795
- picker.date.seconds(parseInt($(e.target).text(), 10));
796
- actions.showPicker.call(picker);
1257
+ if ((typeof date === 'boolean') && date === false) {
1258
+ options.maxDate = false;
1259
+ update();
1260
+ return picker;
797
1261
  }
798
- },
799
1262
 
800
- doAction = function (e) {
801
- var oldDate = moment(picker.date),
802
- action = $(e.currentTarget).data('action'),
803
- rv = actions[action].apply(picker, arguments);
804
- stopEvent(e);
805
- if (!picker.date) {
806
- picker.date = moment({y: 1970});
807
- }
808
- set();
809
- fillTime();
810
- notifyChange(oldDate, e.type);
811
- return rv;
812
- },
1263
+ var parsedDate = parseInputDate(date);
813
1264
 
814
- stopEvent = function (e) {
815
- e.stopPropagation();
816
- e.preventDefault();
817
- },
1265
+ if (!parsedDate.isValid()) {
1266
+ throw new TypeError('maxDate() Could not parse date parameter: ' + date);
1267
+ }
1268
+ if (options.minDate && parsedDate.isBefore(options.minDate)) {
1269
+ throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat));
1270
+ }
1271
+ options.maxDate = parsedDate;
1272
+ if (options.maxDate.isBefore(date)) {
1273
+ setValue(options.maxDate);
1274
+ }
1275
+ update();
1276
+ return picker;
1277
+ };
818
1278
 
819
- keydown = function (e) {
820
- if (e.keyCode === 27) { // allow escape to hide picker
821
- picker.hide();
1279
+ picker.minDate = function (date) {
1280
+ if (arguments.length === 0) {
1281
+ return options.minDate ? options.minDate.clone() : options.minDate;
822
1282
  }
823
- },
824
1283
 
825
- change = function (e) {
826
- moment.locale(picker.options.language);
827
- var input = $(e.target), oldDate = moment(picker.date), newDate = moment(input.val(), picker.format, picker.options.useStrict);
828
- if (newDate.isValid() && !isInDisableDates(newDate) && isInEnableDates(newDate)) {
1284
+ if ((typeof date === 'boolean') && date === false) {
1285
+ options.minDate = false;
829
1286
  update();
830
- picker.setValue(newDate);
831
- notifyChange(oldDate, e.type);
832
- set();
1287
+ return picker;
1288
+ }
1289
+
1290
+ var parsedDate = parseInputDate(date);
1291
+
1292
+ if (!parsedDate.isValid()) {
1293
+ throw new TypeError('minDate() Could not parse date parameter: ' + date);
833
1294
  }
834
- else {
835
- picker.viewDate = oldDate;
836
- picker.unset = true;
837
- notifyChange(oldDate, e.type);
838
- notifyError(newDate);
1295
+ if (options.maxDate && parsedDate.isAfter(options.maxDate)) {
1296
+ throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat));
839
1297
  }
840
- },
1298
+ options.minDate = parsedDate;
1299
+ if (options.minDate.isAfter(date)) {
1300
+ setValue(options.minDate);
1301
+ }
1302
+ update();
1303
+ return picker;
1304
+ };
841
1305
 
842
- showMode = function (dir) {
843
- if (dir) {
844
- picker.viewMode = Math.max(picker.minViewMode, Math.min(2, picker.viewMode + dir));
1306
+ picker.defaultDate = function (defaultDate) {
1307
+ if (arguments.length === 0) {
1308
+ return options.defaultDate ? options.defaultDate.clone() : options.defaultDate;
1309
+ }
1310
+ if (!defaultDate) {
1311
+ options.defaultDate = false;
1312
+ return picker;
1313
+ }
1314
+ var parsedDate = parseInputDate(defaultDate);
1315
+ if (!parsedDate.isValid()) {
1316
+ throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate);
1317
+ }
1318
+ if (!isValid(parsedDate)) {
1319
+ throw new TypeError('defaultDate() date passed is invalid according to component setup validations');
845
1320
  }
846
- picker.widget.find('.datepicker > div').hide().filter('.datepicker-' + dpGlobal.modes[picker.viewMode].clsName).show();
847
- },
848
1321
 
849
- attachDatePickerEvents = function () {
850
- var $this, $parent, expanded, closed, collapseData;
851
- picker.widget.on('click', '.datepicker *', $.proxy(click, this)); // this handles date picker clicks
852
- picker.widget.on('click', '[data-action]', $.proxy(doAction, this)); // this handles time picker clicks
853
- picker.widget.on('mousedown', $.proxy(stopEvent, this));
854
- picker.element.on('keydown', $.proxy(keydown, this));
855
- if (picker.options.pickDate && picker.options.pickTime) {
856
- picker.widget.on('click.togglePicker', '.accordion-toggle', function (e) {
857
- e.stopPropagation();
858
- $this = $(this);
859
- $parent = $this.closest('ul');
860
- expanded = $parent.find('.in');
861
- closed = $parent.find('.collapse:not(.in)');
1322
+ options.defaultDate = parsedDate;
862
1323
 
863
- if (expanded && expanded.length) {
864
- collapseData = expanded.data('collapse');
865
- if (collapseData && collapseData.transitioning) {
866
- return;
867
- }
868
- expanded.collapse('hide');
869
- closed.collapse('show');
870
- $this.find('span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date);
871
- if (picker.component) {
872
- picker.component.find('span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date);
873
- }
874
- }
875
- });
1324
+ if (options.defaultDate && input.val().trim() === '') {
1325
+ setValue(options.defaultDate);
876
1326
  }
877
- if (picker.isInput) {
878
- picker.element.on({
879
- 'click': $.proxy(picker.show, this),
880
- 'focus': $.proxy(picker.show, this),
881
- 'change': $.proxy(change, this),
882
- 'blur': $.proxy(picker.hide, this)
883
- });
884
- } else {
885
- picker.element.on({
886
- 'change': $.proxy(change, this)
887
- }, 'input');
888
- if (picker.component) {
889
- picker.component.on('click', $.proxy(picker.show, this));
890
- picker.component.on('mousedown', $.proxy(stopEvent, this));
891
- } else {
892
- picker.element.on('click', $.proxy(picker.show, this));
893
- }
1327
+ return picker;
1328
+ };
1329
+
1330
+ picker.locale = function (locale) {
1331
+ if (arguments.length === 0) {
1332
+ return options.locale;
894
1333
  }
895
- },
896
1334
 
897
- attachDatePickerGlobalEvents = function () {
898
- $(window).on(
899
- 'resize.datetimepicker' + picker.id, $.proxy(place, this));
900
- if (!picker.isInput) {
901
- $(document).on(
902
- 'mousedown.datetimepicker' + picker.id, $.proxy(picker.hide, this));
1335
+ if (!moment.localeData(locale)) {
1336
+ throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!');
903
1337
  }
904
- },
905
1338
 
906
- detachDatePickerEvents = function () {
907
- picker.widget.off('click', '.datepicker *', picker.click);
908
- picker.widget.off('click', '[data-action]');
909
- picker.widget.off('mousedown', picker.stopEvent);
910
- if (picker.options.pickDate && picker.options.pickTime) {
911
- picker.widget.off('click.togglePicker');
1339
+ options.locale = locale;
1340
+ date.locale(options.locale);
1341
+ viewDate.locale(options.locale);
1342
+
1343
+ if (actualFormat) {
1344
+ initFormatting(); // reinit formatting
912
1345
  }
913
- if (picker.isInput) {
914
- picker.element.off({
915
- 'focus': picker.show,
916
- 'change': change,
917
- 'click': picker.show,
918
- 'blur' : picker.hide
919
- });
920
- } else {
921
- picker.element.off({
922
- 'change': change
923
- }, 'input');
924
- if (picker.component) {
925
- picker.component.off('click', picker.show);
926
- picker.component.off('mousedown', picker.stopEvent);
927
- } else {
928
- picker.element.off('click', picker.show);
929
- }
1346
+ if (widget) {
1347
+ hide();
1348
+ show();
930
1349
  }
931
- },
1350
+ return picker;
1351
+ };
932
1352
 
933
- detachDatePickerGlobalEvents = function () {
934
- $(window).off('resize.datetimepicker' + picker.id);
935
- if (!picker.isInput) {
936
- $(document).off('mousedown.datetimepicker' + picker.id);
1353
+ picker.stepping = function (stepping) {
1354
+ if (arguments.length === 0) {
1355
+ return options.stepping;
937
1356
  }
938
- },
939
1357
 
940
- isInFixed = function () {
941
- if (picker.element) {
942
- var parents = picker.element.parents(), inFixed = false, i;
943
- for (i = 0; i < parents.length; i++) {
944
- if ($(parents[i]).css('position') === 'fixed') {
945
- inFixed = true;
946
- break;
947
- }
948
- }
949
- return inFixed;
950
- } else {
951
- return false;
1358
+ stepping = parseInt(stepping, 10);
1359
+ if (isNaN(stepping) || stepping < 1) {
1360
+ stepping = 1;
952
1361
  }
953
- },
1362
+ options.stepping = stepping;
1363
+ return picker;
1364
+ };
954
1365
 
955
- set = function () {
956
- moment.locale(picker.options.language);
957
- var formatted = '';
958
- if (!picker.unset) {
959
- formatted = moment(picker.date).format(picker.format);
960
- }
961
- getPickerInput().val(formatted);
962
- picker.element.data('date', formatted);
963
- if (!picker.options.pickTime) {
964
- picker.hide();
1366
+ picker.useCurrent = function (useCurrent) {
1367
+ var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute'];
1368
+ if (arguments.length === 0) {
1369
+ return options.useCurrent;
965
1370
  }
966
- },
967
1371
 
968
- checkDate = function (direction, unit, amount) {
969
- moment.locale(picker.options.language);
970
- var newDate;
971
- if (direction === 'add') {
972
- newDate = moment(picker.date);
973
- if (newDate.hours() === 23) {
974
- newDate.add(amount, unit);
975
- }
976
- newDate.add(amount, unit);
1372
+ if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) {
1373
+ throw new TypeError('useCurrent() expects a boolean or string parameter');
977
1374
  }
978
- else {
979
- newDate = moment(picker.date).subtract(amount, unit);
1375
+ if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) {
1376
+ throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', '));
980
1377
  }
981
- if (isInDisableDates(moment(newDate.subtract(amount, unit))) || isInDisableDates(newDate)) {
982
- notifyError(newDate.format(picker.format));
983
- return;
1378
+ options.useCurrent = useCurrent;
1379
+ return picker;
1380
+ };
1381
+
1382
+ picker.collapse = function (collapse) {
1383
+ if (arguments.length === 0) {
1384
+ return options.collapse;
984
1385
  }
985
1386
 
986
- if (direction === 'add') {
987
- picker.date.add(amount, unit);
1387
+ if (typeof collapse !== 'boolean') {
1388
+ throw new TypeError('collapse() expects a boolean parameter');
988
1389
  }
989
- else {
990
- picker.date.subtract(amount, unit);
1390
+ if (options.collapse === collapse) {
1391
+ return picker;
991
1392
  }
992
- picker.unset = false;
993
- },
1393
+ options.collapse = collapse;
1394
+ if (widget) {
1395
+ hide();
1396
+ show();
1397
+ }
1398
+ return picker;
1399
+ };
994
1400
 
995
- isInDisableDates = function (date, timeUnit) {
996
- moment.locale(picker.options.language);
997
- var maxDate = moment(picker.options.maxDate, picker.format, picker.options.useStrict),
998
- minDate = moment(picker.options.minDate, picker.format, picker.options.useStrict);
1401
+ picker.icons = function (icons) {
1402
+ if (arguments.length === 0) {
1403
+ return $.extend({}, options.icons);
1404
+ }
999
1405
 
1000
- if (timeUnit) {
1001
- maxDate = maxDate.endOf(timeUnit);
1002
- minDate = minDate.startOf(timeUnit);
1406
+ if (!(icons instanceof Object)) {
1407
+ throw new TypeError('icons() expects parameter to be an Object');
1003
1408
  }
1409
+ $.extend(options.icons, icons);
1410
+ if (widget) {
1411
+ hide();
1412
+ show();
1413
+ }
1414
+ return picker;
1415
+ };
1004
1416
 
1005
- if (date.isAfter(maxDate) || date.isBefore(minDate)) {
1006
- return true;
1417
+ picker.useStrict = function (useStrict) {
1418
+ if (arguments.length === 0) {
1419
+ return options.useStrict;
1007
1420
  }
1008
- if (picker.options.disabledDates === false) {
1009
- return false;
1421
+
1422
+ if (typeof useStrict !== 'boolean') {
1423
+ throw new TypeError('useStrict() expects a boolean parameter');
1010
1424
  }
1011
- return picker.options.disabledDates[date.format('YYYY-MM-DD')] === true;
1012
- },
1013
- isInEnableDates = function (date) {
1014
- moment.locale(picker.options.language);
1015
- if (picker.options.enabledDates === false) {
1016
- return true;
1425
+ options.useStrict = useStrict;
1426
+ return picker;
1427
+ };
1428
+
1429
+ picker.sideBySide = function (sideBySide) {
1430
+ if (arguments.length === 0) {
1431
+ return options.sideBySide;
1017
1432
  }
1018
- return picker.options.enabledDates[date.format('YYYY-MM-DD')] === true;
1019
- },
1020
1433
 
1021
- indexGivenDates = function (givenDatesArray) {
1022
- // Store given enabledDates and disabledDates as keys.
1023
- // This way we can check their existence in O(1) time instead of looping through whole array.
1024
- // (for example: picker.options.enabledDates['2014-02-27'] === true)
1025
- var givenDatesIndexed = {}, givenDatesCount = 0, i;
1026
- for (i = 0; i < givenDatesArray.length; i++) {
1027
- if (moment.isMoment(givenDatesArray[i]) || givenDatesArray[i] instanceof Date) {
1028
- dDate = moment(givenDatesArray[i]);
1029
- } else {
1030
- dDate = moment(givenDatesArray[i], picker.format, picker.options.useStrict);
1031
- }
1032
- if (dDate.isValid()) {
1033
- givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true;
1034
- givenDatesCount++;
1035
- }
1434
+ if (typeof sideBySide !== 'boolean') {
1435
+ throw new TypeError('sideBySide() expects a boolean parameter');
1036
1436
  }
1037
- if (givenDatesCount > 0) {
1038
- return givenDatesIndexed;
1437
+ options.sideBySide = sideBySide;
1438
+ if (widget) {
1439
+ hide();
1440
+ show();
1039
1441
  }
1040
- return false;
1041
- },
1442
+ return picker;
1443
+ };
1042
1444
 
1043
- padLeft = function (string) {
1044
- string = string.toString();
1045
- if (string.length >= 2) {
1046
- return string;
1445
+ picker.viewMode = function (newViewMode) {
1446
+ if (arguments.length === 0) {
1447
+ return options.viewMode;
1047
1448
  }
1048
- return '0' + string;
1049
- },
1050
1449
 
1051
- getTemplate = function () {
1052
- var
1053
- headTemplate =
1054
- '<thead>' +
1055
- '<tr>' +
1056
- '<th class="prev">&lsaquo;</th><th colspan="' + (picker.options.calendarWeeks ? '6' : '5') + '" class="picker-switch"></th><th class="next">&rsaquo;</th>' +
1057
- '</tr>' +
1058
- '</thead>',
1059
- contTemplate =
1060
- '<tbody><tr><td colspan="' + (picker.options.calendarWeeks ? '8' : '7') + '"></td></tr></tbody>',
1061
- template = '<div class="datepicker-days">' +
1062
- '<table class="table-condensed">' + headTemplate + '<tbody></tbody></table>' +
1063
- '</div>' +
1064
- '<div class="datepicker-months">' +
1065
- '<table class="table-condensed">' + headTemplate + contTemplate + '</table>' +
1066
- '</div>' +
1067
- '<div class="datepicker-years">' +
1068
- '<table class="table-condensed">' + headTemplate + contTemplate + '</table>' +
1069
- '</div>',
1070
- ret = '';
1071
- if (picker.options.pickDate && picker.options.pickTime) {
1072
- ret = '<div class="bootstrap-datetimepicker-widget' + (picker.options.sideBySide ? ' timepicker-sbs' : '') + (picker.use24hours ? ' usetwentyfour' : '') + ' dropdown-menu" style="z-index:9999 !important;">';
1073
- if (picker.options.sideBySide) {
1074
- ret += '<div class="row">' +
1075
- '<div class="col-sm-6 datepicker">' + template + '</div>' +
1076
- '<div class="col-sm-6 timepicker">' + tpGlobal.getTemplate() + '</div>' +
1077
- '</div>';
1078
- } else {
1079
- ret += '<ul class="list-unstyled">' +
1080
- '<li' + (picker.options.collapse ? ' class="collapse in"' : '') + '>' +
1081
- '<div class="datepicker">' + template + '</div>' +
1082
- '</li>' +
1083
- '<li class="picker-switch accordion-toggle"><a class="btn" style="width:100%"><span class="' + picker.options.icons.time + '"></span></a></li>' +
1084
- '<li' + (picker.options.collapse ? ' class="collapse"' : '') + '>' +
1085
- '<div class="timepicker">' + tpGlobal.getTemplate() + '</div>' +
1086
- '</li>' +
1087
- '</ul>';
1088
- }
1089
- ret += '</div>';
1090
- return ret;
1450
+ if (typeof newViewMode !== 'string') {
1451
+ throw new TypeError('viewMode() expects a string parameter');
1091
1452
  }
1092
- if (picker.options.pickTime) {
1093
- return (
1094
- '<div class="bootstrap-datetimepicker-widget dropdown-menu">' +
1095
- '<div class="timepicker">' + tpGlobal.getTemplate() + '</div>' +
1096
- '</div>'
1097
- );
1098
- }
1099
- return (
1100
- '<div class="bootstrap-datetimepicker-widget dropdown-menu">' +
1101
- '<div class="datepicker">' + template + '</div>' +
1102
- '</div>'
1103
- );
1104
- },
1105
1453
 
1106
- dpGlobal = {
1107
- modes: [
1108
- {
1109
- clsName: 'days',
1110
- navFnc: 'month',
1111
- navStep: 1
1112
- },
1113
- {
1114
- clsName: 'months',
1115
- navFnc: 'year',
1116
- navStep: 1
1117
- },
1118
- {
1119
- clsName: 'years',
1120
- navFnc: 'year',
1121
- navStep: 10
1122
- }
1123
- ]
1124
- },
1454
+ if (viewModes.indexOf(newViewMode) === -1) {
1455
+ throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value');
1456
+ }
1125
1457
 
1126
- tpGlobal = {
1127
- hourTemplate: '<span data-action="showHours" data-time-component="hours" class="timepicker-hour"></span>',
1128
- minuteTemplate: '<span data-action="showMinutes" data-time-component="minutes" class="timepicker-minute"></span>',
1129
- secondTemplate: '<span data-action="showSeconds" data-time-component="seconds" class="timepicker-second"></span>'
1130
- };
1458
+ options.viewMode = newViewMode;
1459
+ currentViewMode = Math.max(viewModes.indexOf(newViewMode), minViewModeNumber);
1131
1460
 
1132
- tpGlobal.getTemplate = function () {
1133
- return (
1134
- '<div class="timepicker-picker">' +
1135
- '<table class="table-condensed">' +
1136
- '<tr>' +
1137
- '<td><a href="#" class="btn" data-action="incrementHours"><span class="' + picker.options.icons.up + '"></span></a></td>' +
1138
- '<td class="separator"></td>' +
1139
- '<td>' + (picker.options.useMinutes ? '<a href="#" class="btn" data-action="incrementMinutes"><span class="' + picker.options.icons.up + '"></span></a>' : '') + '</td>' +
1140
- (picker.options.useSeconds ?
1141
- '<td class="separator"></td><td><a href="#" class="btn" data-action="incrementSeconds"><span class="' + picker.options.icons.up + '"></span></a></td>' : '') +
1142
- (picker.use24hours ? '' : '<td class="separator"></td>') +
1143
- '</tr>' +
1144
- '<tr>' +
1145
- '<td>' + tpGlobal.hourTemplate + '</td> ' +
1146
- '<td class="separator">:</td>' +
1147
- '<td>' + (picker.options.useMinutes ? tpGlobal.minuteTemplate : '<span class="timepicker-minute">00</span>') + '</td> ' +
1148
- (picker.options.useSeconds ?
1149
- '<td class="separator">:</td><td>' + tpGlobal.secondTemplate + '</td>' : '') +
1150
- (picker.use24hours ? '' : '<td class="separator"></td>' +
1151
- '<td><button type="button" class="btn btn-primary" data-action="togglePeriod"></button></td>') +
1152
- '</tr>' +
1153
- '<tr>' +
1154
- '<td><a href="#" class="btn" data-action="decrementHours"><span class="' + picker.options.icons.down + '"></span></a></td>' +
1155
- '<td class="separator"></td>' +
1156
- '<td>' + (picker.options.useMinutes ? '<a href="#" class="btn" data-action="decrementMinutes"><span class="' + picker.options.icons.down + '"></span></a>' : '') + '</td>' +
1157
- (picker.options.useSeconds ?
1158
- '<td class="separator"></td><td><a href="#" class="btn" data-action="decrementSeconds"><span class="' + picker.options.icons.down + '"></span></a></td>' : '') +
1159
- (picker.use24hours ? '' : '<td class="separator"></td>') +
1160
- '</tr>' +
1161
- '</table>' +
1162
- '</div>' +
1163
- '<div class="timepicker-hours" data-action="selectHour">' +
1164
- '<table class="table-condensed"></table>' +
1165
- '</div>' +
1166
- '<div class="timepicker-minutes" data-action="selectMinute">' +
1167
- '<table class="table-condensed"></table>' +
1168
- '</div>' +
1169
- (picker.options.useSeconds ?
1170
- '<div class="timepicker-seconds" data-action="selectSecond"><table class="table-condensed"></table></div>' : '')
1171
- );
1461
+ showMode();
1462
+ return picker;
1172
1463
  };
1173
1464
 
1174
- picker.destroy = function () {
1175
- detachDatePickerEvents();
1176
- detachDatePickerGlobalEvents();
1177
- picker.widget.remove();
1178
- picker.element.removeData('DateTimePicker');
1179
- if (picker.component) {
1180
- picker.component.removeData('DateTimePicker');
1465
+ picker.toolbarPlacement = function (toolbarPlacement) {
1466
+ if (arguments.length === 0) {
1467
+ return options.toolbarPlacement;
1181
1468
  }
1182
- };
1183
1469
 
1184
- picker.show = function (e) {
1185
- if (getPickerInput().prop('disabled')) {
1186
- return;
1187
- }
1188
- if (picker.options.useCurrent) {
1189
- if (getPickerInput().val() === '') {
1190
- if (picker.options.minuteStepping !== 1) {
1191
- var mDate = moment(),
1192
- rInterval = picker.options.minuteStepping;
1193
- mDate.minutes((Math.round(mDate.minutes() / rInterval) * rInterval) % 60).seconds(0);
1194
- picker.setValue(mDate.format(picker.format));
1195
- } else {
1196
- picker.setValue(moment().format(picker.format));
1197
- }
1198
- notifyChange('', e.type);
1199
- }
1470
+ if (typeof toolbarPlacement !== 'string') {
1471
+ throw new TypeError('toolbarPlacement() expects a string parameter');
1200
1472
  }
1201
- // if this is a click event on the input field and picker is already open don't hide it
1202
- if (e && e.type === 'click' && picker.isInput && picker.widget.hasClass('picker-open')) {
1203
- return;
1473
+ if (toolbarPlacements.indexOf(toolbarPlacement) === -1) {
1474
+ throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value');
1204
1475
  }
1205
- if (picker.widget.hasClass('picker-open')) {
1206
- picker.widget.hide();
1207
- picker.widget.removeClass('picker-open');
1476
+ options.toolbarPlacement = toolbarPlacement;
1477
+
1478
+ if (widget) {
1479
+ hide();
1480
+ show();
1208
1481
  }
1209
- else {
1210
- picker.widget.show();
1211
- picker.widget.addClass('picker-open');
1482
+ return picker;
1483
+ };
1484
+
1485
+ picker.widgetPositioning = function (widgetPositioning) {
1486
+ if (arguments.length === 0) {
1487
+ return $.extend({}, options.widgetPositioning);
1212
1488
  }
1213
- picker.height = picker.component ? picker.component.outerHeight() : picker.element.outerHeight();
1214
- place();
1215
- picker.element.trigger({
1216
- type: 'dp.show',
1217
- date: moment(picker.date)
1218
- });
1219
- attachDatePickerGlobalEvents();
1220
- if (e) {
1221
- stopEvent(e);
1489
+
1490
+ if (({}).toString.call(widgetPositioning) !== '[object Object]') {
1491
+ throw new TypeError('widgetPositioning() expects an object variable');
1492
+ }
1493
+ if (widgetPositioning.horizontal) {
1494
+ if (typeof widgetPositioning.horizontal !== 'string') {
1495
+ throw new TypeError('widgetPositioning() horizontal variable must be a string');
1496
+ }
1497
+ widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase();
1498
+ if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) {
1499
+ throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')');
1500
+ }
1501
+ options.widgetPositioning.horizontal = widgetPositioning.horizontal;
1222
1502
  }
1503
+ if (widgetPositioning.vertical) {
1504
+ if (typeof widgetPositioning.vertical !== 'string') {
1505
+ throw new TypeError('widgetPositioning() vertical variable must be a string');
1506
+ }
1507
+ widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase();
1508
+ if (verticalModes.indexOf(widgetPositioning.vertical) === -1) {
1509
+ throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')');
1510
+ }
1511
+ options.widgetPositioning.vertical = widgetPositioning.vertical;
1512
+ }
1513
+ update();
1514
+ return picker;
1223
1515
  };
1224
1516
 
1225
- picker.disable = function () {
1226
- var input = getPickerInput();
1227
- if (input.prop('disabled')) {
1228
- return;
1517
+ picker.calendarWeeks = function (showCalendarWeeks) {
1518
+ if (arguments.length === 0) {
1519
+ return options.calendarWeeks;
1229
1520
  }
1230
- input.prop('disabled', true);
1231
- detachDatePickerEvents();
1232
- };
1233
1521
 
1234
- picker.enable = function () {
1235
- var input = getPickerInput();
1236
- if (!input.prop('disabled')) {
1237
- return;
1522
+ if (typeof showCalendarWeeks !== 'boolean') {
1523
+ throw new TypeError('calendarWeeks() expects parameter to be a boolean value');
1238
1524
  }
1239
- input.prop('disabled', false);
1240
- attachDatePickerEvents();
1525
+
1526
+ options.calendarWeeks = showCalendarWeeks;
1527
+ update();
1528
+ return picker;
1241
1529
  };
1242
1530
 
1243
- picker.hide = function () {
1244
- // Ignore event if in the middle of a picker transition
1245
- var collapse = picker.widget.find('.collapse'), i, collapseData;
1246
- for (i = 0; i < collapse.length; i++) {
1247
- collapseData = collapse.eq(i).data('collapse');
1248
- if (collapseData && collapseData.transitioning) {
1249
- return;
1250
- }
1531
+ picker.showTodayButton = function (showTodayButton) {
1532
+ if (arguments.length === 0) {
1533
+ return options.showTodayButton;
1251
1534
  }
1252
- picker.widget.hide();
1253
- picker.widget.removeClass('picker-open');
1254
- picker.viewMode = picker.startViewMode;
1255
- showMode();
1256
- picker.element.trigger({
1257
- type: 'dp.hide',
1258
- date: moment(picker.date)
1259
- });
1260
- detachDatePickerGlobalEvents();
1261
- };
1262
1535
 
1263
- picker.setValue = function (newDate) {
1264
- moment.locale(picker.options.language);
1265
- if (!newDate) {
1266
- picker.unset = true;
1267
- set();
1268
- } else {
1269
- picker.unset = false;
1536
+ if (typeof showTodayButton !== 'boolean') {
1537
+ throw new TypeError('showTodayButton() expects a boolean parameter');
1270
1538
  }
1271
- if (!moment.isMoment(newDate)) {
1272
- newDate = (newDate instanceof Date) ? moment(newDate) : moment(newDate, picker.format, picker.options.useStrict);
1273
- } else {
1274
- newDate = newDate.locale(picker.options.language);
1539
+
1540
+ options.showTodayButton = showTodayButton;
1541
+ if (widget) {
1542
+ hide();
1543
+ show();
1275
1544
  }
1276
- if (newDate.isValid()) {
1277
- picker.date = newDate;
1278
- set();
1279
- picker.viewDate = moment({y: picker.date.year(), M: picker.date.month()});
1280
- fillDate();
1281
- fillTime();
1545
+ return picker;
1546
+ };
1547
+
1548
+ picker.showClear = function (showClear) {
1549
+ if (arguments.length === 0) {
1550
+ return options.showClear;
1282
1551
  }
1283
- else {
1284
- notifyError(newDate);
1552
+
1553
+ if (typeof showClear !== 'boolean') {
1554
+ throw new TypeError('showClear() expects a boolean parameter');
1285
1555
  }
1286
- };
1287
1556
 
1288
- picker.getDate = function () {
1289
- if (picker.unset) {
1290
- return null;
1557
+ options.showClear = showClear;
1558
+ if (widget) {
1559
+ hide();
1560
+ show();
1291
1561
  }
1292
- return moment(picker.date);
1562
+ return picker;
1293
1563
  };
1294
1564
 
1295
- picker.setDate = function (date) {
1296
- var oldDate = moment(picker.date);
1297
- if (!date) {
1298
- picker.setValue(null);
1299
- } else {
1300
- picker.setValue(date);
1565
+ picker.widgetParent = function (widgetParent) {
1566
+ if (arguments.length === 0) {
1567
+ return options.widgetParent;
1301
1568
  }
1302
- notifyChange(oldDate, 'function');
1303
- };
1304
1569
 
1305
- picker.setDisabledDates = function (dates) {
1306
- picker.options.disabledDates = indexGivenDates(dates);
1307
- if (picker.viewDate) {
1308
- update();
1570
+ if (typeof widgetParent === 'string') {
1571
+ widgetParent = $(widgetParent);
1309
1572
  }
1310
- };
1311
1573
 
1312
- picker.setEnabledDates = function (dates) {
1313
- picker.options.enabledDates = indexGivenDates(dates);
1314
- if (picker.viewDate) {
1315
- update();
1574
+ if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof jQuery))) {
1575
+ throw new TypeError('widgetParent() expects a string or a jQuery object parameter');
1316
1576
  }
1317
- };
1318
1577
 
1319
- picker.setMaxDate = function (date) {
1320
- if (date === undefined) {
1321
- return;
1578
+ options.widgetParent = widgetParent;
1579
+ if (widget) {
1580
+ hide();
1581
+ show();
1322
1582
  }
1323
- if (moment.isMoment(date) || date instanceof Date) {
1324
- picker.options.maxDate = moment(date);
1325
- } else {
1326
- picker.options.maxDate = moment(date, picker.format, picker.options.useStrict);
1583
+ return picker;
1584
+ };
1585
+
1586
+ picker.keepOpen = function (keepOpen) {
1587
+ if (arguments.length === 0) {
1588
+ return options.format;
1327
1589
  }
1328
- if (picker.viewDate) {
1329
- update();
1590
+
1591
+ if (typeof keepOpen !== 'boolean') {
1592
+ throw new TypeError('keepOpen() expects a boolean parameter');
1330
1593
  }
1594
+
1595
+ options.keepOpen = keepOpen;
1596
+ return picker;
1331
1597
  };
1332
1598
 
1333
- picker.setMinDate = function (date) {
1334
- if (date === undefined) {
1335
- return;
1599
+ // initializing element and component attributes
1600
+ if (element.is('input')) {
1601
+ input = element;
1602
+ } else {
1603
+ input = element.find('.datepickerinput');
1604
+ if (input.size() === 0) {
1605
+ input = element.find('input');
1606
+ } else if (!input.is('input')) {
1607
+ throw new Error('CSS class "datepickerinput" cannot be applied to non input element');
1336
1608
  }
1337
- if (moment.isMoment(date) || date instanceof Date) {
1338
- picker.options.minDate = moment(date);
1609
+ }
1610
+
1611
+ if (element.hasClass('input-group')) {
1612
+ // in case there is more then one 'input-group-addon' Issue #48
1613
+ if (element.find('.datepickerbutton').size() === 0) {
1614
+ component = element.find('[class^="input-group-"]');
1339
1615
  } else {
1340
- picker.options.minDate = moment(date, picker.format, picker.options.useStrict);
1341
- }
1342
- if (picker.viewDate) {
1343
- update();
1616
+ component = element.find('.datepickerbutton');
1344
1617
  }
1345
- };
1618
+ }
1619
+
1620
+ if (!input.is('input')) {
1621
+ throw new Error('Could not initialize DateTimePicker without an input element');
1622
+ }
1623
+
1624
+ $.extend(true, options, dataToOptions());
1625
+
1626
+ picker.options(options);
1627
+
1628
+ initFormatting();
1629
+
1630
+ attachDatePickerElementEvents();
1631
+
1632
+ if (input.prop('disabled')) {
1633
+ picker.disable();
1634
+ }
1635
+
1636
+ if (input.val().trim().length !== 0) {
1637
+ setValue(parseInputDate(input.val().trim()));
1638
+ } else if (options.defaultDate) {
1639
+ setValue(options.defaultDate);
1640
+ }
1346
1641
 
1347
- init();
1642
+ return picker;
1348
1643
  };
1349
1644
 
1645
+ /********************************************************************************
1646
+ *
1647
+ * jQuery plugin constructor and defaults object
1648
+ *
1649
+ ********************************************************************************/
1650
+
1350
1651
  $.fn.datetimepicker = function (options) {
1351
1652
  return this.each(function () {
1352
- var $this = $(this),
1353
- data = $this.data('DateTimePicker');
1354
- if (!data) {
1355
- $this.data('DateTimePicker', new DateTimePicker(this, options));
1653
+ var $this = $(this);
1654
+ if (!$this.data('DateTimePicker')) {
1655
+ // create a private copy of the defaults object
1656
+ options = $.extend(true, {}, $.fn.datetimepicker.defaults, options);
1657
+ $this.data('DateTimePicker', dateTimePicker($this, options));
1356
1658
  }
1357
1659
  });
1358
1660
  };
1359
1661
 
1360
1662
  $.fn.datetimepicker.defaults = {
1361
1663
  format: false,
1362
- pickDate: true,
1363
- pickTime: true,
1364
- useMinutes: true,
1365
- useSeconds: false,
1664
+ dayViewHeaderFormat: 'MMMM YYYY',
1665
+ extraFormats: false,
1666
+ stepping: 1,
1667
+ minDate: false,
1668
+ maxDate: false,
1366
1669
  useCurrent: true,
1367
- calendarWeeks: false,
1368
- minuteStepping: 1,
1369
- minDate: moment({y: 1900}),
1370
- maxDate: moment().add(100, 'y'),
1371
- showToday: true,
1372
1670
  collapse: true,
1373
- language: moment.locale(),
1374
- defaultDate: '',
1671
+ locale: moment.locale(),
1672
+ defaultDate: false,
1375
1673
  disabledDates: false,
1376
1674
  enabledDates: false,
1377
- icons: {},
1675
+ icons: {
1676
+ time: 'glyphicon glyphicon-time',
1677
+ date: 'glyphicon glyphicon-calendar',
1678
+ up: 'glyphicon glyphicon-chevron-up',
1679
+ down: 'glyphicon glyphicon-chevron-down',
1680
+ previous: 'glyphicon glyphicon-chevron-left',
1681
+ next: 'glyphicon glyphicon-chevron-right',
1682
+ today: 'glyphicon glyphicon-screenshot',
1683
+ clear: 'glyphicon glyphicon-trash'
1684
+ },
1378
1685
  useStrict: false,
1379
- direction: 'auto',
1380
1686
  sideBySide: false,
1381
1687
  daysOfWeekDisabled: [],
1382
- widgetParent: false
1688
+ calendarWeeks: false,
1689
+ viewMode: 'days',
1690
+ toolbarPlacement: 'default',
1691
+ showTodayButton: false,
1692
+ showClear: false,
1693
+ widgetPositioning: {
1694
+ horizontal: 'auto',
1695
+ vertical: 'auto'
1696
+ },
1697
+ widgetParent: null,
1698
+ keepOpen: false
1383
1699
  };
1384
1700
  }));