undercase 0.2.29 → 0.2.57

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -4
  3. data/Rakefile +0 -6
  4. data/app/assets/images/backgrounds/diag_black.png +0 -0
  5. data/app/assets/javascripts/undercase/patterns/AbsolutelyPositionTableCells.js +12 -0
  6. data/app/assets/javascripts/undercase/patterns/Filter.js +6 -0
  7. data/app/assets/javascripts/undercase/patterns/daterange.js +28 -0
  8. data/app/assets/javascripts/undercase/patterns/modal.js +1 -4
  9. data/app/assets/javascripts/undercase/patterns/timepicker.js +6 -1
  10. data/app/assets/javascripts/undercase/vendor/jquery-extensions/jquery.timepicker.js +1125 -0
  11. data/app/assets/stylesheets/undercase/libraries/_borders.scss +1 -0
  12. data/app/assets/stylesheets/undercase/patterns/_borders.scss +9 -0
  13. data/app/assets/stylesheets/undercase/patterns/_filters.scss +41 -10
  14. data/app/assets/stylesheets/undercase/patterns/_forms.scss +20 -1
  15. data/app/assets/stylesheets/undercase/patterns/_layout.scss +14 -0
  16. data/app/assets/stylesheets/undercase/patterns/_lists.scss +57 -0
  17. data/app/assets/stylesheets/undercase/patterns/_popovers.scss +5 -0
  18. data/app/assets/stylesheets/undercase/patterns/_tables.scss +97 -50
  19. data/app/assets/stylesheets/undercase/patterns/_typography.scss +13 -1
  20. data/app/assets/stylesheets/undercase/patterns_new_front_end/_cards.scss +45 -0
  21. data/app/assets/stylesheets/undercase/vendor_static/_all.scss +1 -0
  22. data/app/assets/stylesheets/undercase/vendor_static/_timepicker.scss +72 -0
  23. data/app/assets/stylesheets/undercase_frame/_dummy_site.scss +77 -0
  24. data/app/controllers/undercase/application_controller.rb +1 -0
  25. data/app/inputs/date_picker_input.rb +2 -2
  26. data/app/views/layouts/undercase/application.html.haml +6 -1
  27. data/app/views/undercase/patterns/_definition_list.html.haml +9 -0
  28. data/app/views/undercase/patterns/_filters.html.haml +13 -0
  29. data/app/views/undercase/patterns/_filters_show_only.html.haml +5 -0
  30. data/app/views/undercase/patterns/_grids_basic.html.haml +39 -0
  31. data/app/views/undercase/patterns/_grids_element_spacing.html.haml +94 -0
  32. data/app/views/undercase/patterns/_grids_responsive.html.haml +32 -0
  33. data/app/views/undercase/patterns/_layouts.html.haml +20 -0
  34. data/app/views/undercase/patterns/_list_long.html.haml +45 -0
  35. data/app/views/undercase/patterns/_popover.html.haml +23 -6
  36. data/app/views/undercase/patterns/_tables.html.haml +26 -0
  37. data/app/views/undercase/patterns/{_tables_small_tablet.html.haml → _tables_unsorted.html.haml} +6 -9
  38. data/app/views/undercase/patterns/_typography_highlight.html.haml +5 -0
  39. data/app/views/undercase/patterns/_uc_forms_date_range_picker.html.haml +43 -0
  40. data/app/views/undercase/patterns/_uc_history_card.html.haml +10 -0
  41. data/app/views/undercase/patterns/index.html.haml +1 -1
  42. data/lib/tasks/undercase.rake +53 -0
  43. data/lib/undercase/patterns/definition_list.yml +4 -0
  44. data/lib/undercase/patterns/grids_basic.yml +6 -0
  45. data/lib/undercase/patterns/grids_element_spacing.yml +12 -0
  46. data/lib/undercase/patterns/grids_responsive.yml +6 -0
  47. data/lib/undercase/patterns/layouts.yml +6 -0
  48. data/lib/undercase/patterns/list_long.yml +4 -0
  49. data/lib/undercase/patterns/tables.yml +4 -0
  50. data/lib/undercase/patterns/tables_unsorted.yml +4 -0
  51. data/lib/undercase/patterns/typography_highlight.yml +4 -0
  52. data/lib/undercase/patterns/uc_forms_date_range_picker.yml +4 -0
  53. data/lib/undercase/patterns/uc_history_card.yml +4 -0
  54. data/lib/undercase/version.rb +1 -1
  55. data/public/undercase-0.2.57.tar.gz +0 -0
  56. metadata +44 -11
  57. data/app/views/undercase/patterns/_grid.html.haml +0 -0
  58. data/app/views/undercase/patterns/_tables_mobile.html.haml +0 -23
  59. data/app/views/undercase/patterns/_tables_not_desktop.html.haml +0 -21
  60. data/app/views/undercase/patterns/_tables_small_mobile.html.haml +0 -23
  61. data/lib/undercase/patterns/grid.yml +0 -4
  62. data/lib/undercase/patterns/tables_mobile.yml +0 -4
  63. data/lib/undercase/patterns/tables_not_desktop.yml +0 -4
  64. data/lib/undercase/patterns/tables_small_tablet.yml +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 681e5c05d030374697d9aa1c0b9af2cf4c92363a
4
- data.tar.gz: 38edc4918f814bdcd3356c2541a8ef817264b48b
3
+ metadata.gz: ebced5cd0f92992df799035022405dd789abe974
4
+ data.tar.gz: 69e298b6c3f0d5231143cde630f5d6130138bfcc
5
5
  SHA512:
6
- metadata.gz: ff293ed84d233c9af8802f0ae4e2a479cc8541d4cec66382864203e47e37f706a3d706f347b588fe4e39f2ecdb5d22f36504d8139f93dd1c4261d5796a43c4e0
7
- data.tar.gz: e601328d2c1e9fc470ec2c005f095afb8e1c09dc383690ef6a0a3b352654e5dd6821edb751c16a14770257baaa30efbdd13568d8b634bcb51d7b8a3274928bdf
6
+ metadata.gz: 85c75b6c48ffc9fe56b3417887e6096bfbe0118af8f4ed3468286259d01c29711197befc93ab5f8ea0a39da7dab9a578ebdb85d99c2d2fb5afefa961fb3f1f97
7
+ data.tar.gz: 15a94f93c4f06fb826af88655c2763318bc74d28faa24ad3578b1ed21b69699b4dd486a2ed5410cfdfc5f3355a4862c58df9f5acac673917ecc3e7532e210639
data/README.md CHANGED
@@ -46,13 +46,39 @@ Undercase provides a rake task to easily create the files needed for a new patte
46
46
  rake pattern:create[PATTERNNAME,'DISPLAY NAME']
