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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +42 -0
- data/Rakefile +2 -0
- data/jquery-timepicker-rails.gemspec +19 -0
- data/lib/jquery-timepicker-rails.rb +10 -0
- data/lib/jquery-timepicker-rails/version.rb +7 -0
- data/vendor/assets/javascripts/datepair.js +197 -0
- data/vendor/assets/javascripts/jquery.timepicker.js +566 -0
- data/vendor/assets/stylesheets/jquery.timepicker.css +50 -0
- metadata +67 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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,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: []
|