local_time 1.0.3 → 2.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e386d2a684296b0e7489cc13bee76a7df4495474
4
- data.tar.gz: 1167fdacc381765bc7ef5cf31499e9c46c23083c
3
+ metadata.gz: 9d4048768cb9f06471188cbbfab6127a1eaf7035
4
+ data.tar.gz: 2249470471b3efb5b61006372a6cea2ec31bd8ea
5
5
  SHA512:
6
- metadata.gz: f66918a56f5cb8c3be57c99537a1d054f7a10a6bedf2a309c320da407025e1f2b9b1f3410c60e5f532e3ffacc378bf65183b705253bdbd8d08e7023c741483c2
7
- data.tar.gz: 954b8526c0846718f9bfe0e81eb1660a02399cf76cf9e5cb056bdc492e38abd0c13feb2c22ba09d93c6969457354f25c1cbf1752e34c3b3888f2b6362afd2221
6
+ metadata.gz: b3399affc900a62906e2d4087ab90a7f988af993a1ae174a133c816f5cef5d91bfb50c69938398a46aae0f393206c09325ff8f366b259f84109fd876bab88457
7
+ data.tar.gz: a6d267fb48c9be393af4ba60b30ec6090d6d3a4838b5a362fea672034182103697f83badf5c91d274ca98ac79fc66935e71d67a82512e233f20c7283b9d393f0
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2015 Javan Makhmali, Basecamp
1
+ Copyright 2017 Javan Makhmali, Basecamp
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,8 +1,23 @@
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 and the JavaScript swoops in to convert and format. The helper output is ideal for caching since it's always in UTC time.
1
+ # Local Time
2
2
 
3
- ---
3
+ Local Time makes it easy to display times and dates to users in their local time. Its Rails helpers render `<time>` elements in UTC (making them cache friendly), and its JavaScript component immediately converts those elements from UTC to the browser's local time.
4
+
5
+ ## Installation
6
+
7
+ 1. Add `gem 'local_time'` to your Gemfile.
8
+ 2. Include `local-time.js` in your application's JavaScript bundle.
9
+
10
+ Using the asset pipeline:
11
+ ```js
12
+ //= require local-time
13
+ ```
14
+ Using the npm package:
15
+ ```js
16
+ import LocalTime from "local-time"
17
+ LocalTime.start()
18
+ ```
4
19
 
5
- ####Example
20
+ ## Example
6
21
 
7
22
  ```ruby
8
23
  > comment.created_at
@@ -21,7 +36,7 @@ Renders:
21
36
  datetime="2013-11-27T23:43:22Z">November 27, 2013 11:43pm</time>
22
37
  ```
23
38
 
24
- When the DOM loads, the content is immediately replaced with a local, formatted time:
39
+ And is converted client-side to:
25
40
 
26
41
  ```html
27
42
  <time data-format="%B %e, %Y %l:%M%P"
@@ -33,7 +48,7 @@ When the DOM loads, the content is immediately replaced with a local, formatted
33
48
 
34
49
  *(Line breaks added for readability)*
35
50
 
36
- #### Time and date helpers
51
+ ## Time and date helpers
37
52
 
38
53
  ```erb
39
54
  <%= local_time(time) %>
@@ -67,7 +82,7 @@ To use a strftime format already defined in your app, pass a symbol as the forma
67
82
 
68
83
  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 %Z`
69
84
 
70
- #### Time ago helper
85
+ ## Time ago helpers
71
86
 
72
87
  ```erb
73
88
  <%= local_time_ago(time) %>
@@ -83,7 +98,7 @@ Examples (in quotes):
83
98
  * This year: "on Nov 17"
84
99
  * Last year: "on Jan 31, 2012"
85
100
 
86
- #### Relative time helper
101
+ ## Relative time helpers
87
102
 
88
103
  Preset time and date formats that vary with age. The available types are `date`, `time-ago`, `time-or-date`, and `weekday`. Like the `local_time` helper, `:type` can be passed a string or in an options hash.
89
104
 
@@ -100,32 +115,40 @@ Preset time and date formats that vary with age. The available types are `date`,
100
115
  * `weekday` Displays "Today", "Yesterday", or the weekday (e.g. Wednesday) if the time is within a week of today.
101
116
  * `weekday-or-date` Displays the weekday if it occurs within a week or the date if not. "Yesterday" or "Apr 11"
102
117
 
103
- #### Installation
104
118
 
105
- 1. Add `gem 'local_time'` to your Gemfile.
106
- 2. Run `bundle install`.
107
- 3. Add `//= require local_time` to your JavaScript manifest file (usually found at app/assets/javascripts/application.js).
108
-
109
- #### JavaScript events and library compatibility
110
-
111
- 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.
119
+ ## Configuration
112
120
 
113
- #### JavaScript API
121
+ **Internationalization (I18n)**
114
122
 
115
- `relativeDate`, `relativeTimeAgo`, `relativeTimeOrDate`, `relativeWeekday`, `run`, and `strftime` methods are available on the global `LocalTime` object.
123
+ Local Time includes a [set of default `en` translations](lib/assets/javascripts/src/local-time/config/i18n.coffee) which can be updated directly. Or, you can provide an entirely new set in a different locale:
116
124
 
117
125
  ```js
118
- > LocalTime.relativeTimeAgo(new Date(new Date - 60 * 1000 * 5))
119
- "5 minutes ago"
126
+ LocalTime.config.i18n["es"] = {
127
+ date: {
128
+ dayNames: [ … ],
129
+ monthNames: [ … ],
130
+
131
+ },
132
+ time: {
133
+
134
+ },
135
+ datetime: {
136
+
137
+ }
138
+ }
139
+
140
+ LocalTime.config.locale = "es"
141
+ ```
120
142
 
121
- // Process <time> tags. Equivalent to dispatching a "time:elapse" Event.
122
- > LocalTime.run()
143
+ ## Version History
123
144
 
