bootstrap-timepicker-rails-addon 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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);