47
47
 
48
48
  This will append your new pattern to
49
- * app/assets/stylesheets/undercase/_patterns.scss
49
+
50
+ * `app/assets/stylesheets/undercase/_patterns.scss`
50
51
 
51
52
  This will create new files in:
52
- * app/assets/stylesheets/undercase/patterns_PATTERNNAME.scss
53
- * app/views/undercase/patterns/PATTERNNAME.html.haml
54
- * lib/undercase/patterns/PATTERNNAME.yml
55
53
 
54
+ * `app/assets/stylesheets/undercase/patterns_PATTERNNAME.scss`
55
+ * `app/views/undercase/patterns/PATTERNNAME.html.haml`
56
+ * `lib/undercase/patterns/PATTERNNAME.yml`
57
+
58
+ ## undercase:release rake task
59
+
60
+ On master branch at root of undercase:
61
+
62
+ Use undercase:release rake task
63
+
64
+ * to do a patch release: run `rake undercase:release`
65
+ * to do a major/minor release run `VERSION=1.2.3 rake undercase:release`
66
+
67
+ ###Comprised of smaller tasks:
68
+
69
+ #### undercase:bump
70
+ Sets or increments the version in version.rb.
71
+
72
+ If given a version, (for major/minor or non-sequential releases) will use that. Otherwise will increment the current version number.
73
+
74
+ #### tag
75
+ Uses current version number to create tag and push tag
76
+
77
+ #### undercase:package
78
+ Compiles assets and adds them to tar file
79
+
80
+ #### undercase:deploy
81
+ Pushes local master branch to github and heroku
56
82
 
57
83
  ## Contributing
58
84
  Undercase is a container for generalizable patterns. Keep the following criteria in mind when creating and submitting a new pattern.
data/Rakefile CHANGED
@@ -8,14 +8,8 @@ rescue LoadError => ex
8
8
  end
9
9
 
10
10
  root = File.expand_path('..', __FILE__)
11
- gem_helper = Bundler::GemHelper.new(root)
12
11
  Bundler::GemHelper.install_tasks
13
12
  defined?(RSpec) and RSpec::Core::RakeTask.new(:spec)
14
13
  Dir.glob('./lib/tasks/*.rake').each { |r| import r }
15
14
 
16
15
  task :default => :spec
17
-
18
- desc "Create and push tag #{gem_helper.send(:version_tag)}"
19
- task :tag do
20
- gem_helper.send(:tag_version) { gem_helper.send(:git_push) }
21
- end
@@ -0,0 +1,12 @@
1
+ (function($) {
2
+ $(document).on('AbsolutelyPositionTableCells:present', function(event){
3
+ var ths = $(event.target).find('thead th');
4
+ var sortableThs = ths.has('a');
5
+ var unsortableThs = ths.not(sortableThs);
6
+ var newDiv = $('<div>', {class: 'uc-table__th__inner-wrapper'});
7
+ var newSortableDiv = $('<div>', {class: 'uc-table__th__inner-wrapper uc-table__th__inner-wrapper--sortable'});
8
+
9
+ unsortableThs.wrapInner(newDiv);
10
+ sortableThs.wrapInner(newSortableDiv);
11
+ });
12
+ })(jQuery);
@@ -5,6 +5,12 @@
5
5
  var facets = $('.js-filter__facet', section);
6
6
  var subFacets = $('.js-filter__sub-facet', section);
7
7
 
8
+ var facetsAndSubFacets = facets.toArray().concat(subFacets.toArray());
9
+ var facetsOrSubFacetsChecked = _.any(facetsAndSubFacets, function(facet) {
10
+ return $(facet).is(':checked');
11
+ });
12
+ if(facetsOrSubFacetsChecked) { clearLink.show(); }
13
+
8
14
  // Facet changed
9
15
  facets.on('change', function(event) {
10
16
  var facet = $(event.target);
@@ -0,0 +1,28 @@
1
+ (function($) {
2
+ $(document).on('daterange:present', function(event) {
3
+ var self = $(event.target);
4
+ var $checkbox = self.find(".js-present input[type=checkbox]");
5
+ checkboxDaterangeToggle($checkbox, self);
6
+ $checkbox.change(function(e) { checkboxDaterangeToggle($(e.target), self); });
7
+
8
+ function checkboxDaterangeToggle($checkbox, self) {
9
+ var $present = self.find('.js-present');
10
+ var $daterangeEndFields = self.find(".js-daterange-end-fields");
11
+ var isEnabler = self.data("daterangeToggler") == "enabler";
12
+
13
+ if($checkbox.prop("checked") == isEnabler) {
14
+ $present.removeClass('ongoing');
15
+ $daterangeEndFields.show();
16
+ } else {
17
+ $present.addClass('ongoing');
18
+ $daterangeEndFields.hide();
19
+ clearDependentFields();
20
+ }
21
+
22
+ function clearDependentFields() {
23
+ $daterangeEndFields.find('input').val('');
24
+ $daterangeEndFields.find('select').val('');
25
+ }
26
+ }
27
+ });
28
+ })(jQuery);
@@ -27,7 +27,6 @@
27
27
  modalFixedWrapperFrame.css("background", "none");
28
28
  $("body").addClass("modal-on");
29
29
  $('html, body').animate({ scrollTop: 0 }, 0);
30
- self.wasHidden = self.target.hasClass('js-hidden');
31
30
  self.target.removeClass('js-hidden');
32
31
 
33
32
  self.target.click(function(event) {
@@ -41,9 +40,7 @@
41
40
  $("body").removeClass("modal-on");
42
41
  $('.uc-modal-fixed-wrapper').remove();
43
42
  self.target.trigger('closed');
44
- if(self.wasHidden) {
45
- self.target.addClass('js-hidden');
46
- }
43
+ self.target.addClass('js-hidden');
47
44
  }
48
45
  })
49
46
  };
