jquery-timepicker-rails 0.0.1

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