bootstrap-timepicker-rails-addon 0.5.1 → 0.5.2

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: d53e7c7b6dc408b23358a61a8a105affdd3a1894
4
- data.tar.gz: fafa6ddbf978c829658e6ff259686b3afd9eb50b
3
+ metadata.gz: 73dc4f56352b35fbb9925588df3ec51854da7f17
4
+ data.tar.gz: 237a487ad4ba951ac198028ff951cc1104a9b185
5
5
  SHA512:
6
- metadata.gz: 0591e472cfe6a3dc622a12e67fb6ea46ce74a22afb0118fd008743ada85f54cfe5c935d2bbaa664526d2960a4b85791208d4f9735b69465763c220441f14d26b
7
- data.tar.gz: d721ffc180649f5c0953fe8542d87b92172dd43d992c5945bfc6833294d67db60df60928720d6785e04deffae2d785e168c33db0cf60e057d3f140157ff9983b
6
+ metadata.gz: 9c47312f2fbf678096bbb410552217107073ec9fef8bbc9b84a5e5b9d694379d9ddf63f599d8a1bf87d975a961e2881d9148c15147d864af482439b2829f300c
7
+ data.tar.gz: 93d96b0642b59adf9002adbe7113383b29bb60b8345d259223ab072ba97204931d00ceb427553318433b5d2e03e50708d366fefdd73706481c6cdce121249ec9
data/LICENSE CHANGED
@@ -1,22 +1,22 @@
1
- Copyright (c) 2013
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ Copyright (c) 2013
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,71 +1,71 @@
1
- # Bootstrap::Timepicker::Rails::Addon
2
- This is the GEMified version of [bootstrap-timepicker](https://github.com/jdewit/bootstrap-timepicker)
3
-
4
- bootstrap-timepicker-rails-addon project integrates Timepicker for Twitter Bootstrap 3 with Rails 3 and 4 assets pipeline.
5
-
6
- ## Installation
7
-
8
- Add this line to your application's Gemfile:
9
-
10
- 1) if you used Bootstrap 3 version,
11
- ```ruby
12
- gem 'bootstrap-sass', '~> 3.3.5'
13
- gem 'bootstrap-timepicker-rails-addon', '~> 0.5.1'
14
- ```
15
-
16
- 2) if you used Bootstrap 2 version,
17
- ```ruby
18
- gem 'bootstrap-sass', '~> 2.3.2.0'
19
- gem 'bootstrap-timepicker-rails-addon', '0.3.0'
20
- ```
21
-
22
- Or you can install from latest build(only support Bootstrap 3 version in master branch):
23
-
24
- ```ruby
25
- gem 'bootstrap-sass', '~> 3.3.5'
26
- gem 'bootstrap-timepicker-rails-addon', :require => 'bootstrap-timepicker-rails-addon',
27
- :git => 'git://github.com/ywjno/bootstrap-timepicker-rails-addon.git'
28
- ```
29
-
30
- And then execute:
31
-
32
- $ bundle
33
-
34
- Or install it yourself as:
35
-
36
- $ gem install bootstrap-timepicker-rails-addon
37
-
38
- ## Usage
39
-
40
- Add this line to app/assets/javascripts/application.js
41
-
42
- //= require bootstrap-timepicker
43
-
44
- Add this line to app/assets/stylesheets/application.css.scss
45
-
46
- @import "bootstrap-timepicker";
47
-
48
- Just call timepicker() with any selector in view.
49
-
50
- ```javascript
51
- $('#timepicker').timepicker();
52
- ```
53
-
54
- And here is the html code sample.
55
-
56
- ```html
57
- <div class="input-group bootstrap-timepicker timepicker">
58
- <input id="timepicker1" type="text" class="form-control input-small">
59
- <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
60
- </div>
61
- ```
62
-
63
- Please view the bootstrap-timepicker <a href="http://jdewit.github.io/bootstrap-timepicker/">demos & documentation</a>.
64
-
65
- ## Contributing
66
-
67
- 1. Fork it
68
- 2. Create your feature branch (`git checkout -b my-new-feature`)
69
- 3. Commit your changes (`git commit -am 'Add some feature'`)
70
- 4. Push to the branch (`git push origin my-new-feature`)
71
- 5. Create new Pull Request
1
+ # Bootstrap::Timepicker::Rails::Addon
2
+ This is the GEMified version of [bootstrap-timepicker](https://github.com/jdewit/bootstrap-timepicker)
3
+
4
+ bootstrap-timepicker-rails-addon project integrates Timepicker for Twitter Bootstrap 3 with Rails 3, 4 and 5 assets pipeline.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ 1) if you used Bootstrap 3 version,
11
+ ```ruby
12
+ gem 'bootstrap-sass', '~> 3.3.7'
13
+ gem 'bootstrap-timepicker-rails-addon', '~> 0.5.1'
14
+ ```
15
+
16
+ 2) if you used Bootstrap 2 version,
17
+ ```ruby
18
+ gem 'bootstrap-sass', '~> 2.3.2.0'
19
+ gem 'bootstrap-timepicker-rails-addon', '0.3.0'
20
+ ```
21
+
22
+ Or you can install from latest build(only support Bootstrap 3 version in master branch):
23
+
24
+ ```ruby
25
+ gem 'bootstrap-sass', '~> 3.3.7'
26
+ gem 'bootstrap-timepicker-rails-addon', :require => 'bootstrap-timepicker-rails-addon',
27
+ :git => 'git://github.com/ywjno/bootstrap-timepicker-rails-addon.git'
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install bootstrap-timepicker-rails-addon
37
+
38
+ ## Usage
39
+
40
+ Add this line to app/assets/javascripts/application.js
41
+
42
+ //= require bootstrap-timepicker
43
+
44
+ Add this line to app/assets/stylesheets/application.css.scss
45
+
46
+ @import "bootstrap-timepicker";
47
+
48
+ Just call timepicker() with any selector in view.
49
+
50
+ ```javascript
51
+ $('#timepicker').timepicker();
52
+ ```
53
+
54
+ And here is the html code sample.
55
+
56
+ ```html
57
+ <div class="input-group bootstrap-timepicker timepicker">
58
+ <input id="timepicker1" type="text" class="form-control input-small">
59
+ <span class="input-group-addon"><i class="glyphicon glyphicon-time"></i></span>
60
+ </div>
61
+ ```
62
+
63
+ Please view the bootstrap-timepicker <a href="http://jdewit.github.io/bootstrap-timepicker/">demos & documentation</a>.
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
@@ -1,12 +1,12 @@
1
- require "bootstrap-timepicker-rails-addon/version"
2
-
3
- module Bootstrap
4
- module Timepicker
5
- module Rails
6
- module Addon
7
- class Engine < ::Rails::Engine
8
- end
9
- end
10
- end
11
- end
12
- end
1
+ require "bootstrap-timepicker-rails-addon/version"
2
+
3
+ module Bootstrap
4
+ module Timepicker
5
+ module Rails
6
+ module Addon
7
+ class Engine < ::Rails::Engine
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,9 +1,9 @@
1
- module Bootstrap
2
- module Timepicker
3
- module Rails
4
- module Addon
5
- VERSION = "0.5.1"
6
- end
7
- end
8
- end
9
- end
1
+ module Bootstrap
2
+ module Timepicker
3
+ module Rails
4
+ module Addon
5
+ VERSION = "0.5.2"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,1169 +1,1177 @@
1
- /*!
2
- * Timepicker Component for Twitter Bootstrap
3
- *
4
- * Copyright 2013 Joris de Wit
5
- *
6
- * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
7
- *
8
- * For the full copyright and license information, please view the LICENSE
9
- * file that was distributed with this source code.
10
- */
11
- (function($, window, document) {
12
- 'use strict';
13
-
14
- // TIMEPICKER PUBLIC CLASS DEFINITION
15
- var Timepicker = function(element, options) {
16
- this.widget = '';
17
- this.$element = $(element);
18
- this.defaultTime = options.defaultTime;
19
- this.disableFocus = options.disableFocus;
20
- this.disableMousewheel = options.disableMousewheel;
21
- this.isOpen = options.isOpen;
22
- this.minuteStep = options.minuteStep;
23
- this.modalBackdrop = options.modalBackdrop;
24
- this.orientation = options.orientation;
25
- this.secondStep = options.secondStep;
26
- this.snapToStep = options.snapToStep;
27
- this.showInputs = options.showInputs;
28
- this.showMeridian = options.showMeridian;
29
- this.showSeconds = options.showSeconds;
30
- this.template = options.template;
31
- this.appendWidgetTo = options.appendWidgetTo;
32
- this.showWidgetOnAddonClick = options.showWidgetOnAddonClick;
33
- this.maxHours = options.maxHours;
34
- this.explicitMode = options.explicitMode; // If true 123 = 1:23, 12345 = 1:23:45, else invalid.
35
-
36
- this.handleDocumentClick = function (e) {
37
- var self = e.data.scope;
38
- // This condition was inspired by bootstrap-datepicker.
39
- // The element the timepicker is invoked on is the input but it has a sibling for addon/button.
40
- if (!(self.$element.parent().find(e.target).length ||
41
- self.$widget.is(e.target) ||
42
- self.$widget.find(e.target).length)) {
43
- self.hideWidget();
44
- }
45
- };
46
- this._init();
47
- };
48
-
49
- Timepicker.prototype = {
50
-
51
- constructor: Timepicker,
52
- _init: function() {
53
- var self = this;
54
-
55
- if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-group') && this.$element.parent().hasClass('bootstrap-timepicker'))) {
56
- this.$element.parent('.input-group.bootstrap-timepicker').find('.input-group-addon').on({
57
- 'click.timepicker': $.proxy(this.showWidget, this)
58
- });
59
- this.$element.on({
60
- 'focus.timepicker': $.proxy(this.highlightUnit, this),
61
- 'click.timepicker': $.proxy(this.highlightUnit, this),
62
- 'keydown.timepicker': $.proxy(this.elementKeydown, this),
63
- 'blur.timepicker': $.proxy(this.blurElement, this),
64
- 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
65
- });
66
- } else {
67
- if (this.template) {
68
- this.$element.on({
69
- 'focus.timepicker': $.proxy(this.showWidget, this),
70
- 'click.timepicker': $.proxy(this.showWidget, this),
71
- 'blur.timepicker': $.proxy(this.blurElement, this),
72
- 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
73
- });
74
- } else {
75
- this.$element.on({
76
- 'focus.timepicker': $.proxy(this.highlightUnit, this),
77
- 'click.timepicker': $.proxy(this.highlightUnit, this),
78
- 'keydown.timepicker': $.proxy(this.elementKeydown, this),
79
- 'blur.timepicker': $.proxy(this.blurElement, this),
80
- 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
81
- });
82
- }
83
- }
84
-
85
- if (this.template !== false) {
86
- this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this));
87
- } else {
88
- this.$widget = false;
89
- }
90
-
91
- if (this.showInputs && this.$widget !== false) {
92
- this.$widget.find('input').each(function() {
93
- $(this).on({
94
- 'click.timepicker': function() { $(this).select(); },
95
- 'keydown.timepicker': $.proxy(self.widgetKeydown, self),
96
- 'keyup.timepicker': $.proxy(self.widgetKeyup, self)
97
- });
98
- });
99
- }
100
-
101
- this.setDefaultTime(this.defaultTime);
102
- },
103
-
104
- blurElement: function() {
105
- this.highlightedUnit = null;
106
- this.updateFromElementVal();
107
- },
108
-
109
- clear: function() {
110
- this.hour = '';
111
- this.minute = '';
112
- this.second = '';
113
- this.meridian = '';
114
-
115
- this.$element.val('');
116
- },
117
-
118
- decrementHour: function() {
119
- if (this.showMeridian) {
120
- if (this.hour === 1) {
121
- this.hour = 12;
122
- } else if (this.hour === 12) {
123
- this.hour--;
124
-
125
- return this.toggleMeridian();
126
- } else if (this.hour === 0) {
127
- this.hour = 11;
128
-
129
- return this.toggleMeridian();
130
- } else {
131
- this.hour--;
132
- }
133
- } else {
134
- if (this.hour <= 0) {
135
- this.hour = this.maxHours - 1;
136
- } else {
137
- this.hour--;
138
- }
139
- }
140
- },
141
-
142
- decrementMinute: function(step) {
143
- var newVal;
144
-
145
- if (step) {
146
- newVal = this.minute - step;
147
- } else {
148
- newVal = this.minute - this.minuteStep;
149
- }
150
-
151
- if (newVal < 0) {
152
- this.decrementHour();
153
- this.minute = newVal + 60;
154
- } else {
155
- this.minute = newVal;
156
- }
157
- },
158
-
159
- decrementSecond: function() {
160
- var newVal = this.second - this.secondStep;
161
-
162
- if (newVal < 0) {
163
- this.decrementMinute(true);
164
- this.second = newVal + 60;
165
- } else {
166
- this.second = newVal;
167
- }
168
- },
169
-
170
- elementKeydown: function(e) {
171
- switch (e.which) {
172
- case 9: //tab
173
- if (e.shiftKey) {
174
- if (this.highlightedUnit === 'hour') {
175
- break;
176
- }
177
- this.highlightPrevUnit();
178
- } else if ((this.showMeridian && this.highlightedUnit === 'meridian') || (this.showSeconds && this.highlightedUnit === 'second') || (!this.showMeridian && !this.showSeconds && this.highlightedUnit ==='minute')) {
179
- break;
180
- } else {
181
- this.highlightNextUnit();
182
- }
183
- e.preventDefault();
184
- this.updateFromElementVal();
185
- break;
186
- case 27: // escape
187
- this.updateFromElementVal();
188
- break;
189
- case 37: // left arrow
190
- e.preventDefault();
191
- this.highlightPrevUnit();
192
- this.updateFromElementVal();
193
- break;
194
- case 38: // up arrow
195
- e.preventDefault();
196
- switch (this.highlightedUnit) {
197
- case 'hour':
198
- this.incrementHour();
199
- this.highlightHour();
200
- break;
201
- case 'minute':
202
- this.incrementMinute();
203
- this.highlightMinute();
204
- break;
205
- case 'second':
206
- this.incrementSecond();
207
- this.highlightSecond();
208
- break;
209
- case 'meridian':
210
- this.toggleMeridian();
211
- this.highlightMeridian();
212
- break;
213
- }
214
- this.update();
215
- break;
216
- case 39: // right arrow
217
- e.preventDefault();
218
- this.highlightNextUnit();
219
- this.updateFromElementVal();
220
- break;
221
- case 40: // down arrow
222
- e.preventDefault();
223
- switch (this.highlightedUnit) {
224
- case 'hour':
225
- this.decrementHour();
226
- this.highlightHour();
227
- break;
228
- case 'minute':
229
- this.decrementMinute();
230
- this.highlightMinute();
231
- break;
232
- case 'second':
233
- this.decrementSecond();
234
- this.highlightSecond();
235
- break;
236
- case 'meridian':
237
- this.toggleMeridian();
238
- this.highlightMeridian();
239
- break;
240
- }
241
-
242
- this.update();
243
- break;
244
- }
245
- },
246
-
247
- getCursorPosition: function() {
248
- var input = this.$element.get(0);
249
-
250
- if ('selectionStart' in input) {// Standard-compliant browsers
251
-
252
- return input.selectionStart;
253
- } else if (document.selection) {// IE fix
254
- input.focus();
255
- var sel = document.selection.createRange(),
256
- selLen = document.selection.createRange().text.length;
257
-
258
- sel.moveStart('character', - input.value.length);
259
-
260
- return sel.text.length - selLen;
261
- }
262
- },
263
-
264
- getTemplate: function() {
265
- var template,
266
- hourTemplate,
267
- minuteTemplate,
268
- secondTemplate,
269
- meridianTemplate,
270
- templateContent;
271
-
272
- if (this.showInputs) {
273
- hourTemplate = '<input type="text" class="bootstrap-timepicker-hour" maxlength="2"/>';
274
- minuteTemplate = '<input type="text" class="bootstrap-timepicker-minute" maxlength="2"/>';
275
- secondTemplate = '<input type="text" class="bootstrap-timepicker-second" maxlength="2"/>';
276
- meridianTemplate = '<input type="text" class="bootstrap-timepicker-meridian" maxlength="2"/>';
277
- } else {
278
- hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
279
- minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
280
- secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
281
- meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
282
- }
283
-
284
- templateContent = '<table>'+
285
- '<tr>'+
286
- '<td><a href="#" data-action="incrementHour"><span class="glyphicon glyphicon-chevron-up"></span></a></td>'+
287
- '<td class="separator">&nbsp;</td>'+
288
- '<td><a href="#" data-action="incrementMinute"><span class="glyphicon glyphicon-chevron-up"></span></a></td>'+
289
- (this.showSeconds ?
290
- '<td class="separator">&nbsp;</td>'+
291
- '<td><a href="#" data-action="incrementSecond"><span class="glyphicon glyphicon-chevron-up"></span></a></td>'
292
- : '') +
293
- (this.showMeridian ?
294
- '<td class="separator">&nbsp;</td>'+
295
- '<td class="meridian-column"><a href="#" data-action="toggleMeridian"><span class="glyphicon glyphicon-chevron-up"></span></a></td>'
296
- : '') +
297
- '</tr>'+
298
- '<tr>'+
299
- '<td>'+ hourTemplate +'</td> '+
300
- '<td class="separator">:</td>'+
301
- '<td>'+ minuteTemplate +'</td> '+
302
- (this.showSeconds ?
303
- '<td class="separator">:</td>'+
304
- '<td>'+ secondTemplate +'</td>'
305
- : '') +
306
- (this.showMeridian ?
307
- '<td class="separator">&nbsp;</td>'+
308
- '<td>'+ meridianTemplate +'</td>'
309
- : '') +
310
- '</tr>'+
311
- '<tr>'+
312
- '<td><a href="#" data-action="decrementHour"><span class="glyphicon glyphicon-chevron-down"></span></a></td>'+
313
- '<td class="separator"></td>'+
314
- '<td><a href="#" data-action="decrementMinute"><span class="glyphicon glyphicon-chevron-down"></span></a></td>'+
315
- (this.showSeconds ?
316
- '<td class="separator">&nbsp;</td>'+
317
- '<td><a href="#" data-action="decrementSecond"><span class="glyphicon glyphicon-chevron-down"></span></a></td>'
318
- : '') +
319
- (this.showMeridian ?
320
- '<td class="separator">&nbsp;</td>'+
321
- '<td><a href="#" data-action="toggleMeridian"><span class="glyphicon glyphicon-chevron-down"></span></a></td>'
322
- : '') +
323
- '</tr>'+
324
- '</table>';
325
-
326
- switch(this.template) {
327
- case 'modal':
328
- template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
329
- '<div class="modal-header">'+
330
- '<a href="#" class="close" data-dismiss="modal">×</a>'+
331
- '<h3>Pick a Time</h3>'+
332
- '</div>'+
333
- '<div class="modal-content">'+
334
- templateContent +
335
- '</div>'+
336
- '<div class="modal-footer">'+
337
- '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
338
- '</div>'+
339
- '</div>';
340
- break;
341
- case 'dropdown':
342
- template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
343
- break;
344
- }
345
-
346
- return template;
347
- },
348
-
349
- getTime: function() {
350
- if (this.hour === '') {
351
- return '';
352
- }
353
-
354
- return this.hour + ':' + (this.minute.toString().length === 1 ? '0' + this.minute : this.minute) + (this.showSeconds ? ':' + (this.second.toString().length === 1 ? '0' + this.second : this.second) : '') + (this.showMeridian ? ' ' + this.meridian : '');
355
- },
356
-
357
- hideWidget: function() {
358
- if (this.isOpen === false) {
359
- return;
360
- }
361
-
362
- this.$element.trigger({
363
- 'type': 'hide.timepicker',
364
- 'time': {
365
- 'value': this.getTime(),
366
- 'hours': this.hour,
367
- 'minutes': this.minute,
368
- 'seconds': this.second,
369
- 'meridian': this.meridian
370
- }
371
- });
372
-
373
- if (this.template === 'modal' && this.$widget.modal) {
374
- this.$widget.modal('hide');
375
- } else {
376
- this.$widget.removeClass('open');
377
- }
378
-
379
- $(document).off('mousedown.timepicker, touchend.timepicker', this.handleDocumentClick);
380
-
381
- this.isOpen = false;
382
- // show/hide approach taken by datepicker
383
- this.$widget.detach();
384
- },
385
-
386
- highlightUnit: function() {
387
- this.position = this.getCursorPosition();
388
- if (this.position >= 0 && this.position <= 2) {
389
- this.highlightHour();
390
- } else if (this.position >= 3 && this.position <= 5) {
391
- this.highlightMinute();
392
- } else if (this.position >= 6 && this.position <= 8) {
393
- if (this.showSeconds) {
394
- this.highlightSecond();
395
- } else {
396
- this.highlightMeridian();
397
- }
398
- } else if (this.position >= 9 && this.position <= 11) {
399
- this.highlightMeridian();
400
- }
401
- },
402
-
403
- highlightNextUnit: function() {
404
- switch (this.highlightedUnit) {
405
- case 'hour':
406
- this.highlightMinute();
407
- break;
408
- case 'minute':
409
- if (this.showSeconds) {
410
- this.highlightSecond();
411
- } else if (this.showMeridian){
412
- this.highlightMeridian();
413
- } else {
414
- this.highlightHour();
415
- }
416
- break;
417
- case 'second':
418
- if (this.showMeridian) {
419
- this.highlightMeridian();
420
- } else {
421
- this.highlightHour();
422
- }
423
- break;
424
- case 'meridian':
425
- this.highlightHour();
426
- break;
427
- }
428
- },
429
-
430
- highlightPrevUnit: function() {
431
- switch (this.highlightedUnit) {
432
- case 'hour':
433
- if(this.showMeridian){
434
- this.highlightMeridian();
435
- } else if (this.showSeconds) {
436
- this.highlightSecond();
437
- } else {
438
- this.highlightMinute();
439
- }
440
- break;
441
- case 'minute':
442
- this.highlightHour();
443
- break;
444
- case 'second':
445
- this.highlightMinute();
446
- break;
447
- case 'meridian':
448
- if (this.showSeconds) {
449
- this.highlightSecond();
450
- } else {
451
- this.highlightMinute();
452
- }
453
- break;
454
- }
455
- },
456
-
457
- highlightHour: function() {
458
- var $element = this.$element.get(0),
459
- self = this;
460
-
461
- this.highlightedUnit = 'hour';
462
-
463
- if ($element.setSelectionRange) {
464
- setTimeout(function() {
465
- if (self.hour < 10) {
466
- $element.setSelectionRange(0,1);
467
- } else {
468
- $element.setSelectionRange(0,2);
469
- }
470
- }, 0);
471
- }
472
- },
473
-
474
- highlightMinute: function() {
475
- var $element = this.$element.get(0),
476
- self = this;
477
-
478
- this.highlightedUnit = 'minute';
479
-
480
- if ($element.setSelectionRange) {
481
- setTimeout(function() {
482
- if (self.hour < 10) {
483
- $element.setSelectionRange(2,4);
484
- } else {
485
- $element.setSelectionRange(3,5);
486
- }
487
- }, 0);
488
- }
489
- },
490
-
491
- highlightSecond: function() {
492
- var $element = this.$element.get(0),
493
- self = this;
494
-
495
- this.highlightedUnit = 'second';
496
-
497
- if ($element.setSelectionRange) {
498
- setTimeout(function() {
499
- if (self.hour < 10) {
500
- $element.setSelectionRange(5,7);
501
- } else {
502
- $element.setSelectionRange(6,8);
503
- }
504
- }, 0);
505
- }
506
- },
507
-
508
- highlightMeridian: function() {
509
- var $element = this.$element.get(0),
510
- self = this;
511
-
512
- this.highlightedUnit = 'meridian';
513
-
514
- if ($element.setSelectionRange) {
515
- if (this.showSeconds) {
516
- setTimeout(function() {
517
- if (self.hour < 10) {
518
- $element.setSelectionRange(8,10);
519
- } else {
520
- $element.setSelectionRange(9,11);
521
- }
522
- }, 0);
523
- } else {
524
- setTimeout(function() {
525
- if (self.hour < 10) {
526
- $element.setSelectionRange(5,7);
527
- } else {
528
- $element.setSelectionRange(6,8);
529
- }
530
- }, 0);
531
- }
532
- }
533
- },
534
-
535
- incrementHour: function() {
536
- if (this.showMeridian) {
537
- if (this.hour === 11) {
538
- this.hour++;
539
- return this.toggleMeridian();
540
- } else if (this.hour === 12) {
541
- this.hour = 0;
542
- }
543
- }
544
- if (this.hour === this.maxHours - 1) {
545
- this.hour = 0;
546
-
547
- return;
548
- }
549
- this.hour++;
550
- },
551
-
552
- incrementMinute: function(step) {
553
- var newVal;
554
-
555
- if (step) {
556
- newVal = this.minute + step;
557
- } else {
558
- newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
559
- }
560
-
561
- if (newVal > 59) {
562
- this.incrementHour();
563
- this.minute = newVal - 60;
564
- } else {
565
- this.minute = newVal;
566
- }
567
- },
568
-
569
- incrementSecond: function() {
570
- var newVal = this.second + this.secondStep - (this.second % this.secondStep);
571
-
572
- if (newVal > 59) {
573
- this.incrementMinute(true);
574
- this.second = newVal - 60;
575
- } else {
576
- this.second = newVal;
577
- }
578
- },
579
-
580
- mousewheel: function(e) {
581
- if (this.disableMousewheel) {
582
- return;
583
- }
584
-
585
- e.preventDefault();
586
- e.stopPropagation();
587
-
588
- var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail,
589
- scrollTo = null;
590
-
591
- if (e.type === 'mousewheel') {
592
- scrollTo = (e.originalEvent.wheelDelta * -1);
593
- }
594
- else if (e.type === 'DOMMouseScroll') {
595
- scrollTo = 40 * e.originalEvent.detail;
596
- }
597
-
598
- if (scrollTo) {
599
- e.preventDefault();
600
- $(this).scrollTop(scrollTo + $(this).scrollTop());
601
- }
602
-
603
- switch (this.highlightedUnit) {
604
- case 'minute':
605
- if (delta > 0) {
606
- this.incrementMinute();
607
- } else {
608
- this.decrementMinute();
609
- }
610
- this.highlightMinute();
611
- break;
612
- case 'second':
613
- if (delta > 0) {
614
- this.incrementSecond();
615
- } else {
616
- this.decrementSecond();
617
- }
618
- this.highlightSecond();
619
- break;
620
- case 'meridian':
621
- this.toggleMeridian();
622
- this.highlightMeridian();
623
- break;
624
- default:
625
- if (delta > 0) {
626
- this.incrementHour();
627
- } else {
628
- this.decrementHour();
629
- }
630
- this.highlightHour();
631
- break;
632
- }
633
-
634
- return false;
635
- },
636
-
637
- /**
638
- * Given a segment value like 43, will round and snap the segment
639
- * to the nearest "step", like 45 if step is 15. Segment will
640
- * "overflow" to 0 if it's larger than 59 or would otherwise
641
- * round up to 60.
642
- */
643
- changeToNearestStep: function (segment, step) {
644
- if (segment % step === 0) {
645
- return segment;
646
- }
647
- if (Math.round((segment % step) / step)) {
648
- return (segment + (step - segment % step)) % 60;
649
- } else {
650
- return segment - segment % step;
651
- }
652
- },
653
-
654
- // This method was adapted from bootstrap-datepicker.
655
- place : function() {
656
- if (this.isInline) {
657
- return;
658
- }
659
- var widgetWidth = this.$widget.outerWidth(), widgetHeight = this.$widget.outerHeight(), visualPadding = 10, windowWidth =
660
- $(window).width(), windowHeight = $(window).height(), scrollTop = $(window).scrollTop();
661
-
662
- var zIndex = parseInt(this.$element.parents().filter(function() { return $(this).css('z-index') !== 'auto'; }).first().css('z-index'), 10) + 10;
663
- var offset = this.component ? this.component.parent().offset() : this.$element.offset();
664
- var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false);
665
- var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false);
666
- var left = offset.left, top = offset.top;
667
-
668
- this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left');
669
-
670
- if (this.orientation.x !== 'auto') {
671
- this.picker.addClass('datepicker-orient-' + this.orientation.x);
672
- if (this.orientation.x === 'right') {
673
- left -= widgetWidth - width;
674
- }
675
- } else{
676
- // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways
677
- // Default to left
678
- this.$widget.addClass('timepicker-orient-left');
679
- if (offset.left < 0) {
680
- left -= offset.left - visualPadding;
681
- } else if (offset.left + widgetWidth > windowWidth) {
682
- left = windowWidth - widgetWidth - visualPadding;
683
- }
684
- }
685
- // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget
686
- var yorient = this.orientation.y, topOverflow, bottomOverflow;
687
- if (yorient === 'auto') {
688
- topOverflow = -scrollTop + offset.top - widgetHeight;
689
- bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight);
690
- if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) {
691
- yorient = 'top';
692
- } else {
693
- yorient = 'bottom';
694
- }
695
- }
696
- this.$widget.addClass('timepicker-orient-' + yorient);
697
- if (yorient === 'top'){
698
- top += height;
699
- } else{
700
- top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10);
701
- }
702
-
703
- this.$widget.css({
704
- top : top,
705
- left : left,
706
- zIndex : zIndex
707
- });
708
- },
709
-
710
- remove: function() {
711
- $('document').off('.timepicker');
712
- if (this.$widget) {
713
- this.$widget.remove();
714
- }
715
- delete this.$element.data().timepicker;
716
- },
717
-
718
- setDefaultTime: function(defaultTime) {
719
- if (!this.$element.val()) {
720
- if (defaultTime === 'current') {
721
- var dTime = new Date(),
722
- hours = dTime.getHours(),
723
- minutes = dTime.getMinutes(),
724
- seconds = dTime.getSeconds(),
725
- meridian = 'AM';
726
-
727
- if (seconds !== 0) {
728
- seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep;
729
- if (seconds === 60) {
730
- minutes += 1;
731
- seconds = 0;
732
- }
733
- }
734
-
735
- if (minutes !== 0) {
736
- minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
737
- if (minutes === 60) {
738
- hours += 1;
739
- minutes = 0;
740
- }
741
- }
742
-
743
- if (this.showMeridian) {
744
- if (hours === 0) {
745
- hours = 12;
746
- } else if (hours >= 12) {
747
- if (hours > 12) {
748
- hours = hours - 12;
749
- }
750
- meridian = 'PM';
751
- } else {
752
- meridian = 'AM';
753
- }
754
- }
755
-
756
- this.hour = hours;
757
- this.minute = minutes;
758
- this.second = seconds;
759
- this.meridian = meridian;
760
-
761
- this.update();
762
-
763
- } else if (defaultTime === false) {
764
- this.hour = 0;
765
- this.minute = 0;
766
- this.second = 0;
767
- this.meridian = 'AM';
768
- } else {
769
- this.setTime(defaultTime);
770
- }
771
- } else {
772
- this.updateFromElementVal();
773
- }
774
- },
775
-
776
- setTime: function(time, ignoreWidget) {
777
- if (!time) {
778
- this.clear();
779
- return;
780
- }
781
-
782
- var timeMode,
783
- timeArray,
784
- hour,
785
- minute,
786
- second,
787
- meridian;
788
-
789
- if (typeof time === 'object' && time.getMonth){
790
- // this is a date object
791
- hour = time.getHours();
792
- minute = time.getMinutes();
793
- second = time.getSeconds();
794
-
795
- if (this.showMeridian){
796
- meridian = 'AM';
797
- if (hour > 12){
798
- meridian = 'PM';
799
- hour = hour % 12;
800
- }
801
-
802
- if (hour === 12){
803
- meridian = 'PM';
804
- }
805
- }
806
- } else {
807
- timeMode = ((/a/i).test(time) ? 1 : 0) + ((/p/i).test(time) ? 2 : 0); // 0 = none, 1 = AM, 2 = PM, 3 = BOTH.
808
- if (timeMode > 2) { // If both are present, fail.
809
- this.clear();
810
- return;
811
- }
812
-
813
- timeArray = time.replace(/[^0-9\:]/g, '').split(':');
814
-
815
- hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
816
-
817
- if(this.explicitMode && hour.length > 2 && (hour.length % 2) !== 0 ) {
818
- this.clear();
819
- return;
820
- }
821
-
822
- minute = timeArray[1] ? timeArray[1].toString() : '';
823
- second = timeArray[2] ? timeArray[2].toString() : '';
824
-
825
- // adaptive time parsing
826
- if (hour.length > 4) {
827
- second = hour.slice(-2);
828
- hour = hour.slice(0, -2);
829
- }
830
-
831
- if (hour.length > 2) {
832
- minute = hour.slice(-2);
833
- hour = hour.slice(0, -2);
834
- }
835
-
836
- if (minute.length > 2) {
837
- second = minute.slice(-2);
838
- minute = minute.slice(0, -2);
839
- }
840
-
841
- hour = parseInt(hour, 10);
842
- minute = parseInt(minute, 10);
843
- second = parseInt(second, 10);
844
-
845
- if (isNaN(hour)) {
846
- hour = 0;
847
- }
848
- if (isNaN(minute)) {
849
- minute = 0;
850
- }
851
- if (isNaN(second)) {
852
- second = 0;
853
- }
854
-
855
- // Adjust the time based upon unit boundary.
856
- // NOTE: Negatives will never occur due to time.replace() above.
857
- if (second > 59) {
858
- second = 59;
859
- }
860
-
861
- if (minute > 59) {
862
- minute = 59;
863
- }
864
-
865
- if (hour >= this.maxHours) {
866
- // No day/date handling.
867
- hour = this.maxHours - 1;
868
- }
869
-
870
- if (this.showMeridian) {
871
- if (hour > 12) {
872
- // Force PM.
873
- timeMode = 2;
874
- hour -= 12;
875
- }
876
- if (!timeMode) {
877
- timeMode = 1;
878
- }
879
- if (hour === 0) {
880
- hour = 12; // AM or PM, reset to 12. 0 AM = 12 AM. 0 PM = 12 PM, etc.
881
- }
882
- meridian = timeMode === 1 ? 'AM' : 'PM';
883
- } else if (hour < 12 && timeMode === 2) {
884
- hour += 12;
885
- } else {
886
- if (hour >= this.maxHours) {
887
- hour = this.maxHours - 1;
888
- } else if (hour < 0) {
889
- hour = 0;
890
- }
891
- }
892
- }
893
-
894
- this.hour = hour;
895
- if (this.snapToStep) {
896
- this.minute = this.changeToNearestStep(minute, this.minuteStep);
897
- this.second = this.changeToNearestStep(second, this.secondStep);
898
- } else {
899
- this.minute = minute;
900
- this.second = second;
901
- }
902
- this.meridian = meridian;
903
-
904
- this.update(ignoreWidget);
905
- },
906
-
907
- showWidget: function() {
908
- if (this.isOpen) {
909
- return;
910
- }
911
-
912
- if (this.$element.is(':disabled')) {
913
- return;
914
- }
915
-
916
- // show/hide approach taken by datepicker
917
- this.$widget.appendTo(this.appendWidgetTo);
918
- $(document).on('mousedown.timepicker, touchend.timepicker', {scope: this}, this.handleDocumentClick);
919
-
920
- this.$element.trigger({
921
- 'type': 'show.timepicker',
922
- 'time': {
923
- 'value': this.getTime(),
924
- 'hours': this.hour,
925
- 'minutes': this.minute,
926
- 'seconds': this.second,
927
- 'meridian': this.meridian
928
- }
929
- });
930
-
931
- this.place();
932
- if (this.disableFocus) {
933
- this.$element.blur();
934
- }
935
-
936
- // widget shouldn't be empty on open
937
- if (this.hour === '') {
938
- if (this.defaultTime) {
939
- this.setDefaultTime(this.defaultTime);
940
- } else {
941
- this.setTime('0:0:0');
942
- }
943
- }
944
-
945
- if (this.template === 'modal' && this.$widget.modal) {
946
- this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
947
- } else {
948
- if (this.isOpen === false) {
949
- this.$widget.addClass('open');
950
- }
951
- }
952
-
953
- this.isOpen = true;
954
- },
955
-
956
- toggleMeridian: function() {
957
- this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
958
- },
959
-
960
- update: function(ignoreWidget) {
961
- this.updateElement();
962
- if (!ignoreWidget) {
963
- this.updateWidget();
964
- }
965
-
966
- this.$element.trigger({
967
- 'type': 'changeTime.timepicker',
968
- 'time': {
969
- 'value': this.getTime(),
970
- 'hours': this.hour,
971
- 'minutes': this.minute,
972
- 'seconds': this.second,
973
- 'meridian': this.meridian
974
- }
975
- });
976
- },
977
-
978
- updateElement: function() {
979
- this.$element.val(this.getTime()).change();
980
- },
981
-
982
- updateFromElementVal: function() {
983
- this.setTime(this.$element.val());
984
- },
985
-
986
- updateWidget: function() {
987
- if (this.$widget === false) {
988
- return;
989
- }
990
-
991
- var hour = this.hour,
992
- minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute,
993
- second = this.second.toString().length === 1 ? '0' + this.second : this.second;
994
-
995
- if (this.showInputs) {
996
- this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
997
- this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
998
-
999
- if (this.showSeconds) {
1000
- this.$widget.find('input.bootstrap-timepicker-second').val(second);
1001
- }
1002
- if (this.showMeridian) {
1003
- this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
1004
- }
1005
- } else {
1006
- this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
1007
- this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
1008
-
1009
- if (this.showSeconds) {
1010
- this.$widget.find('span.bootstrap-timepicker-second').text(second);
1011
- }
1012
- if (this.showMeridian) {
1013
- this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
1014
- }
1015
- }
1016
- },
1017
-
1018
- updateFromWidgetInputs: function() {
1019
- if (this.$widget === false) {
1020
- return;
1021
- }
1022
-
1023
- var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' +
1024
- this.$widget.find('input.bootstrap-timepicker-minute').val() +
1025
- (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') +
1026
- (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '')
1027
- ;
1028
-
1029
- this.setTime(t, true);
1030
- },
1031
-
1032
- widgetClick: function(e) {
1033
- e.stopPropagation();
1034
- e.preventDefault();
1035
-
1036
- var $input = $(e.target),
1037
- action = $input.closest('a').data('action');
1038
-
1039
- if (action) {
1040
- this[action]();
1041
- }
1042
- this.update();
1043
-
1044
- if ($input.is('input')) {
1045
- $input.get(0).setSelectionRange(0,2);
1046
- }
1047
- },
1048
-
1049
- widgetKeydown: function(e) {
1050
- var $input = $(e.target),
1051
- name = $input.attr('class').replace('bootstrap-timepicker-', '');
1052
-
1053
- switch (e.which) {
1054
- case 9: //tab
1055
- if (e.shiftKey) {
1056
- if (name === 'hour') {
1057
- return this.hideWidget();
1058
- }
1059
- } else if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) {
1060
- return this.hideWidget();
1061
- }
1062
- break;
1063
- case 27: // escape
1064
- this.hideWidget();
1065
- break;
1066
- case 38: // up arrow
1067
- e.preventDefault();
1068
- switch (name) {
1069
- case 'hour':
1070
- this.incrementHour();
1071
- break;
1072
- case 'minute':
1073
- this.incrementMinute();
1074
- break;
1075
- case 'second':
1076
- this.incrementSecond();
1077
- break;
1078
- case 'meridian':
1079
- this.toggleMeridian();
1080
- break;
1081
- }
1082
- this.setTime(this.getTime());
1083
- $input.get(0).setSelectionRange(0,2);
1084
- break;
1085
- case 40: // down arrow
1086
- e.preventDefault();
1087
- switch (name) {
1088
- case 'hour':
1089
- this.decrementHour();
1090
- break;
1091
- case 'minute':
1092
- this.decrementMinute();
1093
- break;
1094
- case 'second':
1095
- this.decrementSecond();
1096
- break;
1097
- case 'meridian':
1098
- this.toggleMeridian();
1099
- break;
1100
- }
1101
- this.setTime(this.getTime());
1102
- $input.get(0).setSelectionRange(0,2);
1103
- break;
1104
- }
1105
- },
1106
-
1107
- widgetKeyup: function(e) {
1108
- if ((e.which === 65) || (e.which === 77) || (e.which === 80) || (e.which === 46) || (e.which === 8) || (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105)) {
1109
- this.updateFromWidgetInputs();
1110
- }
1111
- }
1112
- };
1113
-
1114
- //TIMEPICKER PLUGIN DEFINITION
1115
- $.fn.timepicker = function(option) {
1116
- var args = Array.apply(null, arguments);
1117
- args.shift();
1118
- return this.each(function() {
1119
- var $this = $(this),
1120
- data = $this.data('timepicker'),
1121
- options = typeof option === 'object' && option;
1122
-
1123
- if (!data) {
1124
- $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
1125
- }
1126
-
1127
- if (typeof option === 'string') {
1128
- data[option].apply(data, args);
1129
- }
1130
- });
1131
- };
1132
-
1133
- $.fn.timepicker.defaults = {
1134
- defaultTime: 'current',
1135
- disableFocus: false,
1136
- disableMousewheel: false,
1137
- isOpen: false,
1138
- minuteStep: 15,
1139
- modalBackdrop: false,
1140
- orientation: { x: 'auto', y: 'auto'},
1141
- secondStep: 15,
1142
- snapToStep: false,
1143
- showSeconds: false,
1144
- showInputs: true,
1145
- showMeridian: true,
1146
- template: 'dropdown',
1147
- appendWidgetTo: 'body',
1148
- showWidgetOnAddonClick: true,
1149
- maxHours: 24,
1150
- explicitMode: false
1151
- };
1152
-
1153
- $.fn.timepicker.Constructor = Timepicker;
1154
-
1155
- $(document).on(
1156
- 'focus.timepicker.data-api click.timepicker.data-api',
1157
- '[data-provide="timepicker"]',
1158
- function(e){
1159
- var $this = $(this);
1160
- if ($this.data('timepicker')) {
1161
- return;
1162
- }
1163
- e.preventDefault();
1164
- // component click requires us to explicitly show it
1165
- $this.timepicker();
1166
- }
1167
- );
1168
-
1169
- })(jQuery, window, document);
1
+ /*!
2
+ * Timepicker Component for Twitter Bootstrap
3
+ *
4
+ * Copyright 2013 Joris de Wit and bootstrap-timepicker contributors
5
+ *
6
+ * Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+ (function($, window, document) {
12
+ 'use strict';
13
+
14
+ // TIMEPICKER PUBLIC CLASS DEFINITION
15
+ var Timepicker = function(element, options) {
16
+ this.widget = '';
17
+ this.$element = $(element);
18
+ this.defaultTime = options.defaultTime;
19
+ this.disableFocus = options.disableFocus;
20
+ this.disableMousewheel = options.disableMousewheel;
21
+ this.isOpen = options.isOpen;
22
+ this.minuteStep = options.minuteStep;
23
+ this.modalBackdrop = options.modalBackdrop;
24
+ this.orientation = options.orientation;
25
+ this.secondStep = options.secondStep;
26
+ this.snapToStep = options.snapToStep;
27
+ this.showInputs = options.showInputs;
28
+ this.showMeridian = options.showMeridian;
29
+ this.showSeconds = options.showSeconds;
30
+ this.template = options.template;
31
+ this.appendWidgetTo = options.appendWidgetTo;
32
+ this.showWidgetOnAddonClick = options.showWidgetOnAddonClick;
33
+ this.icons = options.icons;
34
+ this.maxHours = options.maxHours;
35
+ this.explicitMode = options.explicitMode; // If true 123 = 1:23, 12345 = 1:23:45, else invalid.
36
+
37
+ this.handleDocumentClick = function (e) {
38
+ var self = e.data.scope;
39
+ // This condition was inspired by bootstrap-datepicker.
40
+ // The element the timepicker is invoked on is the input but it has a sibling for addon/button.
41
+ if (!(self.$element.parent().find(e.target).length ||
42
+ self.$widget.is(e.target) ||
43
+ self.$widget.find(e.target).length)) {
44
+ self.hideWidget();
45
+ }
46
+ };
47
+
48
+ this._init();
49
+ };
50
+
51
+ Timepicker.prototype = {
52
+
53
+ constructor: Timepicker,
54
+ _init: function() {
55
+ var self = this;
56
+
57
+ if (this.showWidgetOnAddonClick && (this.$element.parent().hasClass('input-group') && this.$element.parent().hasClass('bootstrap-timepicker'))) {
58
+ this.$element.parent('.input-group.bootstrap-timepicker').find('.input-group-addon').on({
59
+ 'click.timepicker': $.proxy(this.showWidget, this)
60
+ });
61
+ this.$element.on({
62
+ 'focus.timepicker': $.proxy(this.highlightUnit, this),
63
+ 'click.timepicker': $.proxy(this.highlightUnit, this),
64
+ 'keydown.timepicker': $.proxy(this.elementKeydown, this),
65
+ 'blur.timepicker': $.proxy(this.blurElement, this),
66
+ 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
67
+ });
68
+ } else {
69
+ if (this.template) {
70
+ this.$element.on({
71
+ 'focus.timepicker': $.proxy(this.showWidget, this),
72
+ 'click.timepicker': $.proxy(this.showWidget, this),
73
+ 'blur.timepicker': $.proxy(this.blurElement, this),
74
+ 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
75
+ });
76
+ } else {
77
+ this.$element.on({
78
+ 'focus.timepicker': $.proxy(this.highlightUnit, this),
79
+ 'click.timepicker': $.proxy(this.highlightUnit, this),
80
+ 'keydown.timepicker': $.proxy(this.elementKeydown, this),
81
+ 'blur.timepicker': $.proxy(this.blurElement, this),
82
+ 'mousewheel.timepicker DOMMouseScroll.timepicker': $.proxy(this.mousewheel, this)
83
+ });
84
+ }
85
+ }
86
+
87
+ if (this.template !== false) {
88
+ this.$widget = $(this.getTemplate()).on('click', $.proxy(this.widgetClick, this));
89
+ } else {
90
+ this.$widget = false;
91
+ }
92
+
93
+ if (this.showInputs && this.$widget !== false) {
94
+ this.$widget.find('input').each(function() {
95
+ $(this).on({
96
+ 'click.timepicker': function() { $(this).select(); },
97
+ 'keydown.timepicker': $.proxy(self.widgetKeydown, self),
98
+ 'keyup.timepicker': $.proxy(self.widgetKeyup, self)
99
+ });
100
+ });
101
+ }
102
+
103
+ this.setDefaultTime(this.defaultTime);
104
+ },
105
+
106
+ blurElement: function() {
107
+ this.highlightedUnit = null;
108
+ this.updateFromElementVal();
109
+ },
110
+
111
+ clear: function() {
112
+ this.hour = '';
113
+ this.minute = '';
114
+ this.second = '';
115
+ this.meridian = '';
116
+
117
+ this.$element.val('');
118
+ },
119
+
120
+ decrementHour: function() {
121
+ if (this.showMeridian) {
122
+ if (this.hour === 1) {
123
+ this.hour = 12;
124
+ } else if (this.hour === 12) {
125
+ this.hour--;
126
+
127
+ return this.toggleMeridian();
128
+ } else if (this.hour === 0) {
129
+ this.hour = 11;
130
+
131
+ return this.toggleMeridian();
132
+ } else {
133
+ this.hour--;
134
+ }
135
+ } else {
136
+ if (this.hour <= 0) {
137
+ this.hour = this.maxHours - 1;
138
+ } else {
139
+ this.hour--;
140
+ }
141
+ }
142
+ },
143
+
144
+ decrementMinute: function(step) {
145
+ var newVal;
146
+
147
+ if (step) {
148
+ newVal = this.minute - step;
149
+ } else {
150
+ newVal = this.minute - this.minuteStep;
151
+ }
152
+
153
+ if (newVal < 0) {
154
+ this.decrementHour();
155
+ this.minute = newVal + 60;
156
+ } else {
157
+ this.minute = newVal;
158
+ }
159
+ },
160
+
161
+ decrementSecond: function() {
162
+ var newVal = this.second - this.secondStep;
163
+
164
+ if (newVal < 0) {
165
+ this.decrementMinute(true);
166
+ this.second = newVal + 60;
167
+ } else {
168
+ this.second = newVal;
169
+ }
170
+ },
171
+
172
+ elementKeydown: function(e) {
173
+ switch (e.which) {
174
+ case 9: //tab
175
+ if (e.shiftKey) {
176
+ if (this.highlightedUnit === 'hour') {
177
+ this.hideWidget();
178
+ break;
179
+ }
180
+ this.highlightPrevUnit();
181
+ } else if ((this.showMeridian && this.highlightedUnit === 'meridian') || (this.showSeconds && this.highlightedUnit === 'second') || (!this.showMeridian && !this.showSeconds && this.highlightedUnit ==='minute')) {
182
+ this.hideWidget();
183
+ break;
184
+ } else {
185
+ this.highlightNextUnit();
186
+ }
187
+ e.preventDefault();
188
+ this.updateFromElementVal();
189
+ break;
190
+ case 27: // escape
191
+ this.updateFromElementVal();
192
+ break;
193
+ case 37: // left arrow
194
+ e.preventDefault();
195
+ this.highlightPrevUnit();
196
+ this.updateFromElementVal();
197
+ break;
198
+ case 38: // up arrow
199
+ e.preventDefault();
200
+ switch (this.highlightedUnit) {
201
+ case 'hour':
202
+ this.incrementHour();
203
+ this.highlightHour();
204
+ break;
205
+ case 'minute':
206
+ this.incrementMinute();
207
+ this.highlightMinute();
208
+ break;
209
+ case 'second':
210
+ this.incrementSecond();
211
+ this.highlightSecond();
212
+ break;
213
+ case 'meridian':
214
+ this.toggleMeridian();
215
+ this.highlightMeridian();
216
+ break;
217
+ }
218
+ this.update();
219
+ break;
220
+ case 39: // right arrow
221
+ e.preventDefault();
222
+ this.highlightNextUnit();
223
+ this.updateFromElementVal();
224
+ break;
225
+ case 40: // down arrow
226
+ e.preventDefault();
227
+ switch (this.highlightedUnit) {
228
+ case 'hour':
229
+ this.decrementHour();
230
+ this.highlightHour();
231
+ break;
232
+ case 'minute':
233
+ this.decrementMinute();
234
+ this.highlightMinute();
235
+ break;
236
+ case 'second':
237
+ this.decrementSecond();
238
+ this.highlightSecond();
239
+ break;
240
+ case 'meridian':
241
+ this.toggleMeridian();
242
+ this.highlightMeridian();
243
+ break;
244
+ }
245
+
246
+ this.update();
247
+ break;
248
+ }
249
+ },
250
+
251
+ getCursorPosition: function() {
252
+ var input = this.$element.get(0);
253
+
254
+ if ('selectionStart' in input) {// Standard-compliant browsers
255
+
256
+ return input.selectionStart;
257
+ } else if (document.selection) {// IE fix
258
+ input.focus();
259
+ var sel = document.selection.createRange(),
260
+ selLen = document.selection.createRange().text.length;
261
+
262
+ sel.moveStart('character', - input.value.length);
263
+
264
+ return sel.text.length - selLen;
265
+ }
266
+ },
267
+
268
+ getTemplate: function() {
269
+ var template,
270
+ hourTemplate,
271
+ minuteTemplate,
272
+ secondTemplate,
273
+ meridianTemplate,
274
+ templateContent;
275
+
276
+ if (this.showInputs) {
277
+ hourTemplate = '<input type="text" class="bootstrap-timepicker-hour" maxlength="2"/>';
278
+ minuteTemplate = '<input type="text" class="bootstrap-timepicker-minute" maxlength="2"/>';
279
+ secondTemplate = '<input type="text" class="bootstrap-timepicker-second" maxlength="2"/>';
280
+ meridianTemplate = '<input type="text" class="bootstrap-timepicker-meridian" maxlength="2"/>';
281
+ } else {
282
+ hourTemplate = '<span class="bootstrap-timepicker-hour"></span>';
283
+ minuteTemplate = '<span class="bootstrap-timepicker-minute"></span>';
284
+ secondTemplate = '<span class="bootstrap-timepicker-second"></span>';
285
+ meridianTemplate = '<span class="bootstrap-timepicker-meridian"></span>';
286
+ }
287
+
288
+ templateContent = '<table>'+
289
+ '<tr>'+
290
+ '<td><a href="#" data-action="incrementHour"><span class="'+ this.icons.up +'"></span></a></td>'+
291
+ '<td class="separator">&nbsp;</td>'+
292
+ '<td><a href="#" data-action="incrementMinute"><span class="'+ this.icons.up +'"></span></a></td>'+
293
+ (this.showSeconds ?
294
+ '<td class="separator">&nbsp;</td>'+
295
+ '<td><a href="#" data-action="incrementSecond"><span class="'+ this.icons.up +'"></span></a></td>'
296
+ : '') +
297
+ (this.showMeridian ?
298
+ '<td class="separator">&nbsp;</td>'+
299
+ '<td class="meridian-column"><a href="#" data-action="toggleMeridian"><span class="'+ this.icons.up +'"></span></a></td>'
300
+ : '') +
301
+ '</tr>'+
302
+ '<tr>'+
303
+ '<td>'+ hourTemplate +'</td> '+
304
+ '<td class="separator">:</td>'+
305
+ '<td>'+ minuteTemplate +'</td> '+
306
+ (this.showSeconds ?
307
+ '<td class="separator">:</td>'+
308
+ '<td>'+ secondTemplate +'</td>'
309
+ : '') +
310
+ (this.showMeridian ?
311
+ '<td class="separator">&nbsp;</td>'+
312
+ '<td>'+ meridianTemplate +'</td>'
313
+ : '') +
314
+ '</tr>'+
315
+ '<tr>'+
316
+ '<td><a href="#" data-action="decrementHour"><span class="'+ this.icons.down +'"></span></a></td>'+
317
+ '<td class="separator"></td>'+
318
+ '<td><a href="#" data-action="decrementMinute"><span class="'+ this.icons.down +'"></span></a></td>'+
319
+ (this.showSeconds ?
320
+ '<td class="separator">&nbsp;</td>'+
321
+ '<td><a href="#" data-action="decrementSecond"><span class="'+ this.icons.down +'"></span></a></td>'
322
+ : '') +
323
+ (this.showMeridian ?
324
+ '<td class="separator">&nbsp;</td>'+
325
+ '<td><a href="#" data-action="toggleMeridian"><span class="'+ this.icons.down +'"></span></a></td>'
326
+ : '') +
327
+ '</tr>'+
328
+ '</table>';
329
+
330
+ switch(this.template) {
331
+ case 'modal':
332
+ template = '<div class="bootstrap-timepicker-widget modal hide fade in" data-backdrop="'+ (this.modalBackdrop ? 'true' : 'false') +'">'+
333
+ '<div class="modal-header">'+
334
+ '<a href="#" class="close" data-dismiss="modal">&times;</a>'+
335
+ '<h3>Pick a Time</h3>'+
336
+ '</div>'+
337
+ '<div class="modal-content">'+
338
+ templateContent +
339
+ '</div>'+
340
+ '<div class="modal-footer">'+
341
+ '<a href="#" class="btn btn-primary" data-dismiss="modal">OK</a>'+
342
+ '</div>'+
343
+ '</div>';
344
+ break;
345
+ case 'dropdown':
346
+ template = '<div class="bootstrap-timepicker-widget dropdown-menu">'+ templateContent +'</div>';
347
+ break;
348
+ }
349
+
350
+ return template;
351
+ },
352
+
353
+ getTime: function() {
354
+ if (this.hour === '') {
355
+ return '';
356
+ }
357
+
358
+ return this.hour + ':' + (this.minute.toString().length === 1 ? '0' + this.minute : this.minute) + (this.showSeconds ? ':' + (this.second.toString().length === 1 ? '0' + this.second : this.second) : '') + (this.showMeridian ? ' ' + this.meridian : '');
359
+ },
360
+
361
+ hideWidget: function() {
362
+ if (this.isOpen === false) {
363
+ return;
364
+ }
365
+
366
+ this.$element.trigger({
367
+ 'type': 'hide.timepicker',
368
+ 'time': {
369
+ 'value': this.getTime(),
370
+ 'hours': this.hour,
371
+ 'minutes': this.minute,
372
+ 'seconds': this.second,
373
+ 'meridian': this.meridian
374
+ }
375
+ });
376
+
377
+ if (this.template === 'modal' && this.$widget.modal) {
378
+ this.$widget.modal('hide');
379
+ } else {
380
+ this.$widget.removeClass('open');
381
+ }
382
+
383
+ $(document).off('mousedown.timepicker, touchend.timepicker', this.handleDocumentClick);
384
+
385
+ this.isOpen = false;
386
+ // show/hide approach taken by datepicker
387
+ this.$widget.detach();
388
+ },
389
+
390
+ highlightUnit: function() {
391
+ this.position = this.getCursorPosition();
392
+ if (this.position >= 0 && this.position <= 2) {
393
+ this.highlightHour();
394
+ } else if (this.position >= 3 && this.position <= 5) {
395
+ this.highlightMinute();
396
+ } else if (this.position >= 6 && this.position <= 8) {
397
+ if (this.showSeconds) {
398
+ this.highlightSecond();
399
+ } else {
400
+ this.highlightMeridian();
401
+ }
402
+ } else if (this.position >= 9 && this.position <= 11) {
403
+ this.highlightMeridian();
404
+ }
405
+ },
406
+
407
+ highlightNextUnit: function() {
408
+ switch (this.highlightedUnit) {
409
+ case 'hour':
410
+ this.highlightMinute();
411
+ break;
412
+ case 'minute':
413
+ if (this.showSeconds) {
414
+ this.highlightSecond();
415
+ } else if (this.showMeridian){
416
+ this.highlightMeridian();
417
+ } else {
418
+ this.highlightHour();
419
+ }
420
+ break;
421
+ case 'second':
422
+ if (this.showMeridian) {
423
+ this.highlightMeridian();
424
+ } else {
425
+ this.highlightHour();
426
+ }
427
+ break;
428
+ case 'meridian':
429
+ this.highlightHour();
430
+ break;
431
+ }
432
+ },
433
+
434
+ highlightPrevUnit: function() {
435
+ switch (this.highlightedUnit) {
436
+ case 'hour':
437
+ if(this.showMeridian){
438
+ this.highlightMeridian();
439
+ } else if (this.showSeconds) {
440
+ this.highlightSecond();
441
+ } else {
442
+ this.highlightMinute();
443
+ }
444
+ break;
445
+ case 'minute':
446
+ this.highlightHour();
447
+ break;
448
+ case 'second':
449
+ this.highlightMinute();
450
+ break;
451
+ case 'meridian':
452
+ if (this.showSeconds) {
453
+ this.highlightSecond();
454
+ } else {
455
+ this.highlightMinute();
456
+ }
457
+ break;
458
+ }
459
+ },
460
+
461
+ highlightHour: function() {
462
+ var $element = this.$element.get(0),
463
+ self = this;
464
+
465
+ this.highlightedUnit = 'hour';
466
+
467
+ if ($element.setSelectionRange) {
468
+ setTimeout(function() {
469
+ if (self.hour < 10) {
470
+ $element.setSelectionRange(0,1);
471
+ } else {
472
+ $element.setSelectionRange(0,2);
473
+ }
474
+ }, 0);
475
+ }
476
+ },
477
+
478
+ highlightMinute: function() {
479
+ var $element = this.$element.get(0),
480
+ self = this;
481
+
482
+ this.highlightedUnit = 'minute';
483
+
484
+ if ($element.setSelectionRange) {
485
+ setTimeout(function() {
486
+ if (self.hour < 10) {
487
+ $element.setSelectionRange(2,4);
488
+ } else {
489
+ $element.setSelectionRange(3,5);
490
+ }
491
+ }, 0);
492
+ }
493
+ },
494
+
495
+ highlightSecond: function() {
496
+ var $element = this.$element.get(0),
497
+ self = this;
498
+
499
+ this.highlightedUnit = 'second';
500
+
501
+ if ($element.setSelectionRange) {
502
+ setTimeout(function() {
503
+ if (self.hour < 10) {
504
+ $element.setSelectionRange(5,7);
505
+ } else {
506
+ $element.setSelectionRange(6,8);
507
+ }
508
+ }, 0);
509
+ }
510
+ },
511
+
512
+ highlightMeridian: function() {
513
+ var $element = this.$element.get(0),
514
+ self = this;
515
+
516
+ this.highlightedUnit = 'meridian';
517
+
518
+ if ($element.setSelectionRange) {
519
+ if (this.showSeconds) {
520
+ setTimeout(function() {
521
+ if (self.hour < 10) {
522
+ $element.setSelectionRange(8,10);
523
+ } else {
524
+ $element.setSelectionRange(9,11);
525
+ }
526
+ }, 0);
527
+ } else {
528
+ setTimeout(function() {
529
+ if (self.hour < 10) {
530
+ $element.setSelectionRange(5,7);
531
+ } else {
532
+ $element.setSelectionRange(6,8);
533
+ }
534
+ }, 0);
535
+ }
536
+ }
537
+ },
538
+
539
+ incrementHour: function() {
540
+ if (this.showMeridian) {
541
+ if (this.hour === 11) {
542
+ this.hour++;
543
+ return this.toggleMeridian();
544
+ } else if (this.hour === 12) {
545
+ this.hour = 0;
546
+ }
547
+ }
548
+ if (this.hour === this.maxHours - 1) {
549
+ this.hour = 0;
550
+
551
+ return;
552
+ }
553
+ this.hour++;
554
+ },
555
+
556
+ incrementMinute: function(step) {
557
+ var newVal;
558
+
559
+ if (step) {
560
+ newVal = this.minute + step;
561
+ } else {
562
+ newVal = this.minute + this.minuteStep - (this.minute % this.minuteStep);
563
+ }
564
+
565
+ if (newVal > 59) {
566
+ this.incrementHour();
567
+ this.minute = newVal - 60;
568
+ } else {
569
+ this.minute = newVal;
570
+ }
571
+ },
572
+
573
+ incrementSecond: function() {
574
+ var newVal = this.second + this.secondStep - (this.second % this.secondStep);
575
+
576
+ if (newVal > 59) {
577
+ this.incrementMinute(true);
578
+ this.second = newVal - 60;
579
+ } else {
580
+ this.second = newVal;
581
+ }
582
+ },
583
+
584
+ mousewheel: function(e) {
585
+ if (this.disableMousewheel) {
586
+ return;
587
+ }
588
+
589
+ e.preventDefault();
590
+ e.stopPropagation();
591
+
592
+ var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail,
593
+ scrollTo = null;
594
+
595
+ if (e.type === 'mousewheel') {
596
+ scrollTo = (e.originalEvent.wheelDelta * -1);
597
+ }
598
+ else if (e.type === 'DOMMouseScroll') {
599
+ scrollTo = 40 * e.originalEvent.detail;
600
+ }
601
+
602
+ if (scrollTo) {
603
+ e.preventDefault();
604
+ $(this).scrollTop(scrollTo + $(this).scrollTop());
605
+ }
606
+
607
+ switch (this.highlightedUnit) {
608
+ case 'minute':
609
+ if (delta > 0) {
610
+ this.incrementMinute();
611
+ } else {
612
+ this.decrementMinute();
613
+ }
614
+ this.highlightMinute();
615
+ break;
616
+ case 'second':
617
+ if (delta > 0) {
618
+ this.incrementSecond();
619
+ } else {
620
+ this.decrementSecond();
621
+ }
622
+ this.highlightSecond();
623
+ break;
624
+ case 'meridian':
625
+ this.toggleMeridian();
626
+ this.highlightMeridian();
627
+ break;
628
+ default:
629
+ if (delta > 0) {
630
+ this.incrementHour();
631
+ } else {
632
+ this.decrementHour();
633
+ }
634
+ this.highlightHour();
635
+ break;
636
+ }
637
+
638
+ return false;
639
+ },
640
+
641
+ /**
642
+ * Given a segment value like 43, will round and snap the segment
643
+ * to the nearest "step", like 45 if step is 15. Segment will
644
+ * "overflow" to 0 if it's larger than 59 or would otherwise
645
+ * round up to 60.
646
+ */
647
+ changeToNearestStep: function (segment, step) {
648
+ if (segment % step === 0) {
649
+ return segment;
650
+ }
651
+ if (Math.round((segment % step) / step)) {
652
+ return (segment + (step - segment % step)) % 60;
653
+ } else {
654
+ return segment - segment % step;
655
+ }
656
+ },
657
+
658
+ // This method was adapted from bootstrap-datepicker.
659
+ place : function() {
660
+ if (this.isInline) {
661
+ return;
662
+ }
663
+ var widgetWidth = this.$widget.outerWidth(), widgetHeight = this.$widget.outerHeight(), visualPadding = 10, windowWidth =
664
+ $(window).width(), windowHeight = $(window).height(), scrollTop = $(window).scrollTop();
665
+
666
+ var zIndex = parseInt(this.$element.parents().filter(function() { return $(this).css('z-index') !== 'auto'; }).first().css('z-index'), 10) + 10;
667
+ var offset = this.component ? this.component.parent().offset() : this.$element.offset();
668
+ var height = this.component ? this.component.outerHeight(true) : this.$element.outerHeight(false);
669
+ var width = this.component ? this.component.outerWidth(true) : this.$element.outerWidth(false);
670
+ var left = offset.left, top = offset.top;
671
+
672
+ this.$widget.removeClass('timepicker-orient-top timepicker-orient-bottom timepicker-orient-right timepicker-orient-left');
673
+
674
+ if (this.orientation.x !== 'auto') {
675
+ this.$widget.addClass('timepicker-orient-' + this.orientation.x);
676
+ if (this.orientation.x === 'right') {
677
+ left -= widgetWidth - width;
678
+ }
679
+ } else{
680
+ // auto x orientation is best-placement: if it crosses a window edge, fudge it sideways
681
+ // Default to left
682
+ this.$widget.addClass('timepicker-orient-left');
683
+ if (offset.left < 0) {
684
+ left -= offset.left - visualPadding;
685
+ } else if (offset.left + widgetWidth > windowWidth) {
686
+ left = windowWidth - widgetWidth - visualPadding;
687
+ }
688
+ }
689
+ // auto y orientation is best-situation: top or bottom, no fudging, decision based on which shows more of the widget
690
+ var yorient = this.orientation.y, topOverflow, bottomOverflow;
691
+ if (yorient === 'auto') {
692
+ topOverflow = -scrollTop + offset.top - widgetHeight;
693
+ bottomOverflow = scrollTop + windowHeight - (offset.top + height + widgetHeight);
694
+ if (Math.max(topOverflow, bottomOverflow) === bottomOverflow) {
695
+ yorient = 'top';
696
+ } else {
697
+ yorient = 'bottom';
698
+ }
699
+ }
700
+ this.$widget.addClass('timepicker-orient-' + yorient);
701
+ if (yorient === 'top'){
702
+ top += height;
703
+ } else{
704
+ top -= widgetHeight + parseInt(this.$widget.css('padding-top'), 10);
705
+ }
706
+
707
+ this.$widget.css({
708
+ top : top,
709
+ left : left,
710
+ zIndex : zIndex
711
+ });
712
+ },
713
+
714
+ remove: function() {
715
+ $('document').off('.timepicker');
716
+ if (this.$widget) {
717
+ this.$widget.remove();
718
+ }
719
+ delete this.$element.data().timepicker;
720
+ },
721
+
722
+ setDefaultTime: function(defaultTime) {
723
+ if (!this.$element.val()) {
724
+ if (defaultTime === 'current') {
725
+ var dTime = new Date(),
726
+ hours = dTime.getHours(),
727
+ minutes = dTime.getMinutes(),
728
+ seconds = dTime.getSeconds(),
729
+ meridian = 'AM';
730
+
731
+ if (seconds !== 0) {
732
+ seconds = Math.ceil(dTime.getSeconds() / this.secondStep) * this.secondStep;
733
+ if (seconds === 60) {
734
+ minutes += 1;
735
+ seconds = 0;
736
+ }
737
+ }
738
+
739
+ if (minutes !== 0) {
740
+ minutes = Math.ceil(dTime.getMinutes() / this.minuteStep) * this.minuteStep;
741
+ if (minutes === 60) {
742
+ hours += 1;
743
+ minutes = 0;
744
+ }
745
+ }
746
+
747
+ if (this.showMeridian) {
748
+ if (hours === 0) {
749
+ hours = 12;
750
+ } else if (hours >= 12) {
751
+ if (hours > 12) {
752
+ hours = hours - 12;
753
+ }
754
+ meridian = 'PM';
755
+ } else {
756
+ meridian = 'AM';
757
+ }
758
+ }
759
+
760
+ this.hour = hours;
761
+ this.minute = minutes;
762
+ this.second = seconds;
763
+ this.meridian = meridian;
764
+
765
+ this.update();
766
+
767
+ } else if (defaultTime === false) {
768
+ this.hour = 0;
769
+ this.minute = 0;
770
+ this.second = 0;
771
+ this.meridian = 'AM';
772
+ } else {
773
+ this.setTime(defaultTime);
774
+ }
775
+ } else {
776
+ this.updateFromElementVal();
777
+ }
778
+ },
779
+
780
+ setTime: function(time, ignoreWidget) {
781
+ if (!time) {
782
+ this.clear();
783
+ return;
784
+ }
785
+
786
+ var timeMode,
787
+ timeArray,
788
+ hour,
789
+ minute,
790
+ second,
791
+ meridian;
792
+
793
+ if (typeof time === 'object' && time.getMonth){
794
+ // this is a date object
795
+ hour = time.getHours();
796
+ minute = time.getMinutes();
797
+ second = time.getSeconds();
798
+
799
+ if (this.showMeridian){
800
+ meridian = 'AM';
801
+ if (hour > 12){
802
+ meridian = 'PM';
803
+ hour = hour % 12;
804
+ }
805
+
806
+ if (hour === 12){
807
+ meridian = 'PM';
808
+ }
809
+ }
810
+ } else {
811
+ timeMode = ((/a/i).test(time) ? 1 : 0) + ((/p/i).test(time) ? 2 : 0); // 0 = none, 1 = AM, 2 = PM, 3 = BOTH.
812
+ if (timeMode > 2) { // If both are present, fail.
813
+ this.clear();
814
+ return;
815
+ }
816
+
817
+ timeArray = time.replace(/[^0-9\:]/g, '').split(':');
818
+
819
+ hour = timeArray[0] ? timeArray[0].toString() : timeArray.toString();
820
+
821
+ if(this.explicitMode && hour.length > 2 && (hour.length % 2) !== 0 ) {
822
+ this.clear();
823
+ return;
824
+ }
825
+
826
+ minute = timeArray[1] ? timeArray[1].toString() : '';
827
+ second = timeArray[2] ? timeArray[2].toString() : '';
828
+
829
+ // adaptive time parsing
830
+ if (hour.length > 4) {
831
+ second = hour.slice(-2);
832
+ hour = hour.slice(0, -2);
833
+ }
834
+
835
+ if (hour.length > 2) {
836
+ minute = hour.slice(-2);
837
+ hour = hour.slice(0, -2);
838
+ }
839
+
840
+ if (minute.length > 2) {
841
+ second = minute.slice(-2);
842
+ minute = minute.slice(0, -2);
843
+ }
844
+
845
+ hour = parseInt(hour, 10);
846
+ minute = parseInt(minute, 10);
847
+ second = parseInt(second, 10);
848
+
849
+ if (isNaN(hour)) {
850
+ hour = 0;
851
+ }
852
+ if (isNaN(minute)) {
853
+ minute = 0;
854
+ }
855
+ if (isNaN(second)) {
856
+ second = 0;
857
+ }
858
+
859
+ // Adjust the time based upon unit boundary.
860
+ // NOTE: Negatives will never occur due to time.replace() above.
861
+ if (second > 59) {
862
+ second = 59;
863
+ }
864
+
865
+ if (minute > 59) {
866
+ minute = 59;
867
+ }
868
+
869
+ if (hour >= this.maxHours) {
870
+ // No day/date handling.
871
+ hour = this.maxHours - 1;
872
+ }
873
+
874
+ if (this.showMeridian) {
875
+ if (hour > 12) {
876
+ // Force PM.
877
+ timeMode = 2;
878
+ hour -= 12;
879
+ }
880
+ if (!timeMode) {
881
+ timeMode = 1;
882
+ }
883
+ if (hour === 0) {
884
+ hour = 12; // AM or PM, reset to 12. 0 AM = 12 AM. 0 PM = 12 PM, etc.
885
+ }
886
+ meridian = timeMode === 1 ? 'AM' : 'PM';
887
+ } else if (hour < 12 && timeMode === 2) {
888
+ hour += 12;
889
+ } else {
890
+ if (hour >= this.maxHours) {
891
+ hour = this.maxHours - 1;
892
+ } else if ((hour < 0) || (hour === 12 && timeMode === 1)){
893
+ hour = 0;
894
+ }
895
+ }
896
+ }
897
+
898
+ this.hour = hour;
899
+ if (this.snapToStep) {
900
+ this.minute = this.changeToNearestStep(minute, this.minuteStep);
901
+ this.second = this.changeToNearestStep(second, this.secondStep);
902
+ } else {
903
+ this.minute = minute;
904
+ this.second = second;
905
+ }
906
+ this.meridian = meridian;
907
+
908
+ this.update(ignoreWidget);
909
+ },
910
+
911
+ showWidget: function() {
912
+ if (this.isOpen) {
913
+ return;
914
+ }
915
+
916
+ if (this.$element.is(':disabled')) {
917
+ return;
918
+ }
919
+
920
+ // show/hide approach taken by datepicker
921
+ this.$widget.appendTo(this.appendWidgetTo);
922
+ $(document).on('mousedown.timepicker, touchend.timepicker', {scope: this}, this.handleDocumentClick);
923
+
924
+ this.$element.trigger({
925
+ 'type': 'show.timepicker',
926
+ 'time': {
927
+ 'value': this.getTime(),
928
+ 'hours': this.hour,
929
+ 'minutes': this.minute,
930
+ 'seconds': this.second,
931
+ 'meridian': this.meridian
932
+ }
933
+ });
934
+
935
+ this.place();
936
+ if (this.disableFocus) {
937
+ this.$element.blur();
938
+ }
939
+
940
+ // widget shouldn't be empty on open
941
+ if (this.hour === '') {
942
+ if (this.defaultTime) {
943
+ this.setDefaultTime(this.defaultTime);
944
+ } else {
945
+ this.setTime('0:0:0');
946
+ }
947
+ }
948
+
949
+ if (this.template === 'modal' && this.$widget.modal) {
950
+ this.$widget.modal('show').on('hidden', $.proxy(this.hideWidget, this));
951
+ } else {
952
+ if (this.isOpen === false) {
953
+ this.$widget.addClass('open');
954
+ }
955
+ }
956
+
957
+ this.isOpen = true;
958
+ },
959
+
960
+ toggleMeridian: function() {
961
+ this.meridian = this.meridian === 'AM' ? 'PM' : 'AM';
962
+ },
963
+
964
+ update: function(ignoreWidget) {
965
+ this.updateElement();
966
+ if (!ignoreWidget) {
967
+ this.updateWidget();
968
+ }
969
+
970
+ this.$element.trigger({
971
+ 'type': 'changeTime.timepicker',
972
+ 'time': {
973
+ 'value': this.getTime(),
974
+ 'hours': this.hour,
975
+ 'minutes': this.minute,
976
+ 'seconds': this.second,
977
+ 'meridian': this.meridian
978
+ }
979
+ });
980
+ },
981
+
982
+ updateElement: function() {
983
+ this.$element.val(this.getTime()).change();
984
+ },
985
+
986
+ updateFromElementVal: function() {
987
+ this.setTime(this.$element.val());
988
+ },
989
+
990
+ updateWidget: function() {
991
+ if (this.$widget === false) {
992
+ return;
993
+ }
994
+
995
+ var hour = this.hour,
996
+ minute = this.minute.toString().length === 1 ? '0' + this.minute : this.minute,
997
+ second = this.second.toString().length === 1 ? '0' + this.second : this.second;
998
+
999
+ if (this.showInputs) {
1000
+ this.$widget.find('input.bootstrap-timepicker-hour').val(hour);
1001
+ this.$widget.find('input.bootstrap-timepicker-minute').val(minute);
1002
+
1003
+ if (this.showSeconds) {
1004
+ this.$widget.find('input.bootstrap-timepicker-second').val(second);
1005
+ }
1006
+ if (this.showMeridian) {
1007
+ this.$widget.find('input.bootstrap-timepicker-meridian').val(this.meridian);
1008
+ }
1009
+ } else {
1010
+ this.$widget.find('span.bootstrap-timepicker-hour').text(hour);
1011
+ this.$widget.find('span.bootstrap-timepicker-minute').text(minute);
1012
+
1013
+ if (this.showSeconds) {
1014
+ this.$widget.find('span.bootstrap-timepicker-second').text(second);
1015
+ }
1016
+ if (this.showMeridian) {
1017
+ this.$widget.find('span.bootstrap-timepicker-meridian').text(this.meridian);
1018
+ }
1019
+ }
1020
+ },
1021
+
1022
+ updateFromWidgetInputs: function() {
1023
+ if (this.$widget === false) {
1024
+ return;
1025
+ }
1026
+
1027
+ var t = this.$widget.find('input.bootstrap-timepicker-hour').val() + ':' +
1028
+ this.$widget.find('input.bootstrap-timepicker-minute').val() +
1029
+ (this.showSeconds ? ':' + this.$widget.find('input.bootstrap-timepicker-second').val() : '') +
1030
+ (this.showMeridian ? this.$widget.find('input.bootstrap-timepicker-meridian').val() : '')
1031
+ ;
1032
+
1033
+ this.setTime(t, true);
1034
+ },
1035
+
1036
+ widgetClick: function(e) {
1037
+ e.stopPropagation();
1038
+ e.preventDefault();
1039
+
1040
+ var $input = $(e.target),
1041
+ action = $input.closest('a').data('action');
1042
+
1043
+ if (action) {
1044
+ this[action]();
1045
+ }
1046
+ this.update();
1047
+
1048
+ if ($input.is('input')) {
1049
+ $input.get(0).setSelectionRange(0,2);
1050
+ }
1051
+ },
1052
+
1053
+ widgetKeydown: function(e) {
1054
+ var $input = $(e.target),
1055
+ name = $input.attr('class').replace('bootstrap-timepicker-', '');
1056
+
1057
+ switch (e.which) {
1058
+ case 9: //tab
1059
+ if (e.shiftKey) {
1060
+ if (name === 'hour') {
1061
+ return this.hideWidget();
1062
+ }
1063
+ } else if ((this.showMeridian && name === 'meridian') || (this.showSeconds && name === 'second') || (!this.showMeridian && !this.showSeconds && name === 'minute')) {
1064
+ return this.hideWidget();
1065
+ }
1066
+ break;
1067
+ case 27: // escape
1068
+ this.hideWidget();
1069
+ break;
1070
+ case 38: // up arrow
1071
+ e.preventDefault();
1072
+ switch (name) {
1073
+ case 'hour':
1074
+ this.incrementHour();
1075
+ break;
1076
+ case 'minute':
1077
+ this.incrementMinute();
1078
+ break;
1079
+ case 'second':
1080
+ this.incrementSecond();
1081
+ break;
1082
+ case 'meridian':
1083
+ this.toggleMeridian();
1084
+ break;
1085
+ }
1086
+ this.setTime(this.getTime());
1087
+ $input.get(0).setSelectionRange(0,2);
1088
+ break;
1089
+ case 40: // down arrow
1090
+ e.preventDefault();
1091
+ switch (name) {
1092
+ case 'hour':
1093
+ this.decrementHour();
1094
+ break;
1095
+ case 'minute':
1096
+ this.decrementMinute();
1097
+ break;
1098
+ case 'second':
1099
+ this.decrementSecond();
1100
+ break;
1101
+ case 'meridian':
1102
+ this.toggleMeridian();
1103
+ break;
1104
+ }
1105
+ this.setTime(this.getTime());
1106
+ $input.get(0).setSelectionRange(0,2);
1107
+ break;
1108
+ }
1109
+ },
1110
+
1111
+ widgetKeyup: function(e) {
1112
+ if ((e.which === 65) || (e.which === 77) || (e.which === 80) || (e.which === 46) || (e.which === 8) || (e.which >= 48 && e.which <= 57) || (e.which >= 96 && e.which <= 105)) {
1113
+ this.updateFromWidgetInputs();
1114
+ }
1115
+ }
1116
+ };
1117
+
1118
+ //TIMEPICKER PLUGIN DEFINITION
1119
+ $.fn.timepicker = function(option) {
1120
+ var args = Array.apply(null, arguments);
1121
+ args.shift();
1122
+ return this.each(function() {
1123
+ var $this = $(this),
1124
+ data = $this.data('timepicker'),
1125
+ options = typeof option === 'object' && option;
1126
+
1127
+ if (!data) {
1128
+ $this.data('timepicker', (data = new Timepicker(this, $.extend({}, $.fn.timepicker.defaults, options, $(this).data()))));
1129
+ }
1130
+
1131
+ if (typeof option === 'string') {
1132
+ data[option].apply(data, args);
1133
+ }
1134
+ });
1135
+ };
1136
+
1137
+ $.fn.timepicker.defaults = {
1138
+ defaultTime: 'current',
1139
+ disableFocus: false,
1140
+ disableMousewheel: false,
1141
+ isOpen: false,
1142
+ minuteStep: 15,
1143
+ modalBackdrop: false,
1144
+ orientation: { x: 'auto', y: 'auto'},
1145
+ secondStep: 15,
1146
+ snapToStep: false,
1147
+ showSeconds: false,
1148
+ showInputs: true,
1149
+ showMeridian: true,
1150
+ template: 'dropdown',
1151
+ appendWidgetTo: 'body',
1152
+ showWidgetOnAddonClick: true,
1153
+ icons: {
1154
+ up: 'glyphicon glyphicon-chevron-up',
1155
+ down: 'glyphicon glyphicon-chevron-down'
1156
+ },
1157
+ maxHours: 24,
1158
+ explicitMode: false
1159
+ };
1160
+
1161
+ $.fn.timepicker.Constructor = Timepicker;
1162
+
1163
+ $(document).on(
1164
+ 'focus.timepicker.data-api click.timepicker.data-api',
1165
+ '[data-provide="timepicker"]',
1166
+ function(e){
1167
+ var $this = $(this);
1168
+ if ($this.data('timepicker')) {
1169
+ return;
1170
+ }
1171
+ e.preventDefault();
1172
+ // component click requires us to explicitly show it
1173
+ $this.timepicker();
1174
+ }
1175
+ );
1176
+
1177
+ })(jQuery, window, document);