@@ -1,5 +1,10 @@
1
1
  (function($, ns) {
2
2
  $(document).on('timepicker:present', function(event) {
3
- $(event.target).calendricalTime({timeInterval: 15, padding: 6});
3
+ $(event.target).timepicker({
4
+ 'scrollDefault': 'now',
5
+ 'showOnFocus': false,
6
+ 'step': 15,
7
+ 'timeFormat': 'h:i A'
8
+ });
4
9
  });
5
10
  })(jQuery);
@@ -0,0 +1,1125 @@
1
+ /************************
2
+ jquery-timepicker v1.4.6
3
+ http://jonthornton.github.com/jquery-timepicker/
4
+
5
+ requires jQuery 1.7+
6
+ ************************/
7
+
8
+
9
+ (function (factory) {
10
+ if (typeof define === 'function' && define.amd) {
11
+ // AMD. Register as an anonymous module.
12
+ define(['jquery'], factory);
13
+ } else {
14
+ // Browser globals
15
+ factory(jQuery);
16
+ }
17
+ }(function ($) {
18
+ var _baseDate = _generateBaseDate();
19
+ var _ONE_DAY = 86400;
20
+ var _lang = {
21
+ am: 'am',
22
+ pm: 'pm',
23
+ AM: 'AM',
24
+ PM: 'PM',
25
+ decimal: '.',
26
+ mins: 'mins',
27
+ hr: 'hr',
28
+ hrs: 'hrs'
29
+ };
30
+
31
+ var methods =
32
+ {
33
+ init: function(options)
34
+ {
35
+ return this.each(function()
36
+ {
37
+ var self = $(this);
38
+
39
+ // pick up settings from data attributes
40
+ var attributeOptions = [];
41
+ for (var key in $.fn.timepicker.defaults) {
42
+ if (self.data(key)) {
43
+ attributeOptions[key] = self.data(key);
44
+ }
45
+ }
46
+
47
+ var settings = $.extend({}, $.fn.timepicker.defaults, attributeOptions, options);
48
+
49
+ if (settings.lang) {
50
+ _lang = $.extend(_lang, settings.lang);
51
+ }
52
+
53
+ settings = _parseSettings(settings);
54
+ self.data('timepicker-settings', settings);
55
+ self.addClass('ui-timepicker-input');
56
+
57
+ if (settings.useSelect) {
58
+ _render(self);
59
+ } else {
60
+ self.prop('autocomplete', 'off');
61
+ self.on('click.timepicker focus.timepicker', methods.show);
62
+ self.on('change.timepicker', _formatValue);
63
+ self.on('keydown.timepicker', _keydownhandler);
64
+ self.on('keyup.timepicker', _keyuphandler);
65
+
66
+ _formatValue.call(self.get(0));
67
+ }
68
+ });
69
+ },
70
+
71
+ show: function(e)
72
+ {
73
+ var self = $(this);
74
+ var settings = self.data('timepicker-settings');
75
+
76
+ if (e) {
77
+ if (!settings.showOnFocus) {
78
+ return true;
79
+ }
80
+
81
+ e.preventDefault();
82
+ }
83
+
84
+ if (settings.useSelect) {
85
+ self.data('timepicker-list').focus();
86
+ return;
87
+ }
88
+
89
+ if (_hideKeyboard(self)) {
90
+ // block the keyboard on mobile devices
91
+ self.blur();
92
+ }
93
+
94
+ var list = self.data('timepicker-list');
95
+
96
+ // check if input is readonly
97
+ if (self.prop('readonly')) {
98
+ return;
99
+ }
100
+
101
+ // check if list needs to be rendered
102
+ if (!list || list.length === 0 || typeof settings.durationTime === 'function') {
103
+ _render(self);
104
+ list = self.data('timepicker-list');
105
+ }
106
+
107
+ if (list.is(':visible')) {
108
+ return;
109
+ }
110
+
111
+ // make sure other pickers are hidden
112
+ methods.hide();
113
+
114
+ // position the dropdown relative to the input
115
+ list.show();
116
+ var listOffset = {};
117
+
118
+ if (settings.orientation == 'rtl') {
119
+ // right-align the dropdown
120
+ listOffset.left = self.offset().left + self.outerWidth() - list.outerWidth() + parseInt(list.css('marginLeft').replace('px', ''), 10);
121
+ } else {
122
+ // left-align the dropdown
123
+ listOffset.left = self.offset().left + parseInt(list.css('marginLeft').replace('px', ''), 10);
124
+ }
125
+
126
+ if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) {
127
+ // position the dropdown on top
128
+ listOffset.top = self.offset().top - list.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10);
129
+ } else {
130
+ // put it under the input
131
+ listOffset.top = self.offset().top + self.outerHeight() + parseInt(list.css('marginTop').replace('px', ''), 10);
132
+ }
133
+
134
+ list.offset(listOffset);
135
+
136
+ // position scrolling
137
+ var selected = list.find('.ui-timepicker-selected');
138
+
139
+ if (!selected.length) {
140
+ if (_getTimeValue(self)) {
141
+ selected = _findRow(self, list, _time2int(_getTimeValue(self)));
142
+ } else if (settings.scrollDefault) {
143
+ selected = _findRow(self, list, settings.scrollDefault);
144
+ }
145
+ }
146
+
147
+ if (selected && selected.length) {
148
+ var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight();
149
+ list.scrollTop(topOffset);
150
+ } else {
151
+ list.scrollTop(0);
152
+ }
153
+
154
+ // attach close handlers
155
+ $(document).on('touchstart.ui-timepicker mousedown.ui-timepicker', _closeHandler);
156
+ if (settings.closeOnWindowScroll) {
157
+ $(document).on('scroll.ui-timepicker', _closeHandler);
158
+ }
159
+
160
+ self.trigger('showTimepicker');
161
+
162
+ return this;
163
+ },
164
+
165
+ hide: function(e)
166
+ {
167
+ var self = $(this);
168
+ var settings = self.data('timepicker-settings');
169
+
170
+ if (settings && settings.useSelect) {
171
+ self.blur();
172
+ }
173
+
174
+ $('.ui-timepicker-wrapper:visible').each(function() {
175
+ var list = $(this);
176
+ var self = list.data('timepicker-input');
177
+ var settings = self.data('timepicker-settings');
178
+
179
+ if (settings && settings.selectOnBlur) {
180
+ _selectValue(self);
181
+ }
182
+
183
+ list.hide();
184
+ self.trigger('hideTimepicker');
185
+ });
186
+
187
+ return this;
188
+ },
189
+
190
+ option: function(key, value)
191
+ {
192
+ return this.each(function(){
193
+ var self = $(this);
194
+ var settings = self.data('timepicker-settings');
195
+ var list = self.data('timepicker-list');
196
+
197
+ if (typeof key == 'object') {
198
+ settings = $.extend(settings, key);
199
+
200
+ } else if (typeof key == 'string' && typeof value != 'undefined') {
201
+ settings[key] = value;
202
+
203
+ } else if (typeof key == 'string') {
204
+ return settings[key];
205
+ }
206
+
207
+ settings = _parseSettings(settings);
208
+
209
+ self.data('timepicker-settings', settings);
210
+
211
+ if (list) {
212
+ list.remove();
213
+ self.data('timepicker-list', false);
214
+ }
215
+
216
+ if (settings.useSelect) {
217
+ _render(self);
218
+ }
219
+ });
220
+ },
221
+
222
+ getSecondsFromMidnight: function()
223
+ {
224
+ return _time2int(_getTimeValue(this));
225
+ },
226
+
227
+ getTime: function(relative_date)
228
+ {
229
+ var self = this;
230
+
231
+ var time_string = _getTimeValue(self);
232
+ if (!time_string) {
233
+ return null;
234
+ }
235
+
236
+ if (!relative_date) {
237
+ relative_date = new Date();
238
+ }
239
+ var offset = _time2int(time_string);
240
+
241
+ // construct a Date with today's date, and offset's time
242
+ var time = new Date(relative_date);
243
+ time.setHours(offset / 3600);
244
+ time.setMinutes(offset % 3600 / 60);
245
+ time.setSeconds(offset % 60);
246
+ time.setMilliseconds(0);
247
+
248
+ return time;
249
+ },
250
+
251
+ setTime: function(value)
252
+ {
253
+ var self = this;
254
+ var settings = self.data('timepicker-settings');
255
+
256
+ if (settings.forceRoundTime) {
257
+ var prettyTime = _roundAndFormatTime(value, settings)
258
+ } else {
259
+ var prettyTime = _int2time(_time2int(value), settings.timeFormat);
260
+ }
261
+
262
+ _setTimeValue(self, prettyTime);
263
+ if (self.data('timepicker-list')) {
264
+ _setSelected(self, self.data('timepicker-list'));
265
+ }
266
+
267
+ return this;
268
+ },
269
+
270
+ remove: function()
271
+ {
272
+ var self = this;
273
+
274
+ // check if this element is a timepicker
275
+ if (!self.hasClass('ui-timepicker-input')) {
276
+ return;
277
+ }
278
+
279
+ var settings = self.data('timepicker-settings');
280
+ self.removeAttr('autocomplete', 'off');
281
+ self.removeClass('ui-timepicker-input');
282
+ self.removeData('timepicker-settings');
283
+ self.off('.timepicker');
284
+
285
+ // timepicker-list won't be present unless the user has interacted with this timepicker
286
+ if (self.data('timepicker-list')) {
287
+ self.data('timepicker-list').remove();
288
+ }
289
+
290
+ if (settings.useSelect) {
291
+ self.show();
292
+ }
293
+
294
+ self.removeData('timepicker-list');
295
+
296
+ return this;
297
+ }
298
+ };
299
+
300
+ // private methods
301
+
302
+ function _parseSettings(settings)
303
+ {
304
+ if (settings.minTime) {
305
+ settings.minTime = _time2int(settings.minTime);
306
+ }
307
+
308
+ if (settings.maxTime) {
309
+ settings.maxTime = _time2int(settings.maxTime);
310
+ }
311
+
312
+ if (settings.durationTime && typeof settings.durationTime !== 'function') {
313
+ settings.durationTime = _time2int(settings.durationTime);
314
+ }
315
+
316
+ if (settings.scrollDefault == 'now') {
317
+ settings.scrollDefault = _time2int(new Date());
318
+ } else if (settings.scrollDefault) {
319
+ settings.scrollDefault = _time2int(settings.scrollDefault);
320
+ } else if (settings.minTime) {
321
+ settings.scrollDefault = settings.minTime;
322
+ }
323
+
324
+ if (settings.scrollDefault) {
325
+ settings.scrollDefault = _roundTime(settings.scrollDefault, settings);
326
+ }
327
+
328
+ if (settings.timeFormat.match(/[gh]/)) {
329
+ settings._twelveHourTime = true;
330
+ }
331
+
332
+ if (settings.disableTimeRanges.length > 0) {
333
+ // convert string times to integers
334
+ for (var i in settings.disableTimeRanges) {
335
+ settings.disableTimeRanges[i] = [
336
+ _time2int(settings.disableTimeRanges[i][0]),
337
+ _time2int(settings.disableTimeRanges[i][1])
338
+ ];
339
+ }
340
+
341
+ // sort by starting time
342
+ settings.disableTimeRanges = settings.disableTimeRanges.sort(function(a, b){
343
+ return a[0] - b[0];
344
+ });
345
+
346
+ // merge any overlapping ranges
347
+ for (var i = settings.disableTimeRanges.length-1; i > 0; i--) {
348
+ if (settings.disableTimeRanges[i][0] <= settings.disableTimeRanges[i-1][1]) {
349
+ settings.disableTimeRanges[i-1] = [
350
+ Math.min(settings.disableTimeRanges[i][0], settings.disableTimeRanges[i-1][0]),
351
+ Math.max(settings.disableTimeRanges[i][1], settings.disableTimeRanges[i-1][1])
352
+ ];
353
+ settings.disableTimeRanges.splice(i, 1);
354
+ }
355
+ }
356
+ }
357
+
358
+ return settings;
359
+ }
360
+
361
+ function _render(self)
362
+ {
363
+ var settings = self.data('timepicker-settings');
364
+ var list = self.data('timepicker-list');
365
+
366
+ if (list && list.length) {
367
+ list.remove();
368
+ self.data('timepicker-list', false);
369
+ }
370
+
371
+ if (settings.useSelect) {
372
+ list = $('<select />', { 'class': 'ui-timepicker-select' });
373
+ var wrapped_list = list;
374
+ } else {
375
+ list = $('<ul />', { 'class': 'ui-timepicker-list' });
376
+
377
+ var wrapped_list = $('<div />', { 'class': 'ui-timepicker-wrapper', 'tabindex': -1 });
378
+ wrapped_list.css({'display':'none', 'position': 'absolute' }).append(list);
379
+ }
380
+
381
+ if (settings.noneOption) {
382
+ if (settings.noneOption === true) {
383
+ settings.noneOption = (settings.useSelect) ? 'Time...' : 'None';
384
+ }
385
+
386
+ if ($.isArray(settings.noneOption)) {
387
+ for (var i in settings.noneOption) {
388
+ if (parseInt(i, 10) === i){
389
+ var noneElement = _generateNoneElement(settings.noneOption[i], settings.useSelect);
390
+ list.append(noneElement);
391
+ }
392
+ }
393
+ } else {
394
+ var noneElement = _generateNoneElement(settings.noneOption, settings.useSelect);
395
+ list.append(noneElement);
396
+ }
397
+ }
398
+
399
+ if (settings.className) {
400
+ wrapped_list.addClass(settings.className);
401
+ }
402
+
403
+ if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
404
+ wrapped_list.addClass('ui-timepicker-with-duration');
405
+ wrapped_list.addClass('ui-timepicker-step-'+settings.step);
406
+ }
407
+
408
+ var durStart = settings.minTime;
409
+ if (typeof settings.durationTime === 'function') {
410
+ durStart = _time2int(settings.durationTime());
411
+ } else if (settings.durationTime !== null) {
412
+ durStart = settings.durationTime;
413
+ }
414
+ var start = (settings.minTime !== null) ? settings.minTime : 0;
415
+ var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1);
416
+
417
+ if (end <= start) {
418
+ // make sure the end time is greater than start time, otherwise there will be no list to show
419
+ end += _ONE_DAY;
420
+ }
421
+
422
+ if (end === _ONE_DAY-1 && settings.timeFormat.indexOf('H') !== -1) {
423
+ // show a 24:00 option when using military time
424
+ end = _ONE_DAY;
425
+ }
426
+
427
+ var dr = settings.disableTimeRanges;
428
+ var drCur = 0;
429
+ var drLen = dr.length;
430
+
431
+ for (var i=start; i <= end; i += settings.step*60) {
432
+ var timeInt = i;
433
+ var timeString = _int2time(timeInt, settings.timeFormat);
434
+
435
+ if (settings.useSelect) {
436
+ var row = $('<option />', { 'value': timeString });
437
+ row.text(timeString);
438
+ } else {
439
+ var row = $('<li />');
440
+ row.data('time', (timeInt <= 86400 ? timeInt : timeInt % 86400));
441
+ row.text(timeString);
442
+ }
443
+
444
+ if ((settings.minTime !== null || settings.durationTime !== null) && settings.showDuration) {
445
+ var durationString = _int2duration(i - durStart, settings.step);
446
+ if (settings.useSelect) {
447
+ row.text(row.text()+' ('+durationString+')');
448
+ } else {
449
+ var duration = $('<span />', { 'class': 'ui-timepicker-duration' });
450
+ duration.text(' ('+durationString+')');
451
+ row.append(duration);
452
+ }
453
+ }
454
+
455
+ if (drCur < drLen) {
456
+ if (timeInt >= dr[drCur][1]) {
457
+ drCur += 1;
458
+ }
459
+
460
+ if (dr[drCur] && timeInt >= dr[drCur][0] && timeInt < dr[drCur][1]) {
461
+ if (settings.useSelect) {
462
+ row.prop('disabled', true);
463
+ } else {
464
+ row.addClass('ui-timepicker-disabled');
465
+ }
466
+ }
467
+ }
468
+
469
+ list.append(row);
470
+ }
471
+
472
+ wrapped_list.data('timepicker-input', self);
473
+ self.data('timepicker-list', wrapped_list);
474
+
475
+ if (settings.useSelect) {
476
+ list.val(_roundAndFormatTime(self.val(), settings));
477
+ list.on('focus', function(){
478
+ $(this).data('timepicker-input').trigger('showTimepicker');
479
+ });
480
+ list.on('blur', function(){
481
+ $(this).data('timepicker-input').trigger('hideTimepicker');
482
+ });
483
+ list.on('change', function(){
484
+ _setTimeValue(self, $(this).val(), 'select');
485
+ });
486
+
487
+ self.hide().after(list);
488
+ } else {
489
+ var appendTo = settings.appendTo;
490
+ if (typeof appendTo === 'string') {
491
+ appendTo = $(appendTo);
492
+ } else if (typeof appendTo === 'function') {
493
+ appendTo = appendTo(self);
494
+ }
495
+ appendTo.append(wrapped_list);
496
+ _setSelected(self, list);
497
+
498
+ list.on('mousedown', 'li', function(e) {
499
+
500
+ // hack: temporarily disable the focus handler
501
+ // to deal with the fact that IE fires 'focus'
502
+ // events asynchronously
503
+ self.off('focus.timepicker');
504
+ self.on('focus.timepicker-ie-hack', function(){
505
+ self.off('focus.timepicker-ie-hack');
506
+ self.on('focus.timepicker', methods.show);
507
+ });
508
+
509
+ if (!_hideKeyboard(self)) {
510
+ self[0].focus();
511
+ }
512
+
513
+ // make sure only the clicked row is selected
514
+ list.find('li').removeClass('ui-timepicker-selected');
515
+ $(this).addClass('ui-timepicker-selected');
516
+
517
+ if (_selectValue(self)) {
518
+ self.trigger('hideTimepicker');
519
+ wrapped_list.hide();
520
+ }
521
+ });
522
+ }
523
+ }
524
+
525
+ function _generateNoneElement(optionValue, useSelect)
526
+ {
527
+ var label, className, value;
528
+
529
+ if (typeof optionValue == 'object') {
530
+ label = optionValue.label;
531
+ className = optionValue.className;
532
+ value = optionValue.value;
533
+ } else if (typeof optionValue == 'string') {
534
+ label = optionValue;
535
+ } else {
536
+ $.error('Invalid noneOption value');
537
+ }
538
+
539
+ if (useSelect) {
540
+ return $('<option />', {
541
+ 'value': value,
542
+ 'class': className,
543
+ 'text': label
544
+ });
545
+ } else {
546
+ return $('<li />', {
547
+ 'class': className,
548
+ 'text': label
549
+ }).data('time', value);
550
+ }
551
+ }
552
+
553
+ function _roundTime(seconds, settings)
554
+ {
555
+ if (!$.isNumeric(seconds)) {
556
+ seconds = _time2int(seconds);
557
+ }
558
+
559
+ if (seconds === null) {
560
+ return null;
561
+ } else {
562
+ var offset = seconds % (settings.step*60); // step is in minutes
563
+
564
+ if (offset >= settings.step*30) {
565
+ // if offset is larger than a half step, round up
566
+ seconds += (settings.step*60) - offset;
567
+ } else {
568
+ // round down
569
+ seconds -= offset;
570
+ }
571
+
572
+ return seconds;
573
+ }
574
+ }
575
+
576
+ function _roundAndFormatTime(seconds, settings)
577
+ {
578
+ seconds = _roundTime(seconds, settings);
579
+ if (seconds !== null) {
580
+ return _int2time(seconds, settings.timeFormat);
581
+ }
582
+ }
583
+
584
+ function _generateBaseDate()
585
+ {
586
+ return new Date(1970, 1, 1, 0, 0, 0);
587
+ }
588
+
589
+ // event handler to decide whether to close timepicker
590
+ function _closeHandler(e)
591
+ {
592
+ var target = $(e.target);
593
+ var input = target.closest('.ui-timepicker-input');
594
+ if (input.length === 0 && target.closest('.ui-timepicker-wrapper').length === 0) {
595
+ methods.hide();
596
+ $(document).unbind('.ui-timepicker');
597
+ }
598
+ }
599
+
600
+ function _hideKeyboard(self)
601
+ {
602
+ var settings = self.data('timepicker-settings');
603
+ return ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && settings.disableTouchKeyboard);
604
+ }
605
+
606
+ function _findRow(self, list, value)
607
+ {
608
+ if (!value && value !== 0) {
609
+ return false;
610
+ }
611
+
612
+ var settings = self.data('timepicker-settings');
613
+ var out = false;
614
+ var halfStep = settings.step*30;
615
+
616
+ // loop through the menu items
617
+ list.find('li').each(function(i, obj) {
618
+ var jObj = $(obj);
619
+ if (typeof jObj.data('time') != 'number') {
620
+ return;
621
+ }
622
+
623
+ var offset = jObj.data('time') - value;
624
+
625
+ // check if the value is less than half a step from each row
626
+ if (Math.abs(offset) < halfStep || offset == halfStep) {
627
+ out = jObj;
628
+ return false;
629
+ }
630
+ });
631
+
632
+ return out;
633
+ }
634
+
635
+ function _setSelected(self, list)
636
+ {
637
+ list.find('li').removeClass('ui-timepicker-selected');
638
+
639
+ var timeValue = _time2int(_getTimeValue(self), self.data('timepicker-settings'));
640
+ if (timeValue === null) {
641
+ return;
642
+ }
643
+
644
+ var selected = _findRow(self, list, timeValue);
645
+ if (selected) {
646
+
647
+ var topDelta = selected.offset().top - list.offset().top;
648
+
649
+ if (topDelta + selected.outerHeight() > list.outerHeight() || topDelta < 0) {
650
+ list.scrollTop(list.scrollTop() + selected.position().top - selected.outerHeight());
651
+ }
652
+
653
+ selected.addClass('ui-timepicker-selected');
654
+ }
655
+ }
656
+
657
+
658
+ function _formatValue(e)
659
+ {
660
+ if (this.value === '') {
661
+ return;
662
+ }
663
+
664
+ var self = $(this);
665
+ var list = self.data('timepicker-list');
666
+
667
+ if (self.is(':focus') && (!e || e.type != 'change')) {
668
+ return;
669
+ }
670
+
671
+ var seconds = _time2int(this.value);
672
+
673
+ if (seconds === null) {
674
+ self.trigger('timeFormatError');
675
+ return;
676
+ }
677
+
678
+ var settings = self.data('timepicker-settings');
679
+ var rangeError = false;
680
+ // check that the time in within bounds
681
+ if (settings.minTime !== null && seconds < settings.minTime) {
682
+ rangeError = true;
683
+ } else if (settings.maxTime !== null && seconds > settings.maxTime) {
684
+ rangeError = true;
685
+ }
686
+
687
+ // check that time isn't within disabled time ranges
688
+ $.each(settings.disableTimeRanges, function(){
689
+ if (seconds >= this[0] && seconds < this[1]) {
690
+ rangeError = true;
691
+ return false;
692
+ }
693
+ });
694
+
695
+ if (settings.forceRoundTime) {
696
+ var offset = seconds % (settings.step*60); // step is in minutes
697
+
698
+ if (offset >= settings.step*30) {
699
+ // if offset is larger than a half step, round up
700
+ seconds += (settings.step*60) - offset;
701
+ } else {
702
+ // round down
703
+ seconds -= offset;
704
+ }
705
+ }
706
+
707
+ var prettyTime = _int2time(seconds, settings.timeFormat);
708
+
709
+ if (rangeError) {
710
+ if (_setTimeValue(self, prettyTime, 'error')) {
711
+ self.trigger('timeRangeError');
712
+ }
713
+ } else {
714
+ _setTimeValue(self, prettyTime);
715
+ }
716
+ }
717
+
718
+ function _getTimeValue(self)
719
+ {
720
+ if (self.is('input')) {
721
+ return self.val();
722
+ } else {
723
+ // use the element's data attributes to store values
724
+ return self.data('ui-timepicker-value');
725
+ }
726
+ }
727
+
728
+ function _setTimeValue(self, value, source)
729
+ {
730
+ if (self.is('input')) {
731
+ self.val(value);
732
+
733
+ var settings = self.data('timepicker-settings');
734
+ if (settings.useSelect) {
735
+ self.data('timepicker-list').val(_roundAndFormatTime(value, settings));
736
+ }
737
+ }
738
+
739
+ if (self.data('ui-timepicker-value') != value) {
740
+ self.data('ui-timepicker-value', value);
741
+ if (source == 'select') {
742
+ self.trigger('selectTime').trigger('changeTime').trigger('change');
743
+ } else if (source != 'error') {
744
+ self.trigger('changeTime');
745
+ }
746
+
747
+ return true;
748
+ } else {
749
+ self.trigger('selectTime');
750
+ return false;
751
+ }
752
+ }
753
+
754
+ /*
755
+ * Keyboard navigation via arrow keys
756
+ */
757
+ function _keydownhandler(e)
758
+ {
759
+ var self = $(this);
760
+ var list = self.data('timepicker-list');
761
+
762
+ if (!list || !list.is(':visible')) {
763
+ if (e.keyCode == 40) {
764
+ // show the list!
765
+ methods.show.call(self.get(0));
766
+ list = self.data('timepicker-list');
767
+ if (!_hideKeyboard(self)) {
768
+ self.focus();
769
+ }
770
+ } else {
771
+ return true;
772
+ }
773
+ }
774
+
775
+ switch (e.keyCode) {
776
+
777
+ case 13: // return
778
+ if (_selectValue(self)) {
779
+ methods.hide.apply(this);
780
+ }
781
+
782
+ e.preventDefault();
783
+ return false;
784
+
785
+ case 38: // up
786
+ var selected = list.find('.ui-timepicker-selected');
787
+
788
+ if (!selected.length) {
789
+ list.find('li').each(function(i, obj) {
790
+ if ($(obj).position().top > 0) {
791
+ selected = $(obj);
792
+ return false;
793
+ }
794
+ });
795
+ selected.addClass('ui-timepicker-selected');
796
+
797
+ } else if (!selected.is(':first-child')) {
798
+ selected.removeClass('ui-timepicker-selected');
799
+ selected.prev().addClass('ui-timepicker-selected');
800
+
801
+ if (selected.prev().position().top < selected.outerHeight()) {
802
+ list.scrollTop(list.scrollTop() - selected.outerHeight());
803
+ }
804
+ }
805
+
806
+ return false;
807
+
808
+ case 40: // down
809
+ selected = list.find('.ui-timepicker-selected');
810
+
811
+ if (selected.length === 0) {
812
+ list.find('li').each(function(i, obj) {
813
+ if ($(obj).position().top > 0) {
814
+ selected = $(obj);
815
+ return false;
816
+ }
817
+ });
818
+
819
+ selected.addClass('ui-timepicker-selected');
820
+ } else if (!selected.is(':last-child')) {
821
+ selected.removeClass('ui-timepicker-selected');
822
+ selected.next().addClass('ui-timepicker-selected');
823
+
824
+ if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) {
825
+ list.scrollTop(list.scrollTop() + selected.outerHeight());
826
+ }
827
+ }
828
+
829
+ return false;
830
+
831
+ case 27: // escape
832
+ list.find('li').removeClass('ui-timepicker-selected');
833
+ methods.hide();
834
+ break;
835
+
836
+ case 9: //tab
837
+ methods.hide();
838
+ break;
839
+
840
+ default:
841
+ return true;
842
+ }
843
+ }
844
+
845
+ /*
846
+ * Time typeahead
847
+ */
848
+ function _keyuphandler(e)
849
+ {
850
+ var self = $(this);
851
+ var list = self.data('timepicker-list');
852
+
853
+ if (!list || !list.is(':visible')) {
854
+ return true;
855
+ }
856
+
857
+ if (!self.data('timepicker-settings').typeaheadHighlight) {
858
+ list.find('li').removeClass('ui-timepicker-selected');
859
+ return true;
860
+ }
861
+
862
+ switch (e.keyCode) {
863
+
864
+ case 96: // numpad numerals
865
+ case 97:
866
+ case 98:
867
+ case 99:
868
+ case 100:
869
+ case 101:
870
+ case 102:
871
+ case 103:
872
+ case 104:
873
+ case 105:
874
+ case 48: // numerals
875
+ case 49:
876
+ case 50:
877
+ case 51:
878
+ case 52:
879
+ case 53:
880
+ case 54:
881
+ case 55:
882
+ case 56:
883
+ case 57:
884
+ case 65: // a
885
+ case 77: // m
886
+ case 80: // p
887
+ case 186: // colon
888
+ case 8: // backspace
889
+ case 46: // delete
890
+ _setSelected(self, list);
891
+ break;
892
+
893
+ default:
894
+ // list.find('li').removeClass('ui-timepicker-selected');
895
+ return;
896
+ }
897
+ }
898
+
899
+ function _selectValue(self)
900
+ {
901
+ var settings = self.data('timepicker-settings');
902
+ var list = self.data('timepicker-list');
903
+ var timeValue = null;
904
+
905
+ var cursor = list.find('.ui-timepicker-selected');
906
+
907
+ if (cursor.hasClass('ui-timepicker-disabled')) {
908
+ return false;
909
+ }
910
+
911
+ if (cursor.length) {
912
+ // selected value found
913
+ timeValue = cursor.data('time');
914
+ }
915
+
916
+ if (timeValue !== null) {
917
+ if (typeof timeValue == 'string') {
918
+ self.val(timeValue);
919
+ } else {
920
+ var timeString = _int2time(timeValue, settings.timeFormat);
921
+ _setTimeValue(self, timeString, 'select');
922
+ }
923
+ }
924
+
925
+ //self.trigger('change').trigger('selectTime');
926
+ return true;
927
+ }
928
+
929
+ function _int2duration(seconds, step)
930
+ {
931
+ seconds = Math.abs(seconds);
932
+ var minutes = Math.round(seconds/60),
933
+ duration = [],
934
+ hours, mins;
935
+
936
+ if (minutes < 60) {
937
+ // Only show (x mins) under 1 hour
938
+ duration = [minutes, _lang.mins];
939
+ } else {
940
+ hours = Math.floor(minutes/60);
941
+ mins = minutes%60;
942
+
943
+ // Show decimal notation (eg: 1.5 hrs) for 30 minute steps
944
+ if (step == 30 && mins == 30) {
945
+ hours += _lang.decimal + 5;
946
+ }
947
+
948
+ duration.push(hours);
949
+ duration.push(hours == 1 ? _lang.hr : _lang.hrs);
950
+
951
+ // Show remainder minutes notation (eg: 1 hr 15 mins) for non-30 minute steps
952
+ // and only if there are remainder minutes to show
953
+ if (step != 30 && mins) {
954
+ duration.push(mins);
955
+ duration.push(_lang.mins);
956
+ }
957
+ }
958
+
959
+ return duration.join(' ');
960
+ }
961
+
962
+ function _int2time(seconds, format)
963
+ {
964
+ if (seconds === null) {
965
+ return;
966
+ }
967
+
968
+ var time = new Date(_baseDate.valueOf() + (seconds*1000));
969
+
970
+ if (isNaN(time.getTime())) {
971
+ return;
972
+ }
973
+
974
+ var output = '';
975
+ var hour, code;
976
+ for (var i=0; i<format.length; i++) {
977
+
978
+ code = format.charAt(i);
979
+ switch (code) {
980
+
981
+ case 'a':
982
+ output += (time.getHours() > 11) ? _lang.pm : _lang.am;
983
+ break;
984
+
985
+ case 'A':
986
+ output += (time.getHours() > 11) ? _lang.PM : _lang.AM;
987
+ break;
988
+
989
+ case 'g':
990
+ hour = time.getHours() % 12;
991
+ output += (hour === 0) ? '12' : hour;
992
+ break;
993
+
994
+ case 'G':
995
+ output += time.getHours();
996
+ break;
997
+
998
+ case 'h':
999
+ hour = time.getHours() % 12;
1000
+
1001
+ if (hour !== 0 && hour < 10) {
1002
+ hour = '0'+hour;
1003
+ }
1004
+
1005
+ output += (hour === 0) ? '12' : hour;
1006
+ break;
1007
+
1008
+ case 'H':
1009
+ hour = time.getHours();
1010
+ if (seconds === _ONE_DAY) hour = 24;
1011
+ output += (hour > 9) ? hour : '0'+hour;
1012
+ break;
1013
+
1014
+ case 'i':
1015
+ var minutes = time.getMinutes();
1016
+ output += (minutes > 9) ? minutes : '0'+minutes;
1017
+ break;
1018
+
1019
+ case 's':
1020
+ seconds = time.getSeconds();
1021
+ output += (seconds > 9) ? seconds : '0'+seconds;
1022
+ break;
1023
+
1024
+ case '\\':
1025
+ // escape character; add the next character and skip ahead
1026
+ i++;
1027
+ output += format.charAt(i);
1028
+ break;
1029
+
1030
+ default:
1031
+ output += code;
1032
+ }
1033
+ }
1034
+
1035
+ return output;
1036
+ }
1037
+
1038
+ function _time2int(timeString, settings)
1039
+ {
1040
+ if (timeString === '') return null;
1041
+ if (!timeString || timeString+0 == timeString) return timeString;
1042
+
1043
+ if (typeof(timeString) == 'object') {
1044
+ return timeString.getHours()*3600 + timeString.getMinutes()*60 + timeString.getSeconds();
1045
+ }
1046
+
1047
+ timeString = timeString.toLowerCase();
1048
+
1049
+ var d = new Date(0);
1050
+ var time;
1051
+
1052
+ // try to parse time input
1053
+ time = timeString.match(/^([0-2]?[0-9])\W?([0-5][0-9])?\W?([0-5][0-9])?\s*([pa]?)m?$/);
1054
+
1055
+ if (!time) {
1056
+ return null;
1057
+ }
1058
+
1059
+ var hour = parseInt(time[1]*1, 10);
1060
+ var ampm = time[4];
1061
+ var hours = hour;
1062
+
1063
+ if (ampm) {
1064
+ if (hour == 12) {
1065
+ hours = (time[4] == 'p') ? 12 : 0;
1066
+ } else {
1067
+ hours = (hour + (time[4] == 'p' ? 12 : 0));
1068
+ }
1069
+ }
1070
+
1071
+ var minutes = ( time[2]*1 || 0 );
1072
+ var seconds = ( time[3]*1 || 0 );
1073
+ var timeInt = hours*3600 + minutes*60 + seconds;
1074
+
1075
+ // if no am/pm provided, intelligently guess based on the scrollDefault
1076
+ if (!ampm && settings && settings._twelveHourTime && settings.scrollDefault) {
1077
+ var delta = timeInt - settings.scrollDefault;
1078
+ if (delta < 0 && delta >= _ONE_DAY / -2) {
1079
+ timeInt = (timeInt + (_ONE_DAY / 2)) % _ONE_DAY;
1080
+ }
1081
+ }
1082
+
1083
+ return timeInt
1084
+ }
1085
+
1086
+ function _pad2(n) {
1087
+ return ("0" + n).slice(-2);
1088
+ }
1089
+
1090
+ // Plugin entry
1091
+ $.fn.timepicker = function(method)
1092
+ {
1093
+ if (!this.length) return this;
1094
+ if (methods[method]) {
1095
+ // check if this element is a timepicker
1096
+ if (!this.hasClass('ui-timepicker-input')) {
1097
+ return this;
1098
+ }
1099
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
1100
+ }
1101
+ else if(typeof method === "object" || !method) { return methods.init.apply(this, arguments); }
1102
+ else { $.error("Method "+ method + " does not exist on jQuery.timepicker"); }
1103
+ };
1104
+ // Global defaults
1105
+ $.fn.timepicker.defaults = {
1106
+ className: null,
1107
+ minTime: null,
1108
+ maxTime: null,
1109
+ durationTime: null,
1110
+ step: 30,
1111
+ showDuration: false,
1112
+ showOnFocus: true,
1113
+ timeFormat: 'g:ia',
1114
+ scrollDefault: null,
1115
+ selectOnBlur: false,
1116
+ disableTouchKeyboard: false,
1117
+ forceRoundTime: false,
1118
+ appendTo: 'body',
1119
+ orientation: 'ltr',
1120
+ disableTimeRanges: [],
1121
+ closeOnWindowScroll: false,
1122
+ typeaheadHighlight: true,
1123
+ noneOption: false
1124
+ };
1125
+ }));