local_time 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +72 -0
- data/app/assets/javascripts/local_time.js.coffee +195 -0
- data/app/helpers/local_time_helper.rb +29 -0
- data/lib/local_time.rb +4 -0
- data/test/helpers/local_time_helper_test.rb +68 -0
- data/test/javascripts/config.ru +20 -0
- data/test/javascripts/index.html +20 -0
- data/test/javascripts/index.js.coffee +21 -0
- data/test/javascripts/unit/local_time_test.js.coffee +36 -0
- data/test/javascripts/unit/page_events_test.js.coffee +30 -0
- data/test/javascripts/unit/strftime_test.js.coffee +40 -0
- data/test/javascripts/unit/time_ago_test.js.coffee +49 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0bfe22e485d89d3b8b8ef64db29d82d4d4f518c
|
4
|
+
data.tar.gz: 7d662d650e07542e16b13b98315ce589992bf397
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 840814649a2e4c03f7b8b9592ff696c7d5b7516a088a270fe2bdb4fb96e1a29221d455c31bbc31fdaa2a2dc0a38fe3718144c4c2dfe8ced9cbbc1d7ee76f425b
|
7
|
+
data.tar.gz: 2ed6f4bd924c469c23fd1134a53e4818ac1f5de1fd9cf9060859726bb47429176a8e5421f07c48d18303502c288ea38ccf74fff70ce55134f2e6b8fc110d23a4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Javan Makhmali
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
Local Time is a Rails engine with helpers and JavaScript for displaying times and dates to users in their local time. The helpers render a `<time>` element in UTC and the JavaScript swoops in to convert and format. Because the `<time>` element is only rendered in one timezone, it is ideal for caching.
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
####Example
|
6
|
+
|
7
|
+
Assuming the time zone is EST and `Time.now` is `2013-11-27 18:43:22 -0500`:
|
8
|
+
|
9
|
+
```erb
|
10
|
+
<%= local_time(Time.now) # index.html.erb %>
|
11
|
+
```
|
12
|
+
|
13
|
+
Renders in UTC time:
|
14
|
+
|
15
|
+
```html
|
16
|
+
<time data-format="%B %e, %Y %l:%M%P"
|
17
|
+
data-local="time"
|
18
|
+
datetime="2013-11-27T23:43:22Z">November 27, 2013 11:43pm</time>
|
19
|
+
```
|
20
|
+
|
21
|
+
Then immediately converts to local time and strftime formats with JavaScript:
|
22
|
+
|
23
|
+
```html
|
24
|
+
<time data-format="%B %e, %Y %l:%M%P"
|
25
|
+
data-local="time"
|
26
|
+
datetime="2013-11-27T23:43:22Z"
|
27
|
+
data-localized="true">November 27, 2013 6:43pm</time>
|
28
|
+
```
|
29
|
+
|
30
|
+
*(Line breaks added for readability)*
|
31
|
+
|
32
|
+
#### Time and date helpers
|
33
|
+
|
34
|
+
```erb
|
35
|
+
Pass a time and an optional strftime format (default format shown here)
|
36
|
+
<%= local_time(time, format: '%B %e, %Y %l:%M%P') %>
|
37
|
+
|
38
|
+
Alias for local_time with a month-formatted default
|
39
|
+
<%= local_date(time, format: '%B %e, %Y') %>
|
40
|
+
```
|
41
|
+
|
42
|
+
Note: The included strftime JavaScript implementation is not 100% complete. It supports the following directives: `%a %A %b %B %c %d %e %H %I %l %m %M %p %P %S %w %y %Y`
|
43
|
+
|
44
|
+
#### Time ago helper
|
45
|
+
|
46
|
+
```erb
|
47
|
+
<%= local_time_ago(time) %>
|
48
|
+
```
|
49
|
+
|
50
|
+
Displays the relative amount of time passed. With age, the descriptions transition from specific quantities to general dates. The `<time>` elements are updated every 60 seconds. Examples (in quotes):
|
51
|
+
|
52
|
+
* Recent: "a second ago", "32 seconds ago", "an hour ago", "14 hours ago"
|
53
|
+
* Yesterday: "yesterday at 5:22pm"
|
54
|
+
* This week: "Tuesday at 12:48am"
|
55
|
+
* This year: "on Nov 17"
|
56
|
+
* Last year: "on Jan 31, 2012"
|
57
|
+
|
58
|
+
#### Installation
|
59
|
+
|
60
|
+
1. Add `gem 'local_time'` to your Gemfile.
|
61
|
+
2. Run `bundle install`.
|
62
|
+
3. Add `//= require local_time` to your JavaScript manifest file (usually found at app/assets/javascripts/application.js).
|
63
|
+
|
64
|
+
#### JavaScript events and library compatibility
|
65
|
+
|
66
|
+
The included JavaScript does not depend on any frameworks or libraries, and listens for a `DOMContentLoaded` event to run initially. It also listens on `document` for `page:update` if you're using Turbolinks and `ajaxSuccess` if you're using jQuery. This should catch most cases where new `<time>` elements have been added to the DOM and process them automatically. If you're adding new elements in another context, trigger `time:elapse` to process them.
|
67
|
+
|
68
|
+
#### Version History
|
69
|
+
|
70
|
+
**0.1.0** (November 29, 2013)
|
71
|
+
|
72
|
+
* Initial release.
|
@@ -0,0 +1,195 @@
|
|
1
|
+
browserIsCompatible = ->
|
2
|
+
document.querySelectorAll and document.addEventListener
|
3
|
+
|
4
|
+
return unless browserIsCompatible()
|
5
|
+
|
6
|
+
# Older browsers do not support ISO8601 (JSON) timestamps in Date.parse
|
7
|
+
if isNaN Date.parse "2011-01-01T12:00:00-05:00"
|
8
|
+
parse = Date.parse
|
9
|
+
iso8601 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|[-+]?[\d:]+)$/
|
10
|
+
|
11
|
+
Date.parse = (dateString) ->
|
12
|
+
dateString = dateString.toString()
|
13
|
+
if matches = dateString.match iso8601
|
14
|
+
[_, year, month, day, hour, minute, second, zone] = matches
|
15
|
+
offset = zone.replace(":", "") if zone isnt "Z"
|
16
|
+
dateString = "#{year}/#{month}/#{day} #{hour}:#{minute}:#{second} GMT#{[offset]}"
|
17
|
+
parse dateString
|
18
|
+
|
19
|
+
weekdays = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split " "
|
20
|
+
months = "January February March April May June July August September October November December".split " "
|
21
|
+
|
22
|
+
pad = (num) -> ('0' + num).slice -2
|
23
|
+
|
24
|
+
strftime = (time, formatString) ->
|
25
|
+
day = time.getDay()
|
26
|
+
date = time.getDate()
|
27
|
+
month = time.getMonth()
|
28
|
+
year = time.getFullYear()
|
29
|
+
hour = time.getHours()
|
30
|
+
minute = time.getMinutes()
|
31
|
+
second = time.getSeconds()
|
32
|
+
|
33
|
+
formatString.replace /%([%aAbBcdeHIlmMpPSwyY])/g, ([match, modifier]) ->
|
34
|
+
switch modifier
|
35
|
+
when '%' then '%'
|
36
|
+
when 'a' then weekdays[day].slice 0, 3
|
37
|
+
when 'A' then weekdays[day]
|
38
|
+
when 'b' then months[month].slice 0, 3
|
39
|
+
when 'B' then months[month]
|
40
|
+
when 'c' then time.toString()
|
41
|
+
when 'd' then pad date
|
42
|
+
when 'e' then date
|
43
|
+
when 'H' then pad hour
|
44
|
+
when 'I' then pad strftime time, '%l'
|
45
|
+
when 'l' then (if hour is 0 or hour is 12 then 12 else (hour + 12) % 12)
|
46
|
+
when 'm' then pad month + 1
|
47
|
+
when 'M' then pad minute
|
48
|
+
when 'p' then (if hour > 11 then 'PM' else 'AM')
|
49
|
+
when 'P' then (if hour > 11 then 'pm' else 'am')
|
50
|
+
when 'S' then pad second
|
51
|
+
when 'w' then day
|
52
|
+
when 'y' then pad year % 100
|
53
|
+
when 'Y' then year
|
54
|
+
|
55
|
+
|
56
|
+
class CalendarDate
|
57
|
+
@fromDate: (date) ->
|
58
|
+
new this date.getFullYear(), date.getMonth() + 1, date.getDate()
|
59
|
+
|
60
|
+
@today: ->
|
61
|
+
@fromDate new Date
|
62
|
+
|
63
|
+
constructor: (year, month, day) ->
|
64
|
+
@date = new Date Date.UTC year, month - 1
|
65
|
+
@date.setUTCDate day
|
66
|
+
|
67
|
+
@year = @date.getUTCFullYear()
|
68
|
+
@month = @date.getUTCMonth() + 1
|
69
|
+
@day = @date.getUTCDate()
|
70
|
+
|
71
|
+
occursOnSameYearAs: (date) ->
|
72
|
+
@year is date?.year
|
73
|
+
|
74
|
+
occursThisYear: ->
|
75
|
+
@occursOnSameYearAs @constructor.today()
|
76
|
+
|
77
|
+
daysSince: (date) ->
|
78
|
+
if date
|
79
|
+
(@date - date.date) / (1000 * 60 * 60 * 24)
|
80
|
+
|
81
|
+
daysPassed: ->
|
82
|
+
@constructor.today().daysSince @
|
83
|
+
|
84
|
+
|
85
|
+
class RelativeTimeAgo
|
86
|
+
@generate: (date) ->
|
87
|
+
new this(date).toString()
|
88
|
+
|
89
|
+
constructor: (@date) ->
|
90
|
+
@calendarDate = CalendarDate.fromDate @date
|
91
|
+
|
92
|
+
toString: ->
|
93
|
+
# Today: "Saved 5 hours ago"
|
94
|
+
if ago = @timeElapsed()
|
95
|
+
"#{ago} ago"
|
96
|
+
|
97
|
+
# Yesterday: "Saved yesterday at 8:15am"
|
98
|
+
# This week: "Saved Thursday at 8:15am"
|
99
|
+
else if day = @relativeWeekday()
|
100
|
+
"#{day} at #{@formatTime()}"
|
101
|
+
|
102
|
+
# Older: "Saved on Dec 15"
|
103
|
+
else
|
104
|
+
"on #{@formatDate()}"
|
105
|
+
|
106
|
+
timeElapsed: ->
|
107
|
+
ms = new Date().getTime() - @date.getTime()
|
108
|
+
sec = Math.round ms / 1000
|
109
|
+
min = Math.round sec / 60
|
110
|
+
hr = Math.round min / 60
|
111
|
+
|
112
|
+
if ms < 0
|
113
|
+
null
|
114
|
+
else if sec < 10
|
115
|
+
"a second"
|
116
|
+
else if sec < 45
|
117
|
+
"#{sec} seconds"
|
118
|
+
else if sec < 90
|
119
|
+
"a minute"
|
120
|
+
else if min < 45
|
121
|
+
"#{min} minutes"
|
122
|
+
else if min < 90
|
123
|
+
"an hour"
|
124
|
+
else if hr < 24
|
125
|
+
"#{hr} hours"
|
126
|
+
else
|
127
|
+
null
|
128
|
+
|
129
|
+
relativeWeekday: ->
|
130
|
+
daysPassed = @calendarDate.daysPassed()
|
131
|
+
|
132
|
+
if daysPassed > 6
|
133
|
+
null
|
134
|
+
else if daysPassed is 0
|
135
|
+
"today"
|
136
|
+
else if daysPassed is 1
|
137
|
+
"yesterday"
|
138
|
+
else
|
139
|
+
strftime @date, "%A"
|
140
|
+
|
141
|
+
formatDate: ->
|
142
|
+
format = "%b %e"
|
143
|
+
format += ", %Y" unless @calendarDate.occursThisYear()
|
144
|
+
strftime @date, format
|
145
|
+
|
146
|
+
formatTime: ->
|
147
|
+
strftime @date, '%l:%M%P'
|
148
|
+
|
149
|
+
|
150
|
+
domLoaded = false
|
151
|
+
|
152
|
+
update = (callback) ->
|
153
|
+
callback() if domLoaded
|
154
|
+
|
155
|
+
document.addEventListener "time:elapse", callback
|
156
|
+
|
157
|
+
if Turbolinks?.supported
|
158
|
+
document.addEventListener "page:update", callback
|
159
|
+
else
|
160
|
+
setTimeout ->
|
161
|
+
window.addEventListener "popstate", callback
|
162
|
+
, 1
|
163
|
+
|
164
|
+
jQuery?(document).on "ajaxSuccess", (event, xhr) ->
|
165
|
+
callback() if jQuery.trim xhr.responseText
|
166
|
+
|
167
|
+
process = (selector, callback) ->
|
168
|
+
update ->
|
169
|
+
for element in document.querySelectorAll selector
|
170
|
+
callback element
|
171
|
+
|
172
|
+
document.addEventListener "DOMContentLoaded", ->
|
173
|
+
domLoaded = true
|
174
|
+
|
175
|
+
process "time[data-local]:not([data-localized])", (element) ->
|
176
|
+
datetime = element.getAttribute "datetime"
|
177
|
+
format = element.getAttribute "data-format"
|
178
|
+
local = element.getAttribute "data-local"
|
179
|
+
|
180
|
+
time = new Date Date.parse datetime
|
181
|
+
return if isNaN time
|
182
|
+
|
183
|
+
element.innerText =
|
184
|
+
switch local
|
185
|
+
when "time"
|
186
|
+
element.setAttribute "data-localized", true
|
187
|
+
strftime time, format
|
188
|
+
when "time-ago"
|
189
|
+
RelativeTimeAgo.generate time
|
190
|
+
|
191
|
+
setInterval ->
|
192
|
+
event = document.createEvent "Events"
|
193
|
+
event.initEvent "time:elapse", true, true
|
194
|
+
document.dispatchEvent event
|
195
|
+
, 60 * 1000
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module LocalTimeHelper
|
2
|
+
def local_time(time, options = {})
|
3
|
+
time = utc_time(time)
|
4
|
+
format = options.delete(:format).presence || '%B %e, %Y %l:%M%P'
|
5
|
+
|
6
|
+
options[:data] ||= {}
|
7
|
+
options[:data].merge! local: :time, format: format
|
8
|
+
|
9
|
+
time_tag time, time.strftime(format), options
|
10
|
+
end
|
11
|
+
|
12
|
+
def local_date(time, options = {})
|
13
|
+
options.reverse_merge! format: '%B %e, %Y'
|
14
|
+
local_time time, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def local_time_ago(time)
|
18
|
+
time = utc_time(time)
|
19
|
+
time_tag time, time.strftime('%B %e, %Y %l:%M%P'), data: { local: 'time-ago' }
|
20
|
+
end
|
21
|
+
|
22
|
+
def utc_time(time_or_date)
|
23
|
+
if time_or_date.respond_to?(:in_time_zone)
|
24
|
+
time_or_date.in_time_zone.utc
|
25
|
+
else
|
26
|
+
time_or_date.to_time.utc
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/local_time.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative '../../app/helpers/local_time_helper'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'action_view'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
|
7
|
+
class LocalTimeHelperTest < Minitest::Test
|
8
|
+
include ActionView::Helpers::DateHelper, ActionView::Helpers::TagHelper
|
9
|
+
include LocalTimeHelper
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@original_zone = Time.zone
|
13
|
+
Time.zone = ActiveSupport::TimeZone["Central Time (US & Canada)"]
|
14
|
+
|
15
|
+
@date = "2013-11-21"
|
16
|
+
@time = Time.zone.parse(@date)
|
17
|
+
@time_utc = "2013-11-21 06:00:00 UTC"
|
18
|
+
@time_js = "2013-11-21T06:00:00Z"
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
Time.zone = @original_zone
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_utc_time_with_a_date
|
26
|
+
date = Date.parse(@date)
|
27
|
+
assert_equal @time_utc, utc_time(date).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_utc_time_with_a_local_time
|
31
|
+
assert_equal @time_utc, utc_time(@time).to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_utc_time_with_a_utc_time
|
35
|
+
assert_equal @time_utc, utc_time(@time.utc).to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_local_time
|
39
|
+
expected = %Q(<time data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="#{@time_js}">November 21, 2013 6:00am</time>)
|
40
|
+
assert_equal expected, local_time(@time)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_local_time_with_format
|
44
|
+
expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
|
45
|
+
assert_equal expected, local_time(@time, format: '%b %e')
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_local_time_with_options
|
49
|
+
expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}" style="display:none">Nov 21</time>)
|
50
|
+
assert_equal expected, local_time(@time, format: '%b %e', style: 'display:none')
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_local_date
|
54
|
+
expected = %Q(<time data-format="%B %e, %Y" data-local="time" datetime="#{@time_js}">November 21, 2013</time>)
|
55
|
+
assert_equal expected, local_date(@time)
|
56
|
+
assert_equal expected, local_date(@time.to_date)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_local_date_with_format
|
60
|
+
expected = %Q(<time data-format="%b %e" data-local="time" datetime="#{@time_js}">Nov 21</time>)
|
61
|
+
assert_equal expected, local_date(@time, format: '%b %e')
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_local_time_ago
|
65
|
+
expected = %Q(<time data-local="time-ago" datetime="#{@time_js}">November 21, 2013 6:00am</time>)
|
66
|
+
assert_equal expected, local_time_ago(@time)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
require 'coffee-rails'
|
3
|
+
|
4
|
+
Root = File.expand_path("../../..", __FILE__)
|
5
|
+
|
6
|
+
Assets = Sprockets::Environment.new(Root) do |env|
|
7
|
+
env.append_path "app/assets/javascripts"
|
8
|
+
env.append_path "test/javascripts"
|
9
|
+
env.append_path "vendor"
|
10
|
+
end
|
11
|
+
|
12
|
+
map "/css" do
|
13
|
+
run Assets
|
14
|
+
end
|
15
|
+
|
16
|
+
map "/js" do
|
17
|
+
run Assets
|
18
|
+
end
|
19
|
+
|
20
|
+
map("/") { run Rack::File.new("#{Root}/test/javascripts/index.html") }
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<link rel="stylesheet" href="/css/qunit.css">
|
6
|
+
<script type="text/javascript" src="/js/index.js"></script>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<div id="qunit"></div>
|
10
|
+
<div id="qunit-fixture"></div>
|
11
|
+
|
12
|
+
<time id="one" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="2013-11-12T12:13:00Z"></time>
|
13
|
+
<time id="two" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="2013-01-02T02:04:00Z"></time>
|
14
|
+
<time id="past" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="1805-05-15T01:01:00Z"></time>
|
15
|
+
<time id="future" data-format="%B %e, %Y %l:%M%P" data-local="time" datetime="3333-02-14T23:55:00Z"></time>
|
16
|
+
|
17
|
+
<time id="date" data-format="%B %e, %Y" data-local="time" datetime="2013-11-12T12:13:00Z"></time>
|
18
|
+
|
19
|
+
<time id="ago"></time>
|
20
|
+
</html>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#= require qunit
|
2
|
+
#= require moment
|
3
|
+
#= require local_time
|
4
|
+
#= require_directory ./unit
|
5
|
+
#= require_self
|
6
|
+
|
7
|
+
@addTimeEl = (format = "%Y", datetime = "2013-11-12T12:13:00Z") ->
|
8
|
+
el = document.createElement "time"
|
9
|
+
el.setAttribute "data-local", "time"
|
10
|
+
el.setAttribute "data-format", format
|
11
|
+
el.setAttribute "datetime", datetime
|
12
|
+
document.body.appendChild el
|
13
|
+
el
|
14
|
+
|
15
|
+
@triggerEvent = (name, el = document) ->
|
16
|
+
event = document.createEvent "Events"
|
17
|
+
event.initEvent name, true, true
|
18
|
+
el.dispatchEvent event
|
19
|
+
|
20
|
+
@run = ->
|
21
|
+
triggerEvent "time:elapse"
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module "localized"
|
2
|
+
|
3
|
+
for id in ["one", "two", "past", "future"]
|
4
|
+
test id, ->
|
5
|
+
assertLocalized id
|
6
|
+
|
7
|
+
test "date", ->
|
8
|
+
assertLocalized "date", "date"
|
9
|
+
|
10
|
+
test "unparseable time", ->
|
11
|
+
el = addTimeEl "%Y", ":("
|
12
|
+
el.innerText = "2013"
|
13
|
+
run()
|
14
|
+
equal el.innerText, "2013"
|
15
|
+
|
16
|
+
|
17
|
+
assertLocalized = (id, type = "time") ->
|
18
|
+
switch type
|
19
|
+
when "time"
|
20
|
+
momentFormat = "MMMM D, YYYY h:mma"
|
21
|
+
compare = "toString"
|
22
|
+
when "date"
|
23
|
+
momentFormat = "MMMM D, YYYY"
|
24
|
+
compare = "dayOfYear"
|
25
|
+
|
26
|
+
el = document.getElementById id
|
27
|
+
|
28
|
+
ok datetime = el.getAttribute "datetime"
|
29
|
+
ok local = el.innerText
|
30
|
+
|
31
|
+
datetimeParsed = moment datetime
|
32
|
+
localParsed = moment local, momentFormat
|
33
|
+
|
34
|
+
ok datetimeParsed.isValid()
|
35
|
+
ok localParsed.isValid()
|
36
|
+
equal datetimeParsed[compare](), localParsed[compare]()
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module "page events"
|
2
|
+
|
3
|
+
test "document DOMContentLoaded", ->
|
4
|
+
el = addTimeEl()
|
5
|
+
triggerEvent "DOMContentLoaded"
|
6
|
+
ok el.innerText
|
7
|
+
|
8
|
+
test "document time:elapse", ->
|
9
|
+
el = addTimeEl()
|
10
|
+
triggerEvent "time:elapse"
|
11
|
+
ok el.innerText
|
12
|
+
|
13
|
+
test "window popstate", ->
|
14
|
+
el = addTimeEl()
|
15
|
+
triggerEvent "popstate", window
|
16
|
+
ok el.innerText
|
17
|
+
|
18
|
+
test "document page:update with Turbolinks on", ->
|
19
|
+
el = addTimeEl()
|
20
|
+
triggerEvent "page:update"
|
21
|
+
ok not el.innerText
|
22
|
+
|
23
|
+
original = window.Turbolinks
|
24
|
+
window.Turbolinks = { supported: true }
|
25
|
+
|
26
|
+
triggerEvent "DOMContentLoaded"
|
27
|
+
triggerEvent "page:update"
|
28
|
+
ok el.innerText
|
29
|
+
|
30
|
+
window.Turbolinks = original
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module "strftime"
|
2
|
+
|
3
|
+
momentMap =
|
4
|
+
"%a": "ddd"
|
5
|
+
"%A": "dddd"
|
6
|
+
"%b": "MMM"
|
7
|
+
"%B": "MMMM"
|
8
|
+
"%c": "toString()"
|
9
|
+
"%d": "DD"
|
10
|
+
"%e": "D"
|
11
|
+
"%H": "HH"
|
12
|
+
"%I": "hh"
|
13
|
+
"%l": "h"
|
14
|
+
"%m": "MM"
|
15
|
+
"%M": "mm"
|
16
|
+
"%p": "A"
|
17
|
+
"%P": "a"
|
18
|
+
"%S": "ss"
|
19
|
+
"%w": "e"
|
20
|
+
"%y": "YY"
|
21
|
+
"%Y": "YYYY"
|
22
|
+
|
23
|
+
for day in [0..30] by 6
|
24
|
+
do (day) ->
|
25
|
+
for hour in [0..24] by 6
|
26
|
+
do (hour) ->
|
27
|
+
for format, momentFormat of momentMap
|
28
|
+
do (format, momentFormat) ->
|
29
|
+
|
30
|
+
test "#{format} (+#{day} days, #{hour} hours)", ->
|
31
|
+
now = moment().add("days", day).add("hours", hour)
|
32
|
+
el = addTimeEl format, now.toISOString()
|
33
|
+
run()
|
34
|
+
|
35
|
+
equal el.innerText,
|
36
|
+
if func = momentFormat.match(/(\w+)\(\)/)?[1]
|
37
|
+
now.toDate()[func]()
|
38
|
+
else
|
39
|
+
now.format momentFormat
|
40
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module "time ago"
|
2
|
+
|
3
|
+
test "a second ago", ->
|
4
|
+
assertTimeAgo "a second ago", "seconds", 9
|
5
|
+
|
6
|
+
test "seconds ago", ->
|
7
|
+
assertTimeAgo "44 seconds ago", "seconds", 44
|
8
|
+
|
9
|
+
test "a minute ago", ->
|
10
|
+
assertTimeAgo "a minute ago", "seconds", 89
|
11
|
+
|
12
|
+
test "minutes ago", ->
|
13
|
+
assertTimeAgo "44 minutes ago", "minutes", 44
|
14
|
+
|
15
|
+
test "an hour ago", ->
|
16
|
+
assertTimeAgo "an hour ago", "minutes", 89
|
17
|
+
|
18
|
+
test "hours ago", ->
|
19
|
+
assertTimeAgo "23 hours ago", "hours", 23
|
20
|
+
|
21
|
+
test "yesterday", ->
|
22
|
+
time = moment().subtract("days", 1).format "h:mma"
|
23
|
+
assertTimeAgo "yesterday at #{time}", "days", 1
|
24
|
+
|
25
|
+
test "last week", ->
|
26
|
+
ago = moment().subtract "days", 5
|
27
|
+
day = ago.format "dddd"
|
28
|
+
time = ago.format "h:mma"
|
29
|
+
|
30
|
+
assertTimeAgo "#{day} at #{time}", "days", 5
|
31
|
+
|
32
|
+
test "this year", ->
|
33
|
+
# This will fail on the first 7 days of the year.
|
34
|
+
# Not sure how to test without somehow stubbing Date.
|
35
|
+
if moment().dayOfYear() > 7
|
36
|
+
date = moment().subtract("days", 7).format "MMM D"
|
37
|
+
assertTimeAgo "on #{date}", "days", 7
|
38
|
+
|
39
|
+
test "last year", ->
|
40
|
+
date = moment().subtract("days", 366).format "MMM D, YYYY"
|
41
|
+
assertTimeAgo "on #{date}", "days", 366
|
42
|
+
|
43
|
+
|
44
|
+
assertTimeAgo = (string, unit, amount) ->
|
45
|
+
el = document.getElementById "ago"
|
46
|
+
el.setAttribute "data-local", "time-ago"
|
47
|
+
el.setAttribute "datetime", moment().subtract(unit, amount).utc().toISOString()
|
48
|
+
run()
|
49
|
+
equal el.innerText, string
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: local_time
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Javan Makhmali
|
8
|
+
- Sam Stephenson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: coffee-rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
description:
|
29
|
+
email: javan@37signals.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- app/assets/javascripts/local_time.js.coffee
|
35
|
+
- app/helpers/local_time_helper.rb
|
36
|
+
- lib/local_time.rb
|
37
|
+
- MIT-LICENSE
|
38
|
+
- README.md
|
39
|
+
- test/helpers/local_time_helper_test.rb
|
40
|
+
- test/javascripts/config.ru
|
41
|
+
- test/javascripts/index.html
|
42
|
+
- test/javascripts/index.js.coffee
|
43
|
+
- test/javascripts/unit/local_time_test.js.coffee
|
44
|
+
- test/javascripts/unit/page_events_test.js.coffee
|
45
|
+
- test/javascripts/unit/strftime_test.js.coffee
|
46
|
+
- test/javascripts/unit/time_ago_test.js.coffee
|
47
|
+
homepage:
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.1.11
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Rails engine for displaying local times using JavaScript
|
71
|
+
test_files:
|
72
|
+
- test/helpers/local_time_helper_test.rb
|
73
|
+
- test/javascripts/config.ru
|
74
|
+
- test/javascripts/index.html
|
75
|
+
- test/javascripts/index.js.coffee
|
76
|
+
- test/javascripts/unit/local_time_test.js.coffee
|
77
|
+
- test/javascripts/unit/page_events_test.js.coffee
|
78
|
+
- test/javascripts/unit/strftime_test.js.coffee
|
79
|
+
- test/javascripts/unit/time_ago_test.js.coffee
|