undercase 0.2.29 → 0.2.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }));