clockpunch 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .sass-cache/
2
+ clockpunch-*.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clockpunch.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,27 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ clockpunch (0.0.1)
5
+ coffee-script
6
+ sass
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ coffee-script (2.2.0)
12
+ coffee-script-source
13
+ execjs
14
+ coffee-script-source (1.6.2)
15
+ execjs (1.4.0)
16
+ multi_json (~> 1.0)
17
+ multi_json (1.7.7)
18
+ rake (10.1.0)
19
+ sass (3.2.9)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ bundler (~> 1.3)
26
+ clockpunch!
27
+ rake
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jason Hanggi
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,109 @@
1
+ # Time Parsing
2
+
3
+ This library comprises the following pieces:
4
+
5
+ 1. Functions for parsing hour:minute strings and outputing minutes, and vice versa
6
+ 2. A jQuery plugin that binds to an input's change event to automatically update it to the H:MM format and store the integer minute value in a hidden field.
7
+ 3. A gem that you can use with Rails to include the library through the asset pipeline
8
+
9
+ ## Parsing
10
+
11
+ var parser = new TimeParser()
12
+ parser.to_minutes("1:27") // returns 87
13
+
14
+ ### Examples and rules
15
+ <table>
16
+ <tr>
17
+ <th>Input</th>
18
+ <th></th>
19
+ <th>Output in minutes</th>
20
+ <tr>
21
+ <td>1:00</td>
22
+ <td>1 hour</td>
23
+ <td>60
24
+ </tr>
25
+ <tr>
26
+ <td>0:45</td>
27
+ <td>Forty-five minutes</td>
28
+ <td>45</td>
29
+ </tr>
30
+ <tr>
31
+ <td>:45</td>
32
+ <td>Forty-five minutes</td>
33
+ <td>45</td>
34
+ </tr>
35
+ <tr>
36
+ <td>45</td>
37
+ <td>Forty-five minutes</td>
38
+ <td>45</td>
39
+ </tr>
40
+ <tr>
41
+ <td>1.5</td>
42
+ <td>One and a half hours</td>
43
+ <td>90</td>
44
+ </tr>
45
+ <tr>
46
+ <td>.5</td>
47
+ <td>Half an hour</td>
48
+ <td>30</td>
49
+ </tr>
50
+ <tr>
51
+ <td>1h30m</td>
52
+ <td>1 hour, 30 minutes</td>
53
+ <td>90</td>
54
+ </tr>
55
+ <tr>
56
+ <td>1h</td>
57
+ <td>1 hour</td>
58
+ <td>60</td>
59
+ </tr>
60
+ <tr>
61
+ <td>20m</td>
62
+ <td>20 minutes</td>
63
+ <td>20</td>
64
+ </tr>
65
+ </table>
66
+
67
+ ### Bad input
68
+
69
+ - Negative time is always converted to zero. E.g. "-45" becomes 0 minutes
70
+ - All characters besides numbers, decimals, and colons are stripped before any parsing. This means something like "#4ba.2" becomes "4.2", which is 252 minutes.
71
+ - If there are multiple colons, everything after the second one is dropped. E.g. "2:30:15" is treated like "2:30"
72
+ - If you have both colons and decimals, the string is split first on the colon. Hours are read as a decimal, minutes are rounded down.
73
+ - "1.2:30" is read as 1.2 hours, 30 minutes, coming out as 72.
74
+ - "1:30.2" is read like "1:30"
75
+ - See spec/time_parser_spec.coffee for more examples of how input is handled.
76
+
77
+ # Use in Forms
78
+
79
+ Apply the jQuery plugin to the elements:
80
+
81
+ $('.timeinput').timeinput();
82
+
83
+ # Rails
84
+
85
+ ## Installation
86
+
87
+ Add this line to your application's Gemfile:
88
+
89
+ gem "clockpunch"
90
+
91
+ ## Include the assets
92
+ Add to your `application.js`
93
+
94
+ //= require clockpunch
95
+
96
+ Add to your `application.scss`
97
+
98
+ //= require clockpunch
99
+
100
+ # Standalone
101
+
102
+ Copy `app/assets/javascripts/clockpunch.js` and `app/assets/stylesheets/clockpunch.css` into your
103
+ application and include them using `<script src="clockpunch.js"></script>` and `<style>@import url(clockpunch.css)</style>`
104
+
105
+ # Building
106
+
107
+ From the `/source` directory, run `./build.sh`. This will generate the files in `app/assets`.
108
+
109
+ TODO: node, npm installation of coffeescript, sass installation
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,205 @@
1
+ // Generated by CoffeeScript 1.4.0
2
+
3
+ /*
4
+ Clockpunch 1.1
5
+ https://github.com/tablexi/clockpunch
6
+ */
7
+
8
+
9
+ (function() {
10
+ var $, TimeParsingInput;
11
+
12
+ TimeParsingInput = (function() {
13
+
14
+ function TimeParsingInput(elem, format) {
15
+ var self;
16
+ if (format == null) {
17
+ format = null;
18
+ }
19
+ self = this;
20
+ this.$elem = $(elem);
21
+ this.$elem.data('timeparser', this);
22
+ this.parser = new TimeParser();
23
+ this.create_hidden_field(format);
24
+ this.$elem.change(function() {
25
+ var $this, minutes;
26
+ $this = $(this);
27
+ minutes = self.parser.to_minutes($(this).val());
28
+ $this.data('timeparser').$hidden_field.val(minutes).trigger('change');
29
+ return $this.val(self.parser.from_minutes(minutes));
30
+ });
31
+ this.$elem.trigger('change');
32
+ this.create_tooltip();
33
+ }
34
+
35
+ TimeParsingInput.prototype.create_hidden_field = function() {
36
+ var field_name;
37
+ field_name = this.$elem.attr('name');
38
+ this.$hidden_field = $("<input type=\"hidden\" />").attr('name', field_name);
39
+ this.$elem.after(this.$hidden_field);
40
+ return this.$elem.attr('name', "" + field_name + "_display");
41
+ };
42
+
43
+ TimeParsingInput.prototype.create_tooltip = function() {
44
+ var $wrapper;
45
+ $wrapper = $('<div/>').css('position', 'relative').css('display', 'inline-block');
46
+ this.$elem.wrap($wrapper);
47
+ this.$tooltip = $('<span/>').addClass('clockpunch-tooltip').hide();
48
+ this.$tooltip.text(this.$elem.val());
49
+ this.$elem.after(this.$tooltip);
50
+ this.$elem.data('tooltip', this.$tooltip);
51
+ this.$elem.bind('keyup', function() {
52
+ var val;
53
+ val = $(this).val() || 0;
54
+ return $(this).data('tooltip').text(TimeParser.clean(val));
55
+ });
56
+ this.$elem.bind('focus', function() {
57
+ return $(this).data('tooltip').fadeIn('fast');
58
+ });
59
+ return this.$elem.bind('blur', function() {
60
+ return $(this).data('tooltip').fadeOut('fast');
61
+ });
62
+ };
63
+
64
+ return TimeParsingInput;
65
+
66
+ })();
67
+
68
+ if (typeof jQuery !== "undefined" && jQuery !== null) {
69
+ $ = jQuery;
70
+ $.fn.extend({
71
+ timeinput: function(options) {
72
+ return this.each(function(input_field) {
73
+ return new TimeParsingInput(this);
74
+ });
75
+ }
76
+ });
77
+ }
78
+
79
+ window.TimeParser = (function() {
80
+
81
+ function TimeParser(time_format) {
82
+ this.time_format = time_format != null ? time_format : "{HOURS}:{MINUTES}";
83
+ }
84
+
85
+ /*
86
+ # Class Methods
87
+ */
88
+
89
+
90
+ TimeParser.to_minutes = function(string) {
91
+ var parser;
92
+ parser = new TimeParser();
93
+ return parser.to_minutes(string);
94
+ };
95
+
96
+ TimeParser.from_minutes = function(minutes) {
97
+ var parser;
98
+ parser = new TimeParser();
99
+ return parser.from_minutes(minutes);
100
+ };
101
+
102
+ TimeParser.clean = function(string) {
103
+ var minutes;
104
+ minutes = TimeParser.to_minutes(string);
105
+ return TimeParser.from_minutes(minutes);
106
+ };
107
+
108
+ TimeParser.pad = function(str, max) {
109
+ if (max == null) {
110
+ max = 2;
111
+ }
112
+ if (str.length < max) {
113
+ return this.pad("0" + str, max);
114
+ } else {
115
+ return str;
116
+ }
117
+ };
118
+
119
+ /*
120
+ # Instance methods
121
+ */
122
+
123
+
124
+ TimeParser.prototype.to_minutes = function(string) {
125
+ var result;
126
+ result = this.read_format(/(\d+)h(?:(\d{1,2})m)?/, string, function(str) {
127
+ return str.replace(/\s/g, '');
128
+ });
129
+ if (!result) {
130
+ result = this.read_default(string);
131
+ }
132
+ result || (result = {
133
+ hours: 0,
134
+ minutes: 0
135
+ });
136
+ return Math.ceil(result['hours'] * 60 + result['minutes']);
137
+ };
138
+
139
+ TimeParser.prototype.from_minutes = function(minutes) {
140
+ var hours, mins;
141
+ minutes = Math.floor(minutes);
142
+ if (minutes < 0) {
143
+ minutes = 0;
144
+ }
145
+ hours = Math.floor(minutes / 60.0);
146
+ mins = minutes % 60;
147
+ return this.format(hours, mins);
148
+ };
149
+
150
+ TimeParser.prototype.format = function(hours, minutes) {
151
+ return this.time_format.replace('{HOURS}', hours).replace('{MINUTES}', TimeParser.pad(minutes.toString()));
152
+ };
153
+
154
+ /*
155
+ # PRIVATE
156
+ */
157
+
158
+
159
+ TimeParser.prototype.read_format = function(regex, string, clean_function) {
160
+ var cleaned_string, matches;
161
+ if (clean_function == null) {
162
+ clean_function = null;
163
+ }
164
+ cleaned_string = string.toString();
165
+ if (clean_function != null) {
166
+ cleaned_string = clean_function(cleaned_string);
167
+ }
168
+ matches = cleaned_string.match(regex);
169
+ if (!matches) {
170
+ return;
171
+ }
172
+ return {
173
+ hours: parseInt(matches[1]),
174
+ minutes: parseInt(matches[2]) || 0
175
+ };
176
+ };
177
+
178
+ TimeParser.prototype.read_default = function(string) {
179
+ var hours, minutes, parts;
180
+ string = string.toString().replace(/[^\d\:\.]/g, '');
181
+ if (string === '') {
182
+ return 0;
183
+ }
184
+ if (string.indexOf(":") >= 0) {
185
+ parts = string.split(':');
186
+ hours = parseFloat(parts[0] || 0);
187
+ minutes = Math.floor(parseFloat(parts[1] || 0));
188
+ } else if (string.indexOf(".") >= 0) {
189
+ hours = parseFloat(string);
190
+ minutes = 0;
191
+ } else {
192
+ hours = 0;
193
+ minutes = parseFloat(string);
194
+ }
195
+ return {
196
+ hours: hours,
197
+ minutes: minutes
198
+ };
199
+ };
200
+
201
+ return TimeParser;
202
+
203
+ })();
204
+
205
+ }).call(this);
@@ -0,0 +1,30 @@
1
+ .clockpunch-tooltip {
2
+ position: absolute;
3
+ background: #eeeeee;
4
+ border: 1px solid #cccccc;
5
+ padding: 10px 0.5em;
6
+ border-radius: 0.5em;
7
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
8
+ color: #C00;
9
+ font-weight: bold;
10
+ top: -0.5em;
11
+ left: 98%; }
12
+ .clockpunch-tooltip:before {
13
+ position: absolute;
14
+ display: inline-block;
15
+ content: '';
16
+ left: -7px;
17
+ top: 12px;
18
+ border-top: 7px solid transparent;
19
+ border-right: 7px solid #eeeeee;
20
+ border-bottom: 7px solid transparent;
21
+ border-right-color: rgba(0, 0, 0, 0.2); }
22
+ .clockpunch-tooltip:after {
23
+ position: absolute;
24
+ display: inline-block;
25
+ content: '';
26
+ left: -6px;
27
+ top: 13px;
28
+ border-top: 6px solid transparent;
29
+ border-right: 6px solid #eeeeee;
30
+ border-bottom: 6px solid transparent; }
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clockpunch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clockpunch"
8
+ spec.version = Clockpunch::VERSION
9
+ spec.authors = ["Jason Hanggi"]
10
+ spec.email = ["jason@tablexi.com"]
11
+ spec.description = %q{Calculate durations from text input}
12
+ spec.summary = %q{Calculate durations from text input}
13
+ spec.homepage = "https://github.com/tablexi/clockpunch"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files | grep -v source/`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sass"
22
+ spec.add_dependency "coffee-script"
23
+ spec.add_dependency "railties", "~> 3.1"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ end
@@ -0,0 +1,3 @@
1
+ module Clockpunch
2
+ VERSION = "0.1.0"
3
+ end
data/lib/clockpunch.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "clockpunch/version"
2
+
3
+ module Clockpunch
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clockpunch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jason Hanggi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sass
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: coffee-script
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: railties
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Calculate durations from text input
95
+ email:
96
+ - jason@tablexi.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - Gemfile.lock
104
+ - LICENSE.txt
105
+ - README.md
106
+ - Rakefile
107
+ - app/assets/javascripts/clockpunch.js
108
+ - app/assets/stylesheets/clockpunch.css
109
+ - clockpunch.gemspec
110
+ - lib/clockpunch.rb
111
+ - lib/clockpunch/version.rb
112
+ homepage: https://github.com/tablexi/clockpunch
113
+ licenses:
114
+ - MIT
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 1.8.25
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: Calculate durations from text input
137
+ test_files: []