124
- > LocalTime.strftime(new Date, "%B %e, %Y %l:%M%P")
125
- "February 9, 2014 12:55pm"
126
- ```
145
+ **2.0.0** (August 7, 2017)
127
146
 
128
- #### Version History
147
+ * Add internationalization (I18n) API
148
+ * Switch to `MutationObserver` instead of listening for various DOM, Turbolinks, and jQuery events
149
+ * Publish JavaScript module on npm
150
+ * Drop coffee-rails gem dependency
151
+ * Renamed `local_time.js` to `local-time.js`
129
152
 
130
153
  **1.0.3**
131
154
 
@@ -0,0 +1 @@
1
+ (function(){var t=this;(function(){(function(){var t=[].slice;this.LocalTime={config:{},run:function(){return this.getController().processElements()},process:function(){var e,n,r,a;for(n=1<=arguments.length?t.call(arguments,0):[],r=0,a=n.length;r<a;r++)e=n[r],this.getController().processElement(e);return n.length},getController:function(){return null!=this.controller?this.controller:this.controller=new e.Controller}}}).call(this)}).call(t);var e=t.LocalTime;(function(){(function(){e.config.i18n={en:{date:{dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbrDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbrMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],yesterday:"yesterday",today:"today",tomorrow:"tomorrow",on:"on {date}",formats:{"default":"%b %e, %Y",thisYear:"%b %e"}},time:{am:"am",pm:"pm",singular:"a {time}",singularAn:"an {time}",elapsed:"{time} ago",second:"second",seconds:"seconds",minute:"minute",minutes:"minutes",hour:"hour",hours:"hours",formats:{"default":"%l:%M%P"}},datetime:{at:"{date} at {time}",formats:{"default":"%B %e, %Y at %l:%M%P %Z"}}}}}).call(this),function(){e.config.locale="en",e.config.defaultLocale="en"}.call(this),function(){e.config.timerInterval=6e4}.call(this),function(){var t,n,r;r=!isNaN(Date.parse("2011-01-01T12:00:00-05:00")),e.parseDate=function(t){return t=t.toString(),r||(t=n(t)),new Date(Date.parse(t))},t=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|[-+]?[\d:]+)$/,n=function(e){var n,r,a,i,o,s,u,c,l;if(a=e.match(t))return a[0],c=a[1],o=a[2],n=a[3],r=a[4],i=a[5],u=a[6],l=a[7],"Z"!==l&&(s=l.replace(":","")),c+"/"+o+"/"+n+" "+r+":"+i+":"+u+" GMT"+[s]}}.call(this),function(){e.elementMatchesSelector=function(){var t,e,n,r,a,i;return t=document.documentElement,e=null!=(n=null!=(r=null!=(a=null!=(i=t.matches)?i:t.matchesSelector)?a:t.webkitMatchesSelector)?r:t.mozMatchesSelector)?n:t.msMatchesSelector,function(t,n){if((null!=t?t.nodeType:void 0)===Node.ELEMENT_NODE)return e.call(t,n)}}()}.call(this),function(){var t,n,r;t=e.config,r=t.i18n,e.getI18nValue=function(a,i){var o,s;return null==a&&(a=""),o=(null!=i?i:{locale:t.locale}).locale,s=n(r[o],a),null!=s?s:o!==t.defaultLocale?e.getI18nValue(a,{locale:t.defaultLocale}):void 0},e.translate=function(t,n,r){var a,i,o;null==n&&(n={}),o=e.getI18nValue(t,r);for(a in n)i=n[a],o=o.replace("{"+a+"}",i);return o},n=function(t,e){var n,r,a,i,o;for(o=t,i=e.split("."),n=0,a=i.length;n<a;n++){if(r=i[n],null==o[r])return null;o=o[r]}return o}}.call(this),function(){var t,n,r,a,i;t=e.getI18nValue,i=e.translate,e.strftime=a=function(e,o){var s,u,c,l,d,h,f;return u=e.getDay(),s=e.getDate(),d=e.getMonth(),f=e.getFullYear(),c=e.getHours(),l=e.getMinutes(),h=e.getSeconds(),o.replace(/%([%aAbBcdeHIlmMpPSwyYZ])/g,function(o){switch(o[0],o[1]){case"%":return"%";case"a":return t("date.abbrDayNames")[u];case"A":return t("date.dayNames")[u];case"b":return t("date.abbrMonthNames")[d];case"B":return t("date.monthNames")[d];case"c":return e.toString();case"d":return n(s);case"e":return s;case"H":return n(c);case"I":return n(a(e,"%l"));case"l":return 0===c||12===c?12:(c+12)%12;case"m":return n(d+1);case"M":return n(l);case"p":return i("time."+(c>11?"pm":"am")).toUpperCase();case"P":return i("time."+(c>11?"pm":"am"));case"S":return n(h);case"w":return u;case"y":return n(f%100);case"Y":return f;case"Z":return r(e)}})},n=function(t){return("0"+t).slice(-2)},r=function(t){var e,n,r,a,i;return i=t.toString(),(e=null!=(n=i.match(/\(([\w\s]+)\)$/))?n[1]:void 0)?/\s/.test(e)?e.match(/\b(\w)/g).join(""):e:(e=null!=(r=i.match(/(\w{3,4})\s\d{4}$/))?r[1]:void 0)?e:(e=null!=(a=i.match(/(UTC[\+\-]\d+)/))?a[1]:void 0)?e:""}}.call(this),function(){e.CalendarDate=function(){function t(t,e,n){this.date=new Date(Date.UTC(t,e-1)),this.date.setUTCDate(n),this.year=this.date.getUTCFullYear(),this.month=this.date.getUTCMonth()+1,this.day=this.date.getUTCDate(),this.value=this.date.getTime()}return t.fromDate=function(t){return new this(t.getFullYear(),t.getMonth()+1,t.getDate())},t.today=function(){return this.fromDate(new Date)},t.prototype.equals=function(t){return(null!=t?t.value:void 0)===this.value},t.prototype.is=function(t){return this.equals(t)},t.prototype.isToday=function(){return this.is(this.constructor.today())},t.prototype.occursOnSameYearAs=function(t){return this.year===(null!=t?t.year:void 0)},t.prototype.occursThisYear=function(){return this.occursOnSameYearAs(this.constructor.today())},t.prototype.daysSince=function(t){if(t)return(this.date-t.date)/864e5},t.prototype.daysPassed=function(){return this.constructor.today().daysSince(this)},t}()}.call(this),function(){var t,n,r;n=e.strftime,r=e.translate,t=e.getI18nValue,e.RelativeTime=function(){function a(t){this.date=t,this.calendarDate=e.CalendarDate.fromDate(this.date)}return a.prototype.toString=function(){var t,e;return(e=this.toTimeElapsedString())?r("time.elapsed",{time:e}):(t=this.toWeekdayString())?(e=this.toTimeString(),r("datetime.at",{date:t,time:e})):r("date.on",{date:this.toDateString()})},a.prototype.toTimeOrDateString=function(){return this.calendarDate.isToday()?this.toTimeString():this.toDateString()},a.prototype.toTimeElapsedString=function(){var t,e,n,a,i;return n=(new Date).getTime()-this.date.getTime(),a=Math.round(n/1e3),e=Math.round(a/60),t=Math.round(e/60),n<0?null:a<10?(i=r("time.second"),r("time.singular",{time:i})):a<45?a+" "+r("time.seconds"):a<90?(i=r("time.minute"),r("time.singular",{time:i})):e<45?e+" "+r("time.minutes"):e<90?(i=r("time.hour"),r("time.singularAn",{time:i})):t<24?t+" "+r("time.hours"):""},a.prototype.toWeekdayString=function(){switch(this.calendarDate.daysPassed()){case 0:return r("date.today");case 1:return r("date.yesterday");case-1:return r("date.tomorrow");case 2:case 3:case 4:case 5:case 6:return n(this.date,"%A");default:return""}},a.prototype.toDateString=function(){var e;return e=t(this.calendarDate.occursThisYear()?"date.formats.thisYear":"date.formats.default"),n(this.date,e)},a.prototype.toTimeString=function(){return n(this.date,t("time.formats.default"))},a}()}.call(this),function(){var t,n=function(t,e){return function(){return t.apply(e,arguments)}};t=e.elementMatchesSelector,e.PageObserver=function(){function e(t,e){this.selector=t,this.callback=e,this.processInsertion=n(this.processInsertion,this),this.processMutations=n(this.processMutations,this)}return e.prototype.start=function(){if(!this.started)return this.observeWithMutationObserver()||this.observeWithMutationEvent(),this.started=!0},e.prototype.observeWithMutationObserver=function(){var t;if("undefined"!=typeof MutationObserver&&null!==MutationObserver)return t=new MutationObserver(this.processMutations),t.observe(document.documentElement,{childList:!0,subtree:!0}),!0},e.prototype.observeWithMutationEvent=function(){return addEventListener("DOMNodeInserted",this.processInsertion,!1),!0},e.prototype.findSignificantElements=function(e){var n;return n=[],(null!=e?e.nodeType:void 0)===Node.ELEMENT_NODE&&(t(e,this.selector)&&n.push(e),n.push.apply(n,e.querySelectorAll(this.selector))),n},e.prototype.processMutations=function(t){var e,n,r,a,i,o,s,u;for(e=[],n=0,a=t.length;n<a;n++)switch(o=t[n],o.type){case"childList":for(u=o.addedNodes,r=0,i=u.length;r<i;r++)s=u[r],e.push.apply(e,this.findSignificantElements(s))}return this.notify(e)},e.prototype.processInsertion=function(t){var e;return e=this.findSignificantElements(t.target),this.notify(e)},e.prototype.notify=function(t){if(null!=t?t.length:void 0)return"function"==typeof this.callback?this.callback(t):void 0},e}()}.call(this),function(){var t,n,r,a,i=function(t,e){return function(){return t.apply(e,arguments)}};r=e.parseDate,a=e.strftime,n=e.getI18nValue,t=e.config,e.Controller=function(){function o(){this.processElements=i(this.processElements,this),this.pageObserver=new e.PageObserver(s,this.processElements)}var s,u,c;return s="time[data-local]:not([data-localized])",o.prototype.start=function(){if(!this.started)return this.processElements(),this.startTimer(),this.pageObserver.start(),this.started=!0},o.prototype.startTimer=function(){var e;if(e=t.timerInterval)return null!=this.timer?this.timer:this.timer=setInterval(this.processElements,e)},o.prototype.processElements=function(t){var e,n,r;for(null==t&&(t=document.querySelectorAll(s)),n=0,r=t.length;n<r;n++)e=t[n],this.processElement(e);return t.length},o.prototype.processElement=function(t){var e,i,o,s,l;if(e=t.getAttribute("datetime"),i=t.getAttribute("data-format"),o=t.getAttribute("data-local"),s=r(e),!isNaN(s))return t.hasAttribute("title")||(l=a(s,n("datetime.formats.default")),t.setAttribute("title",l)),t.textContent=function(){switch(o){case"time":return u(t),a(s,i);case"date":return u(t),c(s).toDateString();case"time-ago":return c(s).toString();case"time-or-date":return c(s).toTimeOrDateString();case"weekday":return c(s).toWeekdayString();case"weekday-or-date":return c(s).toWeekdayString()||c(s).toDateString()}}()},u=function(t){return t.setAttribute("data-localized","")},c=function(t){return new e.RelativeTime(t)},o}()}.call(this),function(){var t,n,r,a;a=!1,t=function(){return document.attachEvent?"complete"===document.readyState:"loading"!==document.readyState},n=function(t){var e;return null!=(e="function"==typeof requestAnimationFrame?requestAnimationFrame(t):void 0)?e:setTimeout(t,17)},r=function(){var t;return t=e.getController(),t.start()},e.start=function(){if(!a)return a=!0,"undefined"!=typeof MutationObserver&&null!==MutationObserver||t()?r():n(r)},window.LocalTime===e&&e.start()}.call(this)}).call(this),"object"==typeof module&&module.exports?module.exports=e:"function"==typeof define&&define.amd&&define(e)}).call(this);
@@ -1,6 +1,4 @@
1
1
  module LocalTimeHelper
2
- DEFAULT_FORMAT = '%B %e, %Y %l:%M%P'
3
-
4
2
  def local_time(time, options = nil)
5
3
  time = utc_time(time)
6
4
 
@@ -15,7 +13,7 @@ module LocalTimeHelper
15
13
 
16
14
  def local_date(time, options = nil)
17
15
  options, format = extract_options_and_value(options, :format)
18
- options[:format] = format || '%B %e, %Y'
16
+ options[:format] = format || LocalTime.default_date_format
19
17
  local_time time, options
20
18
  end
21
19
 
@@ -26,11 +24,11 @@ module LocalTimeHelper
26
24
  options[:data] ||= {}
27
25
  options[:data].merge! local: type
28
26
 
29
- time_tag time, time.strftime(DEFAULT_FORMAT), options
27
+ time_tag time, time.strftime(LocalTime.default_time_format), options
30
28
  end
31
29
 
32
30
  def local_time_ago(time, options = nil)
33
- options, type = extract_options_and_value(options, :type)
31
+ options, * = extract_options_and_value(options, :type)
34
32
  options[:type] = 'time-ago'
35
33
  local_relative_time time, options
36
34
  end
@@ -49,12 +47,12 @@ module LocalTimeHelper
49
47
  if (i18n_format = I18n.t("time.formats.#{format}", default: [:"date.formats.#{format}", ''])).present?
50
48
  i18n_format
51
49
  elsif (date_format = Time::DATE_FORMATS[format] || Date::DATE_FORMATS[format])
52
- date_format.is_a?(Proc) ? DEFAULT_FORMAT : date_format
50
+ date_format.is_a?(Proc) ? LocalTime.default_time_format : date_format
53
51
  else
54
- DEFAULT_FORMAT
52
+ LocalTime.default_time_format
55
53
  end
56
54
  else
57
- format.presence || DEFAULT_FORMAT
55
+ format.presence || LocalTime.default_time_format
58
56
  end
59
57
  end
60
58
 
data/lib/local_time.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  module LocalTime
2
+ mattr_accessor(:default_time_format) { "%B %e, %Y %l:%M%P" }
3
+ mattr_accessor(:default_date_format) { "%B %e, %Y" }
4
+
2
5
  class Engine < ::Rails::Engine
3
6
  end
4
7
  end
@@ -1,8 +1,10 @@
1
- require_relative '../../app/helpers/local_time_helper'
2
- require 'active_support/all'
1
+ require 'rails'
3
2
  require 'action_view'
4
3
  require 'rails-dom-testing'
5
4
 
5
+ require 'local_time'
6
+ require_relative '../../app/helpers/local_time_helper'
7
+
6
8
  require 'minitest/autorun'
7
9
  begin
8
10
  # 2.0.0
@@ -0,0 +1,47 @@
1
+ {addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
2
+ {config} = LocalTime
3
+ {i18n} = config
4
+
5
+ testGroup "i18n", ->
6
+ testAsync "updating a value", (done) ->
7
+ now = moment()
8
+ values = i18n[config.defaultLocale].date
9
+
10
+ originalValue = values.today
11
+ values.today = "2day"
12
+
13
+ el = addTimeEl type: "weekday", datetime: now.toISOString()
14
+ defer ->
15
+ assert.equal getText(el), "2day"
16
+ assert.equal getText(el), "2day"
17
+ values.today = originalValue
18
+ done()
19
+
20
+ testAsync "adding a new locale", (done) ->
21
+ now = moment()
22
+
23
+ originalLocale = config.locale
24
+ config.locale = "es"
25
+ i18n.es = date: today: "hoy"
26
+
27
+ el = addTimeEl type: "weekday", datetime: now.toISOString()
28
+ defer ->
29
+ assert.equal getText(el), "hoy"
30
+ config.locale = originalLocale
31
+ done()
32
+
33
+ testAsync "falling back to the default locale", (done) ->
34
+ now = moment()
35
+ yesterday = moment().subtract("days", 1)
36
+
37
+ originalLocale = config.locale
38
+ config.locale = "es"
39
+ i18n.es = date: yesterday: "ayer"
40
+
41
+ elWithTranslation = addTimeEl type: "weekday", datetime: yesterday.toISOString()
42
+ elWithoutTranslation = addTimeEl type: "weekday", datetime: now.toISOString()
43
+ defer ->
44
+ assert.equal getText(elWithTranslation), "ayer"
45
+ assert.equal getText(elWithoutTranslation), "today"
46
+ config.locale = originalLocale
47
+ done()
@@ -0,0 +1,36 @@
1
+ {addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
2
+
3
+ testGroup "localized", ->
4
+ for id in ["one", "two", "past", "future"]
5
+ test id, ->
6
+ assertLocalized id
7
+
8
+ test "date", ->
9
+ assertLocalized "date", "date"
10
+
11
+ test "unparseable time", ->
12
+ el = addTimeEl format: "%Y", datetime: ":("
13
+ setText el, "2013"
14
+ assert.equal getText(el), "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
+ assert.ok datetime = el.getAttribute "datetime"
29
+ assert.ok local = getText el
30
+
31
+ datetimeParsed = moment datetime
32
+ localParsed = moment local, momentFormat
33
+
34
+ assert.ok datetimeParsed.isValid()
35
+ assert.ok localParsed.isValid()
36
+ assert.equal datetimeParsed[compare](), localParsed[compare]()
@@ -0,0 +1,89 @@
1
+ {addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
2
+
3
+ testGroup "relative date", ->
4
+ testAsync "this year", (done) ->
5
+ now = moment()
6
+ el = addTimeEl type: "date", datetime: now.toISOString()
7
+ defer ->
8
+ assert.equal getText(el), now.format("MMM D")
9
+ done()
10
+
11
+ testAsync "last year", (done) ->
12
+ before = moment().subtract("years", 1).subtract("days", 1)
13
+ el = addTimeEl type: "date", datetime: before.toISOString()
14
+ defer ->
15
+ assert.equal getText(el), before.format("MMM D, YYYY")
16
+ done()
17
+
18
+ testGroup "relative time or date", ->
19
+ testAsync "today", (done) ->
20
+ now = moment()
21
+ el = addTimeEl type: "time-or-date", datetime: now.toISOString()
22
+ defer ->
23
+ assert.equal getText(el), now.format("h:mma")
24
+ done()
25
+
26
+ testAsync "before today", (done) ->
27
+ before = moment().subtract("days", 1)
28
+ el = addTimeEl type: "time-or-date", datetime: before.toISOString()
29
+ defer ->
30
+ assert.equal getText(el), before.format("MMM D")
31
+ done()
32
+
33
+ testGroup "relative weekday", ->
34
+ testAsync "today", (done) ->
35
+ now = moment()
36
+ el = addTimeEl type: "weekday", datetime: now.toISOString()
37
+ defer ->
38
+ assert.equal getText(el), "today"
39
+ done()
40
+
41
+ testAsync "yesterday", (done) ->
42
+ yesterday = moment().subtract("days", 1)
43
+ el = addTimeEl type: "weekday", datetime: yesterday.toISOString()
44
+ defer ->
45
+ assert.equal getText(el), "yesterday"
46
+ done()
47
+
48
+ testAsync "this week", (done) ->
49
+ recent = moment().subtract("days", 3)
50
+ el = addTimeEl type: "weekday", datetime: recent.toISOString()
51
+ defer ->
52
+ assert.equal getText(el), recent.format("dddd")
53
+ done()
54
+
55
+ testAsync "before this week", (done) ->
56
+ before = moment().subtract("days", 8)
57
+ el = addTimeEl type: "weekday", datetime: before.toISOString()
58
+ defer ->
59
+ assert.equal getText(el), ""
60
+ done()
61
+
62
+ testGroup "relative weekday or date", ->
63
+ testAsync "today", (done) ->
64
+ now = moment()
65
+ el = addTimeEl type: "weekday-or-date", datetime: now.toISOString()
66
+ defer ->
67
+ assert.equal getText(el), "today"
68
+ done()
69
+
70
+ testAsync "yesterday", (done) ->
71
+ yesterday = moment().subtract("days", 1)
72
+ el = addTimeEl type: "weekday-or-date", datetime: yesterday.toISOString()
73
+ defer ->
74
+ assert.equal getText(el), "yesterday"
75
+ done()
76
+
77
+ testAsync "this week", (done) ->
78
+ recent = moment().subtract("days", 3)
79
+ el = addTimeEl type: "weekday-or-date", datetime: recent.toISOString()
80
+ defer ->
81
+ assert.equal getText(el), recent.format("dddd")
82
+ done()
83
+
84
+ testAsync "before this week", (done) ->
85
+ before = moment().subtract("days", 8)
86
+ el = addTimeEl type: "weekday-or-date", datetime: before.toISOString()
87
+ defer ->
88
+ assert.equal getText(el), before.format("MMM D")
89
+ done()
@@ -0,0 +1,48 @@
1
+ {addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
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
+ testGroup "strftime", ->
24
+ for day in [0..30] by 6
25
+ do (day) ->
26
+ for hour in [0..24] by 6
27
+ do (hour) ->
28
+
29
+ for format, momentFormat of momentMap
30
+ do (format, momentFormat) ->
31
+ test "#{format} (+#{day} days, #{hour} hours)", ->
32
+ now = moment().add("days", day).add("hours", hour)
33
+ el = addTimeEl {format, datetime: now.toISOString()}
34
+ LocalTime.process(el)
35
+
36
+ assert.equal getText(el),
37
+ if func = momentFormat.match(/(\w+)\(\)/)?[1]
38
+ now.toDate()[func]()
39
+ else
40
+ now.format momentFormat
41
+
42
+ test "%Z Timezone (+#{day} days, #{hour} hours)", ->
43
+ now = moment().add("days", day).add("hours", hour)
44
+ el = addTimeEl format: "%Z", datetime: now.toISOString()
45
+ LocalTime.process(el)
46
+
47
+ text = getText el
48
+ assert.ok /^(\w{3,4}|UTC[\+\-]\d+)$/.test(text), "'#{text}' doesn't look like a timezone. System date: '#{new Date}'"
@@ -0,0 +1,43 @@
1
+ #= require moment
2
+ #= require sinon-timers
3
+
4
+ #= require_self
5
+ #= require_directory .
6
+
7
+ LocalTime.TestHelpers =
8
+ assert: QUnit.assert
9
+ testGroup: QUnit.module
10
+ test: QUnit.test
11
+
12
+ testAsync: (name, callback) ->
13
+ QUnit.test name, (assert) ->
14
+ done = assert.async()
15
+ callback(done)
16
+
17
+ addTimeEl: ({format, type, datetime} = {}) ->
18
+ format ?= "%Y"
19
+ type ?= "time"
20
+ datetime ?= "2013-11-12T12:13:00Z"
21
+
22
+ el = document.createElement "time"
23
+ el.setAttribute "data-local", type
24
+ el.setAttribute "data-format", format
25
+ el.setAttribute "datetime", datetime
26
+ document.body.appendChild el
27
+ el
28
+
29
+ setText: (el, text) ->
30
+ el.textContent = text
31
+
32
+ getText: (el) ->
33
+ # innerHTML works in all browsers so using it ensures we're
34
+ # reading the text content, not a potentially arbitrary property.
35
+ el.innerHTML
36
+
37
+ triggerEvent: (name, el = document) ->
38
+ event = document.createEvent "Events"
39
+ event.initEvent name, true, true
40
+ el.dispatchEvent event
41
+
42
+ defer: (callback) ->
43
+ setTimeout(callback, 1)
@@ -0,0 +1,56 @@
1
+ {addTimeEl, assert, defer, getText, setText, test, testAsync, testGroup, triggerEvent} = LocalTime.TestHelpers
2
+
3
+ testGroup "time ago", ->
4
+ test "a second ago", ->
5
+ assertTimeAgo "a second ago", "seconds", 9
6
+
7
+ test "seconds ago", ->
8
+ assertTimeAgo "44 seconds ago", "seconds", 44
9
+
10
+ test "a minute ago", ->
11
+ assertTimeAgo "a minute ago", "seconds", 89
12
+
13
+ test "minutes ago", ->
14
+ assertTimeAgo "44 minutes ago", "minutes", 44
15
+
16
+ test "an hour ago", ->
17
+ assertTimeAgo "an hour ago", "minutes", 89
18
+
19
+ test "hours ago", ->
20
+ assertTimeAgo "23 hours ago", "hours", 23
21
+
22
+ test "yesterday", ->
23
+ time = moment().subtract("days", 1).format "h:mma"
24
+ assertTimeAgo "yesterday at #{time}", "days", 1
25
+
26
+ test "tomorrow", ->
27
+ time = moment().add("days", 1).format "h:mma"
28
+ assertTimeAgo "tomorrow at #{time}", "days", -1
29
+
30
+ test "last week", ->
31
+ ago = moment().subtract "days", 5
32
+ day = ago.format "dddd"
33
+ time = ago.format "h:mma"
34
+
35
+ assertTimeAgo "#{day} at #{time}", "days", 5
36
+
37
+ test "this year", ->
38
+ clock = sinon.useFakeTimers(new Date(2013,11,11,11,11).getTime(), "Date")
39
+ date = moment().subtract("days", 7).format "MMM D"
40
+ assertTimeAgo "on #{date}", "days", 7
41
+ clock.restore()
42
+
43
+ test "last year", ->
44
+ date = moment().subtract("days", 366).format "MMM D, YYYY"
45
+ assertTimeAgo "on #{date}", "days", 366
46
+
47
+ test "next year", ->
48
+ date = moment().add("days", 366).format "MMM D, YYYY"
49
+ assertTimeAgo "on #{date}", "days", -366
50
+
51
+ assertTimeAgo = (string, unit, amount) ->
52
+ el = document.getElementById "ago"
53
+ el.setAttribute "data-local", "time-ago"
54
+ el.setAttribute "datetime", moment().subtract(unit, amount).utc().toISOString()
55
+ LocalTime.run()
56
+ assert.equal getText(el), string
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: local_time
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javan Makhmali
@@ -9,78 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-30 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
- - !ruby/object:Gem::Dependency
29
- name: rails
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
- - !ruby/object:Gem::Dependency
43
- name: rails-dom-testing
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: '0'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
- - !ruby/object:Gem::Dependency
57
- name: blade
58
- requirement: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - "~>"
61
- - !ruby/object:Gem::Version
62
- version: 0.3.0
63
- type: :development
64
- prerelease: false
65
- version_requirements: !ruby/object:Gem::Requirement
66
- requirements:
67
- - - "~>"
68
- - !ruby/object:Gem::Version
69
- version: 0.3.0
70
- - !ruby/object:Gem::Dependency
71
- name: blade-sauce_labs_plugin
72
- requirement: !ruby/object:Gem::Requirement
73
- requirements:
74
- - - "~>"
75
- - !ruby/object:Gem::Version
76
- version: 0.3.0
77
- type: :development
78
- prerelease: false
79
- version_requirements: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - "~>"
82
- - !ruby/object:Gem::Version
83
- version: 0.3.0
12
+ date: 2017-08-07 00:00:00.000000000 Z
13
+ dependencies: []
84
14
  description:
85
15
  email: javan@basecamp.com
86
16
  executables: []
@@ -89,18 +19,17 @@ extra_rdoc_files: []
89
19
  files:
90
20
  - MIT-LICENSE
91
21
  - README.md
92
- - app/assets/javascripts/local_time.js.coffee
22
+ - app/assets/javascripts/local-time.js
93
23
  - app/helpers/local_time_helper.rb
94
24
  - lib/local_time.rb
95
25
  - test/helpers/local_time_helper_test.rb
96
26
  - test/javascripts/fixtures/body.html
97
- - test/javascripts/src/local_time_test.js.coffee
98
- - test/javascripts/src/page_events_test.js.coffee
99
- - test/javascripts/src/public_api_test.js.coffee
100
- - test/javascripts/src/relative_date_test.js.coffee
101
- - test/javascripts/src/strftime_test.js.coffee
102
- - test/javascripts/src/test.js.coffee
103
- - test/javascripts/src/time_ago_test.js.coffee
27
+ - test/javascripts/src/i18n_test.coffee
28
+ - test/javascripts/src/local_time_test.coffee
29
+ - test/javascripts/src/relative_date_test.coffee
30
+ - test/javascripts/src/strftime_test.coffee
31
+ - test/javascripts/src/test.coffee
32
+ - test/javascripts/src/time_ago_test.coffee
104
33
  - test/javascripts/vendor/moment.js
105
34
  - test/javascripts/vendor/sinon-timers.js
106
35
  homepage:
@@ -123,19 +52,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
52
  version: '0'
124
53
  requirements: []
125
54
  rubyforge_project:
126
- rubygems_version: 2.4.6
55
+ rubygems_version: 2.5.2
127
56
  signing_key:
128
57
  specification_version: 4
129
58
  summary: Rails engine for cache-friendly, client-side local time
130
59
  test_files:
131
60
  - test/helpers/local_time_helper_test.rb
132
61
  - test/javascripts/fixtures/body.html
133
- - test/javascripts/src/local_time_test.js.coffee
134
- - test/javascripts/src/page_events_test.js.coffee
135
- - test/javascripts/src/public_api_test.js.coffee
136
- - test/javascripts/src/relative_date_test.js.coffee
137
- - test/javascripts/src/strftime_test.js.coffee
138
- - test/javascripts/src/test.js.coffee
139
- - test/javascripts/src/time_ago_test.js.coffee
62
+ - test/javascripts/src/i18n_test.coffee
63
+ - test/javascripts/src/local_time_test.coffee
64
+ - test/javascripts/src/relative_date_test.coffee
65
+ - test/javascripts/src/strftime_test.coffee
66
+ - test/javascripts/src/test.coffee
67
+ - test/javascripts/src/time_ago_test.coffee
140
68
  - test/javascripts/vendor/moment.js
141
69
  - test/javascripts/vendor/sinon-timers.js
@@ -1,251 +0,0 @@
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
- parseTimeZone = (time) ->
25
- string = time.toString()
26
- # Sun Aug 30 2015 10:22:57 GMT-0400 (NAME)
27
- if name = string.match(/\(([\w\s]+)\)$/)?[1]
28
- if /\s/.test(name)
29
- # Sun Aug 30 2015 10:22:57 GMT-0400 (Eastern Daylight Time)
30
- name.match(/\b(\w)/g).join("")
31
- else
32
- # Sun Aug 30 2015 10:22:57 GMT-0400 (EDT)
33
- name
34
- # Sun Aug 30 10:22:57 EDT 2015
35
- else if name = string.match(/(\w{3,4})\s\d{4}$/)?[1]
36
- name
37
- # "Sun Aug 30 10:22:57 UTC-0400 2015"
38
- else if name = string.match(/(UTC[\+\-]\d+)/)?[1]
39
- name
40
- else
41
- ""
42
-
43
- strftime = (time, formatString) ->
44
- day = time.getDay()
45
- date = time.getDate()
46
- month = time.getMonth()
47
- year = time.getFullYear()
48
- hour = time.getHours()
49
- minute = time.getMinutes()
50
- second = time.getSeconds()
51
-
52
- formatString.replace /%([%aAbBcdeHIlmMpPSwyYZ])/g, ([match, modifier]) ->
53
- switch modifier
54
- when '%' then '%'
55
- when 'a' then weekdays[day].slice 0, 3
56
- when 'A' then weekdays[day]
57
- when 'b' then months[month].slice 0, 3
58
- when 'B' then months[month]
59
- when 'c' then time.toString()
60
- when 'd' then pad date
61
- when 'e' then date
62
- when 'H' then pad hour
63
- when 'I' then pad strftime time, '%l'
64
- when 'l' then (if hour is 0 or hour is 12 then 12 else (hour + 12) % 12)
65
- when 'm' then pad month + 1
66
- when 'M' then pad minute
67
- when 'p' then (if hour > 11 then 'PM' else 'AM')
68
- when 'P' then (if hour > 11 then 'pm' else 'am')
69
- when 'S' then pad second
70
- when 'w' then day
71
- when 'y' then pad year % 100
72
- when 'Y' then year
73
- when 'Z' then parseTimeZone(time)
74
-
75
-
76
- class CalendarDate
77
- @fromDate: (date) ->
78
- new this date.getFullYear(), date.getMonth() + 1, date.getDate()
79
-
80
- @today: ->
81
- @fromDate new Date
82
-
83
- constructor: (year, month, day) ->
84
- @date = new Date Date.UTC year, month - 1
85
- @date.setUTCDate day
86
-
87
- @year = @date.getUTCFullYear()
88
- @month = @date.getUTCMonth() + 1
89
- @day = @date.getUTCDate()
90
- @value = @date.getTime()
91
-
92
- equals: (calendarDate) ->
93
- calendarDate?.value is @value
94
-
95
- is: (calendarDate) ->
96
- @equals calendarDate
97
-
98
- isToday: ->
99
- @is @constructor.today()
100
-
101
- occursOnSameYearAs: (date) ->
102
- @year is date?.year
103
-
104
- occursThisYear: ->
105
- @occursOnSameYearAs @constructor.today()
106
-
107
- daysSince: (date) ->
108
- if date
109
- (@date - date.date) / (1000 * 60 * 60 * 24)
110
-
111
- daysPassed: ->
112
- @constructor.today().daysSince @
113
-
114
-
115
- class RelativeTime
116
- constructor: (@date) ->
117
- @calendarDate = CalendarDate.fromDate @date
118
-
119
- toString: ->
120
- # Today: "Saved 5 hours ago"
121
- if ago = @timeElapsed()
122
- "#{ago} ago"
123
-
124
- # Yesterday: "Saved yesterday at 8:15am"
125
- # This week: "Saved Thursday at 8:15am"
126
- else if day = @relativeWeekday()
127
- "#{day} at #{@formatTime()}"
128
-
129
- # Older: "Saved on Dec 15"
130
- else
131
- "on #{@formatDate()}"
132
-
133
- toTimeOrDateString: ->
134
- if @calendarDate.isToday()
135
- @formatTime()
136
- else
137
- @formatDate()
138
-
139
- timeElapsed: ->
140
- ms = new Date().getTime() - @date.getTime()
141
- sec = Math.round ms / 1000
142
- min = Math.round sec / 60
143
- hr = Math.round min / 60
144
-
145
- if ms < 0
146
- null
147
- else if sec < 10
148
- "a second"
149
- else if sec < 45
150
- "#{sec} seconds"
151
- else if sec < 90
152
- "a minute"
153
- else if min < 45
154
- "#{min} minutes"
155
- else if min < 90
156
- "an hour"
157
- else if hr < 24
158
- "#{hr} hours"
159
- else
160
- null
161
-
162
- relativeWeekday: ->
163
- switch @calendarDate.daysPassed()
164
- when 0
165
- "today"
166
- when 1
167
- "yesterday"
168
- when 2,3,4,5,6
169
- strftime @date, "%A"
170
-
171
- formatDate: ->
172
- format = "%b %e"
173
- format += ", %Y" unless @calendarDate.occursThisYear()
174
- strftime @date, format
175
-
176
- formatTime: ->
177
- strftime @date, '%l:%M%P'
178
-
179
- relativeDate = (date) ->
180
- new RelativeTime(date).formatDate()
181
-
182
- relativeTimeAgo = (date) ->
183
- new RelativeTime(date).toString()
184
-
185
- relativeTimeOrDate = (date) ->
186
- new RelativeTime(date).toTimeOrDateString()
187
-
188
- relativeWeekday = (date) ->
189
- if day = new RelativeTime(date).relativeWeekday()
190
- day.charAt(0).toUpperCase() + day.substring(1)
191
-
192
-
193
- domLoaded = false
194
-
195
- update = (callback) ->
196
- callback() if domLoaded
197
-
198
- document.addEventListener "time:elapse", callback
199
-
200
- if Turbolinks?.supported
201
- document.addEventListener "page:update", callback
202
- else
203
- jQuery?(document).on "ajaxSuccess", (event, xhr) ->
204
- callback() if jQuery.trim xhr.responseText
205
-
206
- process = (selector, callback) ->
207
- update ->
208
- for element in document.querySelectorAll selector
209
- callback element
210
-
211
- document.addEventListener "DOMContentLoaded", ->
212
- domLoaded = true
213
- textProperty = if "textContent" of document.body then "textContent" else "innerText"
214
-
215
- process "time[data-local]:not([data-localized])", (element) ->
216
- datetime = element.getAttribute "datetime"
217
- format = element.getAttribute "data-format"
218
- local = element.getAttribute "data-local"
219
-
220
- time = new Date Date.parse datetime
221
- return if isNaN time
222
-
223
- unless element.hasAttribute("title")
224
- element.setAttribute "title", strftime(time, "%B %e, %Y at %l:%M%P %Z")
225
-
226
- element[textProperty] =
227
- switch local
228
- when "date"
229
- element.setAttribute "data-localized", true
230
- relativeDate time
231
- when "time"
232
- element.setAttribute "data-localized", true
233
- strftime time, format
234
- when "time-ago"
235
- relativeTimeAgo time
236
- when "time-or-date"
237
- relativeTimeOrDate time
238
- when "weekday"
239
- relativeWeekday(time) ? ""
240
- when "weekday-or-date"
241
- relativeWeekday(time) ? relativeDate(time)
242
-
243
- run = ->
244
- event = document.createEvent "Events"
245
- event.initEvent "time:elapse", true, true
246
- document.dispatchEvent event
247
-
248
- setInterval run, 60 * 1000
249
-
250
- # Public API
251
- @LocalTime = {relativeDate, relativeTimeAgo, relativeTimeOrDate, relativeWeekday, run, strftime}
@@ -1,36 +0,0 @@
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 format: "%Y", datetime: ":("
12
- setText el, "2013"
13
- run()
14
- equal getText(el), "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 = getText el
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]()
@@ -1,25 +0,0 @@
1
- module "page events"
2
-
3
- test "document DOMContentLoaded", ->
4
- el = addTimeEl()
5
- triggerEvent "DOMContentLoaded"
6
- ok getText el
7
-
8
- test "document time:elapse", ->
9
- el = addTimeEl()
10
- triggerEvent "time:elapse"
11
- ok getText el
12
-
13
- test "document page:update with Turbolinks on", ->
14
- el = addTimeEl()
15
- triggerEvent "page:update"
16
- ok not getText el
17
-
18
- original = window.Turbolinks
19
- window.Turbolinks = { supported: true }
20
-
21
- triggerEvent "DOMContentLoaded"
22
- triggerEvent "page:update"
23
- ok getText el
24
-
25
- window.Turbolinks = original
@@ -1,11 +0,0 @@
1
- module "public API"
2
-
3
- for name, method of @LocalTime when name isnt "strftime"
4
- do (name, method) ->
5
- test "##{name}", ->
6
- ok method(new Date())
7
-
8
- {strftime} = @LocalTime
9
-
10
- test "#strftime", =>
11
- ok strftime(new Date(), "%Y")
@@ -1,96 +0,0 @@
1
- module "relative date"
2
-
3
- test "this year", ->
4
- now = moment()
5
- el = addTimeEl type: "date", datetime: now.toISOString()
6
- run()
7
-
8
- equal getText(el), now.format("MMM D")
9
-
10
- test "last year", ->
11
- before = moment().subtract("years", 1).subtract("days", 1)
12
- el = addTimeEl type: "date", datetime: before.toISOString()
13
- run()
14
-
15
- equal getText(el), before.format("MMM D, YYYY")
16
-
17
-
18
- module "relative time or date"
19
-
20
-
21
- test "today", ->
22
- now = moment()
23
- el = addTimeEl type: "time-or-date", datetime: now.toISOString()
24
- run()
25
-
26
- equal getText(el), now.format("h:mma")
27
-
28
- test "before today", ->
29
- before = moment().subtract("days", 1)
30
- el = addTimeEl type: "time-or-date", datetime: before.toISOString()
31
- run()
32
-
33
- equal getText(el), before.format("MMM D")
34
-
35
-
36
- module "relative weekday"
37
-
38
-
39
- test "today", ->
40
- now = moment()
41
- el = addTimeEl type: "weekday", datetime: now.toISOString()
42
- run()
43
-
44
- equal getText(el), "Today"
45
-
46
- test "yesterday", ->
47
- yesterday = moment().subtract("days", 1)
48
- el = addTimeEl type: "weekday", datetime: yesterday.toISOString()
49
- run()
50
-
51
- equal getText(el), "Yesterday"
52
-
53
- test "this week", ->
54
- recent = moment().subtract("days", 3)
55
- el = addTimeEl type: "weekday", datetime: recent.toISOString()
56
- run()
57
-
58
- equal getText(el), recent.format("dddd")
59
-
60
- test "before this week", ->
61
- before = moment().subtract("days", 8)
62
- el = addTimeEl type: "weekday", datetime: before.toISOString()
63
- run()
64
-
65
- equal getText(el), ""
66
-
67
- module "relative weekday or date"
68
-
69
-
70
- test "today", ->
71
- now = moment()
72
- el = addTimeEl type: "weekday-or-date", datetime: now.toISOString()
73
- run()
74
-
75
- equal getText(el), "Today"
76
-
77
- test "yesterday", ->
78
- yesterday = moment().subtract("days", 1)
79
- el = addTimeEl type: "weekday-or-date", datetime: yesterday.toISOString()
80
- run()
81
-
82
- equal getText(el), "Yesterday"
83
-
84
- test "this week", ->
85
- recent = moment().subtract("days", 3)
86
- el = addTimeEl type: "weekday-or-date", datetime: recent.toISOString()
87
- run()
88
-
89
- equal getText(el), recent.format("dddd")
90
-
91
- test "before this week", ->
92
- before = moment().subtract("days", 8)
93
- el = addTimeEl type: "weekday-or-date", datetime: before.toISOString()
94
- run()
95
-
96
- equal getText(el), before.format("MMM D")
@@ -1,47 +0,0 @@
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
-
28
- for format, momentFormat of momentMap
29
- do (format, momentFormat) ->
30
- test "#{format} (+#{day} days, #{hour} hours)", ->
31
- now = moment().add("days", day).add("hours", hour)
32
- el = addTimeEl {format, datetime: now.toISOString()}
33
- run()
34
-
35
- equal getText(el),
36
- if func = momentFormat.match(/(\w+)\(\)/)?[1]
37
- now.toDate()[func]()
38
- else
39
- now.format momentFormat
40
-
41
- test "%Z Timezone (+#{day} days, #{hour} hours)", ->
42
- now = moment().add("days", day).add("hours", hour)
43
- el = addTimeEl format: "%Z", datetime: now.toISOString()
44
- run()
45
-
46
- text = getText el
47
- ok /^(\w{3,4}|UTC[\+\-]\d+)$/.test(text), "'#{text}' doesn't look like a timezone. System date: '#{new Date}'"
@@ -1,34 +0,0 @@
1
- #= require moment
2
- #= require sinon-timers
3
-
4
- #= require_self
5
- #= require_directory .
6
-
7
- @addTimeEl = ({format, type, datetime} = {}) ->
8
- format ?= "%Y"
9
- type ?= "time"
10
- datetime ?= "2013-11-12T12:13:00Z"
11
-
12
- el = document.createElement "time"
13
- el.setAttribute "data-local", type
14
- el.setAttribute "data-format", format
15
- el.setAttribute "datetime", datetime
16
- document.body.appendChild el
17
- el
18
-
19
- @setText = (el, text) ->
20
- textProperty = if "textContent" of el then "textContent" else "innerText"
21
- el[textProperty] = text
22
-
23
- @getText = (el) ->
24
- # innerHTML works in all browsers so using it ensures we're
25
- # reading the text content, not a potentially arbitrary property.
26
- el.innerHTML
27
-
28
- @triggerEvent = (name, el = document) ->
29
- event = document.createEvent "Events"
30
- event.initEvent name, true, true
31
- el.dispatchEvent event
32
-
33
- @run = ->
34
- triggerEvent "time:elapse"
@@ -1,51 +0,0 @@
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
- clock = sinon.useFakeTimers(new Date(2013,11,11,11,11).getTime(), "Date")
34
- date = moment().subtract("days", 7).format "MMM D"
35
- assertTimeAgo "on #{date}", "days", 7
36
- clock.restore()
37
-
38
- test "last year", ->
39
- date = moment().subtract("days", 366).format "MMM D, YYYY"
40
- assertTimeAgo "on #{date}", "days", 366
41
-
42
- test "next year", ->
43
- date = moment().add("days", 366).format "MMM D, YYYY"
44
- assertTimeAgo "on #{date}", "days", -366
45
-
46
- assertTimeAgo = (string, unit, amount) ->
47
- el = document.getElementById "ago"
48
- el.setAttribute "data-local", "time-ago"
49
- el.setAttribute "datetime", moment().subtract(unit, amount).utc().toISOString()
50
- run()
51
- equal getText(el), string