jquery-timepicker-rails 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jquery-timepicker-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tanguy Krotoff
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 ADDED
@@ -0,0 +1,42 @@
1
+ # jQuery timepicker for Rails
2
+
3
+ jquery-timepicker packaged for the Rails 3.1+ asset pipeline.
4
+ Check [jquery-timepicker project home page](http://github.com/jonthornton/jquery-timepicker).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'jquery-timepicker-rails'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install jquery-timepicker-rails
19
+
20
+ ## Usage
21
+
22
+ Add the following JavaScript file to `app/assets/javascripts/application.js`:
23
+
24
+ //= require jquery.timepicker.js
25
+
26
+ Add the following stylesheet file to `app/assets/stylesheets/application.css`:
27
+
28
+ *= require jquery.timepicker.css
29
+
30
+ Optionally, you can also use `datepair.js`:
31
+
32
+ //= require datepair.js
33
+
34
+ Most people will prefer to copy-paste this file in order to customize it.
35
+
36
+ jquery-timepicker depends on jQuery and [bootstrap-datepicker](http://github.com/eternicode/bootstrap-datepicker) or [jQuery UI Datepicker](http://jqueryui.com/demos/datepicker/).
37
+
38
+ ## License
39
+
40
+ jquery-timepicker is being developed by [Jon Thornton](http://jonthornton.com/) and is under [MIT license](http://en.wikipedia.org/wiki/MIT_License).
41
+
42
+ This gem is also licensed under [MIT license](https://raw.github.com/tkrotoff/jquery-timepicker-rails/master/LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/jquery-timepicker-rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tanguy Krotoff (jQuery plugin by Jon Thornton)"]
6
+ gem.email = ["tkrotoff@gmail.com"]
7
+ gem.description = %q{A jQuery timepicker plugin inspired by Google Calendar}
8
+ gem.summary = %q{jquery-timepicker packaged for the Rails 3.1+ asset pipeline}
9
+ gem.homepage = "http://github.com/tkrotoff/jquery-timepicker-rails"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "jquery-timepicker-rails"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Jquery::Timepicker::Rails::VERSION
17
+
18
+ gem.add_dependency 'railties', '>= 3.1.0'
19
+ end
@@ -0,0 +1,10 @@
1
+ require "jquery-timepicker-rails/version"
2
+
3
+ module Jquery
4
+ module Timepicker
5
+ module Rails
6
+ class Engine < ::Rails::Engine
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Jquery
2
+ module Timepicker
3
+ module Rails
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,197 @@
1
+ $(function() {
2
+
3
+ $('.datepair input.date').each(function(){
4
+ var $this = $(this);
5
+ $this.datepicker({ 'dateFormat': 'm/d/yy' });
6
+
7
+ if ($this.hasClass('start') || $this.hasClass('end')) {
8
+ $this.on('changeDate change', doDatepair);
9
+ }
10
+
11
+ });
12
+
13
+ $('.datepair input.time').each(function() {
14
+ var $this = $(this);
15
+ var opts = { 'showDuration': true, 'timeFormat': 'g:ia', 'scrollDefaultNow': true };
16
+
17
+ if ($this.hasClass('start') || $this.hasClass('end')) {
18
+ opts.onSelect = doDatepair;
19
+ }
20
+
21
+ $this.timepicker(opts);
22
+ });
23
+
24
+ $('.datepair').each(initDatepair);
25
+
26
+ function initDatepair()
27
+ {
28
+ var container = $(this);
29
+
30
+ var startDateInput = container.find('input.start.date');
31
+ var endDateInput = container.find('input.end.date');
32
+ var dateDelta = 0;
33
+
34
+ if (startDateInput.length && endDateInput.length) {
35
+ var startDate = new Date(startDateInput.val());
36
+ var endDate = new Date(endDateInput.val());
37
+
38
+ dateDelta = endDate.getTime() - startDate.getTime();
39
+
40
+ container.data('dateDelta', dateDelta);
41
+ }
42
+
43
+ var startTimeInput = container.find('input.start.time');
44
+ var endTimeInput = container.find('input.end.time');
45
+
46
+ if (startTimeInput.length && endTimeInput.length) {
47
+ var startInt = startTimeInput.timepicker('getSecondsFromMidnight');
48
+ var endInt = endTimeInput.timepicker('getSecondsFromMidnight');
49
+
50
+ container.data('timeDelta', endInt - startInt);
51
+
52
+ if (dateDelta < 86400000) {
53
+ endTimeInput.timepicker('option', 'minTime', startInt);
54
+ }
55
+ }
56
+ }
57
+
58
+ function doDatepair()
59
+ {
60
+ var target = $(this);
61
+ if (target.val() == '') {
62
+ return;
63
+ }
64
+
65
+ var container = target.closest('.datepair');
66
+
67
+ if (target.hasClass('date')) {
68
+ updateDatePair(target, container);
69
+
70
+ } else if (target.hasClass('time')) {
71
+ updateTimePair(target, container);
72
+ }
73
+ }
74
+
75
+ function updateDatePair(target, container)
76
+ {
77
+ var start = container.find('input.start.date');
78
+ var end = container.find('input.end.date');
79
+
80
+ if (!start.length || !end.length) {
81
+ return;
82
+ }
83
+
84
+ var startDate = new Date(start.val());
85
+ var endDate = new Date(end.val());
86
+
87
+ var oldDelta = container.data('dateDelta');
88
+
89
+ if (oldDelta && target.hasClass('start')) {
90
+ var newEnd = new Date(startDate.getTime()+oldDelta);
91
+ end.val(newEnd.format('m/d/Y'));
92
+ end.datepicker('update');
93
+ return;
94
+
95
+ } else {
96
+ var newDelta = endDate.getTime() - startDate.getTime();
97
+
98
+ if (newDelta < 0) {
99
+ newDelta = 0;
100
+
101
+ if (target.hasClass('start')) {
102
+ end.val(startDate.format('m/d/Y'));
103
+ end.datepicker('update');
104
+ } else if (target.hasClass('end')) {
105
+ start.val(endDate.format('m/d/Y'));
106
+ start.datepicker('update');
107
+ }
108
+ }
109
+
110
+ if (newDelta < 86400000) {
111
+ var startTimeVal = container.find('input.start.time').val();
112
+
113
+ if (startTimeVal) {
114
+ container.find('input.end.time').timepicker('option', {'minTime': startTimeVal});
115
+ }
116
+ } else {
117
+ container.find('input.end.time').timepicker('option', {'minTime': null});
118
+ }
119
+
120
+ container.data('dateDelta', newDelta);
121
+ }
122
+ }
123
+
124
+ function updateTimePair(target, container)
125
+ {
126
+ var start = container.find('input.start.time');
127
+ var end = container.find('input.end.time');
128
+
129
+ if (!start.length || !end.length) {
130
+ return;
131
+ }
132
+
133
+ var startInt = start.timepicker('getSecondsFromMidnight');
134
+ var endInt = end.timepicker('getSecondsFromMidnight');
135
+
136
+ var oldDelta = container.data('timeDelta');
137
+ var dateDelta = container.data('dateDelta');
138
+
139
+ if (target.hasClass('start') && (!dateDelta || dateDelta < 86400000)) {
140
+ end.timepicker('option', 'minTime', startInt);
141
+ }
142
+
143
+ var endDateAdvance = 0;
144
+ var newDelta;
145
+
146
+ if (oldDelta && target.hasClass('start')) {
147
+ // lock the duration and advance the end time
148
+
149
+ var newEnd = (startInt+oldDelta)%86400;
150
+
151
+ if (newEnd < 0) {
152
+ newEnd += 86400;
153
+ }
154
+
155
+ end.timepicker('setTime', newEnd);
156
+ newDelta = newEnd - startInt;
157
+ } else if (startInt !== null && endInt !== null) {
158
+ newDelta = endInt - startInt;
159
+ } else {
160
+ return;
161
+ }
162
+
163
+ container.data('timeDelta', newDelta);
164
+
165
+ if (newDelta < 0 && (!oldDelta || oldDelta > 0)) {
166
+ // overnight time span. advance the end date 1 day
167
+ var endDateAdvance = 86400000;
168
+
169
+ } else if (newDelta > 0 && oldDelta < 0) {
170
+ // switching from overnight to same-day time span. decrease the end date 1 day
171
+ var endDateAdvance = -86400000;
172
+ }
173
+
174
+ var startInput = container.find('.start.date');
175
+ var endInput = container.find('.end.date');
176
+
177
+ if (startInput.val() && !endInput.val()) {
178
+ endInput.val(startInput.val());
179
+ endInput.datepicker('update');
180
+ dateDelta = 0;
181
+ container.data('dateDelta', 0);
182
+ }
183
+
184
+ if (endDateAdvance != 0) {
185
+ if (dateDelta || dateDelta === 0) {
186
+ var endDate = new Date(endInput.val());
187
+ var newEnd = new Date(endDate.getTime() + endDateAdvance);
188
+ endInput.val(newEnd.format('m/d/Y'));
189
+ endInput.datepicker('update');
190
+ container.data('dateDelta', dateDelta + endDateAdvance);
191
+ }
192
+ }
193
+ }
194
+ });
195
+
196
+ // Simulates PHP's date function
197
+ Date.prototype.format=function(format){var returnStr='';var replace=Date.replaceChars;for(var i=0;i<format.length;i++){var curChar=format.charAt(i);if(replace[curChar]){returnStr+=replace[curChar].call(this);}else{returnStr+=curChar;}}return returnStr;};Date.replaceChars={shortMonths:['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],longMonths:['January','February','March','April','May','June','July','August','September','October','November','December'],shortDays:['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],longDays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],d:function(){return(this.getDate()<10?'0':'')+this.getDate();},D:function(){return Date.replaceChars.shortDays[this.getDay()];},j:function(){return this.getDate();},l:function(){return Date.replaceChars.longDays[this.getDay()];},N:function(){return this.getDay()+1;},S:function(){return(this.getDate()%10==1&&this.getDate()!=11?'st':(this.getDate()%10==2&&this.getDate()!=12?'nd':(this.getDate()%10==3&&this.getDate()!=13?'rd':'th')));},w:function(){return this.getDay();},z:function(){return"Not Yet Supported";},W:function(){return"Not Yet Supported";},F:function(){return Date.replaceChars.longMonths[this.getMonth()];},m:function(){return(this.getMonth()<9?'0':'')+(this.getMonth()+1);},M:function(){return Date.replaceChars.shortMonths[this.getMonth()];},n:function(){return this.getMonth()+1;},t:function(){return"Not Yet Supported";},L:function(){return(((this.getFullYear()%4==0)&&(this.getFullYear()%100!=0))||(this.getFullYear()%400==0))?'1':'0';},o:function(){return"Not Supported";},Y:function(){return this.getFullYear();},y:function(){return(''+this.getFullYear()).substr(2);},a:function(){return this.getHours()<12?'am':'pm';},A:function(){return this.getHours()<12?'AM':'PM';},B:function(){return"Not Yet Supported";},g:function(){return this.getHours()%12||12;},G:function(){return this.getHours();},h:function(){return((this.getHours()%12||12)<10?'0':'')+(this.getHours()%12||12);},H:function(){return(this.getHours()<10?'0':'')+this.getHours();},i:function(){return(this.getMinutes()<10?'0':'')+this.getMinutes();},s:function(){return(this.getSeconds()<10?'0':'')+this.getSeconds();},e:function(){return"Not Yet Supported";},I:function(){return"Not Supported";},O:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+'00';},P:function(){return(-this.getTimezoneOffset()<0?'-':'+')+(Math.abs(this.getTimezoneOffset()/60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()/60))+':'+(Math.abs(this.getTimezoneOffset()%60)<10?'0':'')+(Math.abs(this.getTimezoneOffset()%60));},T:function(){var m=this.getMonth();this.setMonth(0);var result=this.toTimeString().replace(/^.+ \(?([^\)]+)\)?$/,'$1');this.setMonth(m);return result;},Z:function(){return-this.getTimezoneOffset()*60;},c:function(){return this.format("Y-m-d")+"T"+this.format("H:i:sP");},r:function(){return this.toString();},U:function(){return this.getTime()/1000;}};
@@ -0,0 +1,566 @@
1
+ /************************
2
+ jquery-timepicker
3
+ http://jonthornton.github.com/jquery-timepicker/
4
+
5
+ requires jQuery 1.6+
6
+ ************************/
7
+
8
+
9
+ !(function($)
10
+ {
11
+ var _baseDate = new Date(); _baseDate.setHours(0); _baseDate.setMinutes(0); _baseDate.setSeconds(0);
12
+ var _ONE_DAY = 86400;
13
+ var _defaults = {
14
+ className: null,
15
+ minTime: null,
16
+ maxTime: null,
17
+ durationTime: null,
18
+ step: 30,
19
+ showDuration: false,
20
+ timeFormat: 'g:ia',
21
+ scrollDefaultNow: false,
22
+ scrollDefaultTime: false,
23
+ selectOnBlur: false
24
+ };
25
+ var _lang = {
26
+ decimal: '.',
27
+ mins: 'mins',
28
+ hr: 'hr',
29
+ hrs: 'hrs'
30
+ };
31
+
32
+ var methods =
33
+ {
34
+ init: function(options)
35
+ {
36
+ return this.each(function()
37
+ {
38
+ var self = $(this);
39
+
40
+ // convert dropdowns to text input
41
+ if (self[0].tagName == 'SELECT') {
42
+ var input = $('<input />');
43
+ var attrs = { 'type': 'text', 'value': self.val() };
44
+ var raw_attrs = self[0].attributes;
45
+
46
+ for (var i=0; i < raw_attrs.length; i++) {
47
+ attrs[raw_attrs[i].nodeName] = raw_attrs[i].nodeValue;
48
+ }
49
+
50
+ input.attr(attrs);
51
+ self.replaceWith(input);
52
+ self = input;
53
+ }
54
+
55
+ var settings = $.extend({}, _defaults);
56
+
57
+ if (options) {
58
+ settings = $.extend(settings, options);
59
+ }
60
+
61
+ if (settings.minTime) {
62
+ settings.minTime = _time2int(settings.minTime);
63
+ }
64
+
65
+ if (settings.maxTime) {
66
+ settings.maxTime = _time2int(settings.maxTime);
67
+ }
68
+
69
+ if (settings.durationTime) {
70
+ settings.durationTime = _time2int(settings.durationTime);
71
+ }
72
+
73
+ if (settings.lang) {
74
+ _lang = $.extend(_lang, settings.lang);
75
+ }
76
+
77
+ self.data("settings", settings);
78
+ self.attr('autocomplete', 'off');
79
+ self.click(methods.show).focus(methods.show).keydown(_keyhandler);
80
+ self.addClass('ui-timepicker-input');
81
+
82
+ if (self.val()) {
83
+ var prettyTime = _int2time(_time2int(self.val()), settings.timeFormat);
84
+ self.val(prettyTime);
85
+ }
86
+
87
+ var container = $('<span class="ui-timepicker-container" />');
88
+ self.wrap(container);
89
+
90
+ // close the dropdown when container loses focus
91
+ $("body").attr("tabindex", -1).focusin(function(e) {
92
+ if ($(e.target).closest('.ui-timepicker-container').length == 0) {
93
+ methods.hide();
94
+ }
95
+ });
96
+
97
+ });
98
+ },
99
+
100
+ show: function(e)
101
+ {
102
+ var self = $(this);
103
+ var list = self.siblings('.ui-timepicker-list');
104
+
105
+ // check if a flag was set to close this picker
106
+ if (self.hasClass('ui-timepicker-hideme')) {
107
+ self.removeClass('ui-timepicker-hideme');
108
+ list.hide();
109
+ return;
110
+ }
111
+
112
+ if (list.is(':visible')) {
113
+ return;
114
+ }
115
+
116
+ // make sure other pickers are hidden
117
+ methods.hide();
118
+
119
+ // check if list needs to be rendered
120
+ if (list.length == 0) {
121
+ _render(self);
122
+ list = self.siblings('.ui-timepicker-list');
123
+ }
124
+
125
+ var topMargin = parseInt(self.css('marginTop').slice(0, -2));
126
+ if ((self.offset().top + self.outerHeight(true) + list.outerHeight()) > $(window).height() + $(window).scrollTop()) {
127
+ // position the dropdown on top
128
+ list.css({ "top": self.position().top + topMargin - list.outerHeight() });
129
+ } else {
130
+ // put it under the input
131
+ list.css({ "top": self.position().top + topMargin + self.outerHeight() });
132
+ }
133
+
134
+ list.show();
135
+
136
+ var settings = self.data("settings");
137
+ // position scrolling
138
+ var selected = list.find('.ui-timepicker-selected');
139
+
140
+ if (!selected.length) {
141
+ if (self.val()) {
142
+ selected = _findRow(self, list, _time2int(self.val()));
143
+ } else if (settings.minTime === null && settings.scrollDefaultNow) {
144
+ selected = _findRow(self, list, _time2int(new Date()));
145
+ } else if (settings.scrollDefaultTime !== false) {
146
+ selected = _findRow(self, list, _time2int(settings.scrollDefaultTime));
147
+ }
148
+ }
149
+
150
+ if (selected && selected.length) {
151
+ var topOffset = list.scrollTop() + selected.position().top - selected.outerHeight();
152
+ list.scrollTop(topOffset);
153
+ } else {
154
+ list.scrollTop(0);
155
+ }
156
+
157
+ self.trigger('showTimepicker');
158
+ },
159
+
160
+ hide: function(e)
161
+ {
162
+ $('.ui-timepicker-list:visible').each(function() {
163
+ var list = $(this);
164
+ var self = list.siblings('.ui-timepicker-input');
165
+ var settings = self.data("settings");
166
+ if (settings.selectOnBlur) {
167
+ _selectValue(self);
168
+ }
169
+
170
+ list.hide();
171
+ self.trigger('hideTimepicker');
172
+ });
173
+ },
174
+
175
+ option: function(key, value)
176
+ {
177
+ var self = $(this);
178
+ var settings = self.data("settings");
179
+ var list = self.siblings('.ui-timepicker-list');
180
+
181
+ if (typeof key == 'object') {
182
+ settings = $.extend(settings, key);
183
+
184
+ } else if (typeof key == 'string' && typeof value != 'undefined') {
185
+ settings[key] = value;
186
+
187
+ } else if (typeof key == 'string') {
188
+ return settings[key];
189
+ }
190
+
191
+ if (settings.minTime) {
192
+ settings.minTime = _time2int(settings.minTime);
193
+ }
194
+
195
+ if (settings.maxTime) {
196
+ settings.maxTime = _time2int(settings.maxTime);
197
+ }
198
+
199
+ if (settings.durationTime) {
200
+ settings.durationTime = _time2int(settings.durationTime);
201
+ }
202
+
203
+ self.data("settings", settings);
204
+ list.remove();
205
+ },
206
+
207
+ getSecondsFromMidnight: function()
208
+ {
209
+ return _time2int($(this).val());
210
+ },
211
+
212
+ getTime: function()
213
+ {
214
+ return new Date(_baseDate.valueOf() + (_time2int($(this).val())*1000));
215
+ },
216
+
217
+ setTime: function(value)
218
+ {
219
+ var self = $(this);
220
+ var prettyTime = _int2time(_time2int(value), self.data('settings').timeFormat);
221
+ self.val(prettyTime);
222
+ }
223
+
224
+ };
225
+
226
+ // private methods
227
+
228
+ function _render(self)
229
+ {
230
+ var settings = self.data("settings");
231
+ var list = self.siblings('.ui-timepicker-list');
232
+
233
+ if (list && list.length) {
234
+ list.remove();
235
+ }
236
+
237
+ list = $('<ul />');
238
+ list.attr('tabindex', -1);
239
+ list.addClass('ui-timepicker-list');
240
+ if (settings.className) {
241
+ list.addClass(settings.className);
242
+ }
243
+
244
+ var zIndex = self.css('zIndex');
245
+ zIndex = (zIndex+0 == zIndex) ? zIndex+2 : 2;
246
+ list.css({'display':'none', 'position': 'absolute', "left":(self.position().left), 'zIndex': zIndex });
247
+
248
+ if (settings.minTime !== null && settings.showDuration) {
249
+ list.addClass('ui-timepicker-with-duration');
250
+ }
251
+
252
+ var durStart = (settings.durationTime !== null) ? settings.durationTime : settings.minTime;
253
+ var start = (settings.minTime !== null) ? settings.minTime : 0;
254
+ var end = (settings.maxTime !== null) ? settings.maxTime : (start + _ONE_DAY - 1);
255
+
256
+ if (end <= start) {
257
+ // make sure the end time is greater than start time, otherwise there will be no list to show
258
+ end += _ONE_DAY;
259
+ }
260
+
261
+ for (var i=start; i <= end; i += settings.step*60) {
262
+ var timeInt = i%_ONE_DAY;
263
+ var row = $('<li />');
264
+ row.data('time', timeInt)
265
+ row.text(_int2time(timeInt, settings.timeFormat));
266
+
267
+ if (settings.minTime !== null && settings.showDuration) {
268
+ var duration = $('<span />');
269
+ duration.addClass('ui-timepicker-duration');
270
+ duration.text(' ('+_int2duration(i - durStart)+')');
271
+ row.append(duration)
272
+ }
273
+
274
+ list.append(row);
275
+ }
276
+
277
+ self.after(list);
278
+ _setSelected(self, list);
279
+
280
+ list.delegate('li', 'click', { 'timepicker': self }, function(e) {
281
+ self.addClass('ui-timepicker-hideme');
282
+ self[0].focus();
283
+
284
+ // make sure only the clicked row is selected
285
+ list.find('li').removeClass('ui-timepicker-selected');
286
+ $(this).addClass('ui-timepicker-selected');
287
+
288
+ _selectValue(self);
289
+ list.hide();
290
+ });
291
+ };
292
+
293
+ function _findRow(self, list, value)
294
+ {
295
+ if (!value && value !== 0) {
296
+ return false;
297
+ }
298
+
299
+ var settings = self.data("settings");
300
+ var out = false;
301
+
302
+ // loop through the menu items
303
+ list.find('li').each(function(i, obj) {
304
+ var jObj = $(obj);
305
+
306
+ // check if the value is less than half a step from each row
307
+ if (Math.abs(jObj.data('time') - value) <= settings.step*30) {
308
+ out = jObj;
309
+ return false;
310
+ }
311
+ });
312
+
313
+ return out;
314
+ }
315
+
316
+ function _setSelected(self, list)
317
+ {
318
+ var timeValue = _time2int(self.val());
319
+
320
+ var selected = _findRow(self, list, timeValue);
321
+ if (selected) selected.addClass('ui-timepicker-selected');
322
+ }
323
+
324
+ function _keyhandler(e)
325
+ {
326
+ var self = $(this);
327
+ var list = self.siblings('.ui-timepicker-list');
328
+
329
+ if (!list.is(':visible')) {
330
+ if (e.keyCode == 40) {
331
+ self.focus();
332
+ } else {
333
+ return true;
334
+ }
335
+ };
336
+
337
+ switch (e.keyCode) {
338
+
339
+ case 13: // return
340
+ _selectValue(self);
341
+ methods.hide.apply(this);
342
+ e.preventDefault();
343
+ return false;
344
+ break;
345
+
346
+ case 38: // up
347
+ var selected = list.find('.ui-timepicker-selected');
348
+
349
+ if (!selected.length) {
350
+ var selected;
351
+ list.children().each(function(i, obj) {
352
+ if ($(obj).position().top > 0) {
353
+ selected = $(obj);
354
+ return false;
355
+ }
356
+ });
357
+ selected.addClass('ui-timepicker-selected');
358
+
359
+ } else if (!selected.is(':first-child')) {
360
+ selected.removeClass('ui-timepicker-selected');
361
+ selected.prev().addClass('ui-timepicker-selected');
362
+
363
+ if (selected.prev().position().top < selected.outerHeight()) {
364
+ list.scrollTop(list.scrollTop() - selected.outerHeight());
365
+ }
366
+ }
367
+
368
+ break;
369
+
370
+ case 40: // down
371
+ var selected = list.find('.ui-timepicker-selected');
372
+
373
+ if (selected.length == 0) {
374
+ var selected;
375
+ list.children().each(function(i, obj) {
376
+ if ($(obj).position().top > 0) {
377
+ selected = $(obj);
378
+ return false;
379
+ }
380
+ });
381
+
382
+ selected.addClass('ui-timepicker-selected');
383
+ } else if (!selected.is(':last-child')) {
384
+ selected.removeClass('ui-timepicker-selected');
385
+ selected.next().addClass('ui-timepicker-selected');
386
+
387
+ if (selected.next().position().top + 2*selected.outerHeight() > list.outerHeight()) {
388
+ list.scrollTop(list.scrollTop() + selected.outerHeight());
389
+ }
390
+ }
391
+
392
+ break;
393
+
394
+ case 27: // escape
395
+ list.find('li').removeClass('ui-timepicker-selected');
396
+ list.hide();
397
+ break;
398
+
399
+ case 9:
400
+ case 16:
401
+ case 17:
402
+ case 18:
403
+ case 19:
404
+ case 20:
405
+ case 33:
406
+ case 34:
407
+ case 35:
408
+ case 36:
409
+ case 37:
410
+ case 39:
411
+ case 45:
412
+ return;
413
+
414
+ default:
415
+ list.find('li').removeClass('ui-timepicker-selected');
416
+ return;
417
+ }
418
+ };
419
+
420
+ function _selectValue(self)
421
+ {
422
+ var settings = self.data('settings')
423
+ var list = self.siblings('.ui-timepicker-list');
424
+ var timeValue = null;
425
+
426
+ var cursor = list.find('.ui-timepicker-selected');
427
+
428
+ if (cursor.length) {
429
+ // selected value found
430
+ var timeValue = cursor.data('time');
431
+
432
+ } else if (self.val()) {
433
+
434
+ // no selected value; fall back on input value
435
+ var timeValue = _time2int(self.val());
436
+
437
+ _setSelected(self, list);
438
+ }
439
+
440
+ if (timeValue !== null) {
441
+ var timeString = _int2time(timeValue, settings.timeFormat);
442
+ self.attr('value', timeString);
443
+ }
444
+
445
+ self.trigger('change').trigger('changeTime');
446
+ };
447
+
448
+ function _int2duration(seconds)
449
+ {
450
+ var minutes = Math.round(seconds/60);
451
+ var duration;
452
+
453
+ if (minutes < 60) {
454
+ duration = [minutes, _lang.mins];
455
+ } else if (minutes == 60) {
456
+ duration = ['1', _lang.hr];
457
+ } else {
458
+ var hours = (minutes/60).toFixed(1);
459
+ if (_lang.decimal != '.') hours = hours.replace('.', _lang.decimal);
460
+ duration = [hours, _lang.hrs];
461
+ }
462
+
463
+ return duration.join(' ');
464
+ };
465
+
466
+ function _int2time(seconds, format)
467
+ {
468
+ var time = new Date(_baseDate.valueOf() + (seconds*1000));
469
+ var output = '';
470
+
471
+ for (var i=0; i<format.length; i++) {
472
+
473
+ var code = format.charAt(i);
474
+ switch (code) {
475
+
476
+ case 'a':
477
+ output += (time.getHours() > 11) ? 'pm' : 'am';
478
+ break;
479
+
480
+ case 'A':
481
+ output += (time.getHours() > 11) ? 'PM' : 'AM';
482
+ break;
483
+
484
+ case 'g':
485
+ var hour = time.getHours() % 12;
486
+ output += (hour == 0) ? '12' : hour;
487
+ break;
488
+
489
+ case 'G':
490
+ output += time.getHours();
491
+ break;
492
+
493
+ case 'h':
494
+ var hour = time.getHours() % 12;
495
+
496
+ if (hour != 0 && hour < 10) {
497
+ hour = '0'+hour;
498
+ }
499
+
500
+ output += (hour == 0) ? '12' : hour;
501
+ break;
502
+
503
+ case 'H':
504
+ var hour = time.getHours();
505
+ output += (hour > 9) ? hour : '0'+hour;
506
+ break;
507
+
508
+ case 'i':
509
+ var minutes = time.getMinutes();
510
+ output += (minutes > 9) ? minutes : '0'+minutes;
511
+ break;
512
+
513
+ case 's':
514
+ var seconds = time.getSeconds();
515
+ output += (seconds > 9) ? seconds : '0'+seconds;
516
+ break;
517
+
518
+ default:
519
+ output += code;
520
+ }
521
+ }
522
+
523
+ return output;
524
+ };
525
+
526
+ function _time2int(timeString)
527
+ {
528
+ if (timeString == '') return null;
529
+ if (timeString+0 == timeString) return timeString;
530
+
531
+ if (typeof(timeString) == 'object') {
532
+ timeString = timeString.getHours()+':'+timeString.getMinutes();
533
+ }
534
+
535
+ var d = new Date(0);
536
+ var time = timeString.toLowerCase().match(/(\d+)(?::(\d\d))?\s*([pa]?)/);
537
+
538
+ if (!time) {
539
+ return null;
540
+ }
541
+
542
+ var hour = parseInt(time[1]*1);
543
+
544
+ if (time[3]) {
545
+ if (hour == 12) {
546
+ var hours = (time[3] == 'p') ? 12 : 0;
547
+ } else {
548
+ var hours = (hour + (time[3] == 'p' ? 12 : 0));
549
+ }
550
+
551
+ } else {
552
+ var hours = hour;
553
+ }
554
+
555
+ var minutes = ( time[2]*1 || 0 );
556
+ return hours*3600 + minutes*60;
557
+ };
558
+
559
+ // Plugin entry
560
+ $.fn.timepicker = function(method)
561
+ {
562
+ if(methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); }
563
+ else if(typeof method === "object" || !method) { return methods.init.apply(this, arguments); }
564
+ else { $.error("Method "+ method + " does not exist on jQuery.timepicker"); }
565
+ };
566
+ })(jQuery);
@@ -0,0 +1,50 @@
1
+ .ui-timepicker-list {
2
+ overflow-y: auto;
3
+ height: 150px;
4
+ width: 6.5em;
5
+ background: #fff;
6
+ border: 1px solid #ddd;
7
+ margin: 0;
8
+ padding: 0;
9
+ list-style: none;
10
+ -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);
11
+ -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);
12
+ box-shadow:0 5px 10px rgba(0,0,0,0.2);
13
+ outline: none;
14
+ }
15
+
16
+ .ui-timepicker-list.ui-timepicker-with-duration {
17
+ width: 11em;
18
+ }
19
+
20
+ .ui-timepicker-duration {
21
+ margin-left: 5px; color: #888;
22
+ }
23
+
24
+ .ui-timepicker-list:hover .ui-timepicker-duration {
25
+ color: #888;
26
+ }
27
+
28
+ .ui-timepicker-list li {
29
+ padding: 3px 0 3px 5px;
30
+ cursor: pointer;
31
+ white-space: nowrap;
32
+ color: #000;
33
+ list-style: none;
34
+ margin: 0;
35
+ }
36
+
37
+ .ui-timepicker-list:hover .ui-timepicker-selected {
38
+ background: #fff; color: #000;
39
+ }
40
+
41
+ li.ui-timepicker-selected,
42
+ .ui-timepicker-list li:hover,
43
+ .ui-timepicker-list:hover .ui-timepicker-selected:hover {
44
+ background: #1980EC; color: #fff;
45
+ }
46
+
47
+ li.ui-timepicker-selected .ui-timepicker-duration,
48
+ .ui-timepicker-list li:hover .ui-timepicker-duration {
49
+ color: #ccc;
50
+ }
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jquery-timepicker-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tanguy Krotoff (jQuery plugin by Jon Thornton)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: &21232188 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *21232188
25
+ description: A jQuery timepicker plugin inspired by Google Calendar
26
+ email:
27
+ - tkrotoff@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - jquery-timepicker-rails.gemspec
38
+ - lib/jquery-timepicker-rails.rb
39
+ - lib/jquery-timepicker-rails/version.rb
40
+ - vendor/assets/javascripts/datepair.js
41
+ - vendor/assets/javascripts/jquery.timepicker.js
42
+ - vendor/assets/stylesheets/jquery.timepicker.css
43
+ homepage: http://github.com/tkrotoff/jquery-timepicker-rails
44
+ licenses: []
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 1.8.16
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: jquery-timepicker packaged for the Rails 3.1+ asset pipeline
67
+ test_files: []