local_time 1.0.3 → 2.0.0

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