tent-status 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +21 -0
- data/.kick +8 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +232 -0
- data/LICENSE.txt +22 -0
- data/Procfile +1 -0
- data/README.md +14 -0
- data/Rakefile +43 -0
- data/assets/images/.gitkeep +0 -0
- data/assets/images/chosen-sprite.png +0 -0
- data/assets/images/glyphicons-halflings-white.png +0 -0
- data/assets/images/glyphicons-halflings.png +0 -0
- data/assets/javascripts/.gitkeep +0 -0
- data/assets/javascripts/application.js.coffee +122 -0
- data/assets/javascripts/backbone.js +1443 -0
- data/assets/javascripts/backbone_sync.js.coffee +60 -0
- data/assets/javascripts/boot.js.coffee +3 -0
- data/assets/javascripts/chosen.jquery.js +1129 -0
- data/assets/javascripts/collections/.gitkeep +0 -0
- data/assets/javascripts/collections/followers.js.coffee +5 -0
- data/assets/javascripts/collections/followings.js.coffee +5 -0
- data/assets/javascripts/collections/groups.js.coffee +5 -0
- data/assets/javascripts/collections/posts.js.coffee +5 -0
- data/assets/javascripts/fetch_pool.js.coffee +25 -0
- data/assets/javascripts/helpers/.gitkeep +0 -0
- data/assets/javascripts/helpers/formatting.js.coffee +17 -0
- data/assets/javascripts/hogan.js +706 -0
- data/assets/javascripts/http.js.coffee +18 -0
- data/assets/javascripts/jquery.js +9301 -0
- data/assets/javascripts/models/.gitkeep +0 -0
- data/assets/javascripts/models/follower.js.coffee +27 -0
- data/assets/javascripts/models/following.js.coffee +27 -0
- data/assets/javascripts/models/group.js.coffee +4 -0
- data/assets/javascripts/models/post.js.coffee +32 -0
- data/assets/javascripts/models/profile.js.coffee +30 -0
- data/assets/javascripts/moment.js +1106 -0
- data/assets/javascripts/paginator.js.coffee +67 -0
- data/assets/javascripts/router.js.coffee +71 -0
- data/assets/javascripts/routers/.gitkeep +0 -0
- data/assets/javascripts/routers/followers.js.coffee +14 -0
- data/assets/javascripts/routers/followings.js.coffee +14 -0
- data/assets/javascripts/routers/posts.js.coffee +98 -0
- data/assets/javascripts/templates/.gitkeep +0 -0
- data/assets/javascripts/templates/_follower.js.mustache.slim +17 -0
- data/assets/javascripts/templates/_following.js.mustache.slim +17 -0
- data/assets/javascripts/templates/_new_post_form.js.mustache.slim +10 -0
- data/assets/javascripts/templates/_post.js.mustache.slim +10 -0
- data/assets/javascripts/templates/_post_inner.js.mustache.slim +71 -0
- data/assets/javascripts/templates/_profile_stats.js.mustache.slim +11 -0
- data/assets/javascripts/templates/_reply_form.js.mustache.slim +13 -0
- data/assets/javascripts/templates/conversation.js.mustache.slim +13 -0
- data/assets/javascripts/templates/followers.js.mustache.slim +19 -0
- data/assets/javascripts/templates/followings.js.mustache.slim +22 -0
- data/assets/javascripts/templates/posts.js.mustache.slim +25 -0
- data/assets/javascripts/templates/profile.js.mustache.slim +39 -0
- data/assets/javascripts/underscore.js +1059 -0
- data/assets/javascripts/view.js.coffee +140 -0
- data/assets/javascripts/views/.gitkeep +0 -0
- data/assets/javascripts/views/container.js.coffee +6 -0
- data/assets/javascripts/views/expanding_textarea.js.coffee +14 -0
- data/assets/javascripts/views/fetch_posts_pool.js.coffee +61 -0
- data/assets/javascripts/views/follower_groups_form.js.coffee +26 -0
- data/assets/javascripts/views/followers.js.coffee +28 -0
- data/assets/javascripts/views/following_groups_form.js.coffee +25 -0
- data/assets/javascripts/views/followings.js.coffee +27 -0
- data/assets/javascripts/views/new_following_form.js.coffee +15 -0
- data/assets/javascripts/views/new_post_form.js.coffee +178 -0
- data/assets/javascripts/views/post.js.coffee +139 -0
- data/assets/javascripts/views/posts.js.coffee +55 -0
- data/assets/javascripts/views/posts/conversation.js.coffee +18 -0
- data/assets/javascripts/views/profile.js.coffee +29 -0
- data/assets/javascripts/views/profile_follow_button.js.coffee +29 -0
- data/assets/javascripts/views/profile_stats.js.coffee +38 -0
- data/assets/javascripts/views/remove_follower_btn.js.coffee +18 -0
- data/assets/javascripts/views/reply_post_form.js.coffee +30 -0
- data/assets/javascripts/views/unfollow_btn.js.coffee +18 -0
- data/assets/stylesheets/.gitkeep +0 -0
- data/assets/stylesheets/application.css.sass +117 -0
- data/assets/stylesheets/bootstrap-responsive.css +1040 -0
- data/assets/stylesheets/bootstrap.css.erb +5624 -0
- data/assets/stylesheets/chosen.css.erb +397 -0
- data/config.ru +14 -0
- data/config/asset_sync.rb +12 -0
- data/config/evergreen.rb +16 -0
- data/lib/tent-status.rb +6 -0
- data/lib/tent-status/app.rb +263 -0
- data/lib/tent-status/models/user.rb +39 -0
- data/lib/tent-status/sprockets/environment.rb +25 -0
- data/lib/tent-status/sprockets/helpers.rb +5 -0
- data/lib/tent-status/views/application.slim +69 -0
- data/lib/tent-status/views/auth.slim +8 -0
- data/tent-status.gemspec +34 -0
- metadata +415 -0
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class TentStatus.Models.Follower extends Backbone.Model
|
|
2
|
+
model: 'follower'
|
|
3
|
+
url: => "#{TentStatus.api_root}/followers#{ if @id then "/#{@id}" else ''}"
|
|
4
|
+
|
|
5
|
+
initialize: ->
|
|
6
|
+
@on 'sync', @updateProfile
|
|
7
|
+
@updateProfile()
|
|
8
|
+
|
|
9
|
+
updateProfile: =>
|
|
10
|
+
profile = @get('profile')
|
|
11
|
+
core_profile = {}
|
|
12
|
+
basic_profile = {}
|
|
13
|
+
for type, content of profile
|
|
14
|
+
basic_profile = content if type.match(/types\/info\/basic/)
|
|
15
|
+
core_profile = content if type.match(/types\/info\/core/)
|
|
16
|
+
@set 'core_profile', core_profile
|
|
17
|
+
@set 'basic_profile', basic_profile
|
|
18
|
+
|
|
19
|
+
name: =>
|
|
20
|
+
@get('basic_profile')['name'] || @get('core_profile')['entity']
|
|
21
|
+
|
|
22
|
+
hasName: =>
|
|
23
|
+
!!(@get('basic_profile')['name'])
|
|
24
|
+
|
|
25
|
+
avatar: =>
|
|
26
|
+
@get('basic_profile')['avatar_url']
|
|
27
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class TentStatus.Models.Following extends Backbone.Model
|
|
2
|
+
model: 'following'
|
|
3
|
+
url: => "#{TentStatus.api_root}/followings#{ if @id then "/#{@id}" else ''}#{ if @get('guest') then '?guest=true' else '' }"
|
|
4
|
+
|
|
5
|
+
initialize: ->
|
|
6
|
+
@on 'sync', @updateProfile
|
|
7
|
+
@updateProfile()
|
|
8
|
+
|
|
9
|
+
updateProfile: =>
|
|
10
|
+
profile = @get('profile')
|
|
11
|
+
core_profile = {}
|
|
12
|
+
basic_profile = {}
|
|
13
|
+
for type, content of profile
|
|
14
|
+
basic_profile = content if type.match(/types\/info\/basic/)
|
|
15
|
+
core_profile = content if type.match(/types\/info\/core/)
|
|
16
|
+
@set 'core_profile', core_profile
|
|
17
|
+
@set 'basic_profile', basic_profile
|
|
18
|
+
|
|
19
|
+
name: =>
|
|
20
|
+
@get('basic_profile')['name'] || @get('core_profile')['entity']
|
|
21
|
+
|
|
22
|
+
hasName: =>
|
|
23
|
+
!!(@get('basic_profile')['name'])
|
|
24
|
+
|
|
25
|
+
avatar: =>
|
|
26
|
+
@get('basic_profile')['avatar_url']
|
|
27
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class TentStatus.Models.Post extends Backbone.Model
|
|
2
|
+
model: 'post'
|
|
3
|
+
url: => "#{TentStatus.api_root}/posts#{ if @id then "/#{@id}" else ''}"
|
|
4
|
+
|
|
5
|
+
isRepost: =>
|
|
6
|
+
!!(@get('type') || '').match(/repost/)
|
|
7
|
+
|
|
8
|
+
entity: =>
|
|
9
|
+
return TentStatus.Models.profile if TentStatus.Models.profile.entity() == @get('entity')
|
|
10
|
+
(TentStatus.Collections.followings.find (following) => following.get('entity') == @get('entity')) ||
|
|
11
|
+
(TentStatus.Collections.followers.find (follower) => follower.get('entity') == @get('entity'))
|
|
12
|
+
|
|
13
|
+
name: =>
|
|
14
|
+
@entity()?.name() || @get('entity')
|
|
15
|
+
|
|
16
|
+
hasName: =>
|
|
17
|
+
!!(@entity()?.hasName())
|
|
18
|
+
|
|
19
|
+
avatar: =>
|
|
20
|
+
@entity()?.avatar()
|
|
21
|
+
|
|
22
|
+
validate: (attrs) =>
|
|
23
|
+
errors = []
|
|
24
|
+
|
|
25
|
+
if attrs.text and attrs.text.match /^[\s\r]*$/
|
|
26
|
+
errors.push { text: 'Status must not be empty' }
|
|
27
|
+
|
|
28
|
+
if attrs.text and attrs.text.length > 140
|
|
29
|
+
errors.push { text: 'Status must be no more than 140 characters' }
|
|
30
|
+
|
|
31
|
+
return errors if errors.length
|
|
32
|
+
null
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class Profile extends Backbone.Model
|
|
2
|
+
model: 'profile'
|
|
3
|
+
url: => "#{TentStatus.api_root}/profile"
|
|
4
|
+
|
|
5
|
+
core_profile: =>
|
|
6
|
+
@get('https://tent.io/types/info/core/v0.1.0')
|
|
7
|
+
|
|
8
|
+
basic_profile: =>
|
|
9
|
+
@get('https://tent.io/types/info/basic/v0.1.0')
|
|
10
|
+
|
|
11
|
+
entity: =>
|
|
12
|
+
@core_profile()?['entity']
|
|
13
|
+
|
|
14
|
+
bio: =>
|
|
15
|
+
@basic_profile()?['bio']
|
|
16
|
+
|
|
17
|
+
name: =>
|
|
18
|
+
@basic_profile()?['name'] || TentStatus.Helpers.formatUrl(@core_profile()?['entity'] || '')
|
|
19
|
+
|
|
20
|
+
hasName: =>
|
|
21
|
+
!!(@basic_profile()?['name'])
|
|
22
|
+
|
|
23
|
+
avatar: =>
|
|
24
|
+
@basic_profile()?['avatar_url']
|
|
25
|
+
|
|
26
|
+
TentStatus.Models.profile = new Profile
|
|
27
|
+
|
|
28
|
+
class TentStatus.Models.Profile extends Profile
|
|
29
|
+
url: => "#{TentStatus.api_root}/#{@get('follow_type')}/#{@get('id')}/profile"
|
|
30
|
+
|
|
@@ -0,0 +1,1106 @@
|
|
|
1
|
+
// moment.js
|
|
2
|
+
// version : 1.7.0
|
|
3
|
+
// author : Tim Wood
|
|
4
|
+
// license : MIT
|
|
5
|
+
// momentjs.com
|
|
6
|
+
|
|
7
|
+
(function (Date, undefined) {
|
|
8
|
+
|
|
9
|
+
/************************************
|
|
10
|
+
Constants
|
|
11
|
+
************************************/
|
|
12
|
+
|
|
13
|
+
var moment,
|
|
14
|
+
VERSION = "1.7.0",
|
|
15
|
+
round = Math.round, i,
|
|
16
|
+
// internal storage for language config files
|
|
17
|
+
languages = {},
|
|
18
|
+
currentLanguage = 'en',
|
|
19
|
+
|
|
20
|
+
// check for nodeJS
|
|
21
|
+
hasModule = (typeof module !== 'undefined' && module.exports),
|
|
22
|
+
|
|
23
|
+
// Parameters to check for on the lang config. This list of properties
|
|
24
|
+
// will be inherited from English if not provided in a language
|
|
25
|
+
// definition. monthsParse is also a lang config property, but it
|
|
26
|
+
// cannot be inherited and as such cannot be enumerated here.
|
|
27
|
+
langConfigProperties = 'months|monthsShort|weekdays|weekdaysShort|weekdaysMin|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
|
|
28
|
+
|
|
29
|
+
// ASP.NET json date format regex
|
|
30
|
+
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
|
|
31
|
+
|
|
32
|
+
// format tokens
|
|
33
|
+
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?)/g,
|
|
34
|
+
localFormattingTokens = /(LT|LL?L?L?)/g,
|
|
35
|
+
formattingRemoveEscapes = /(^\[)|(\\)|\]$/g,
|
|
36
|
+
|
|
37
|
+
// parsing tokens
|
|
38
|
+
parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
|
|
39
|
+
|
|
40
|
+
// parsing token regexes
|
|
41
|
+
parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
|
|
42
|
+
parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
|
|
43
|
+
parseTokenThreeDigits = /\d{3}/, // 000 - 999
|
|
44
|
+
parseTokenFourDigits = /\d{1,4}/, // 0 - 9999
|
|
45
|
+
parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers
|
|
46
|
+
parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
|
|
47
|
+
parseTokenT = /T/i, // T (ISO seperator)
|
|
48
|
+
|
|
49
|
+
// preliminary iso regex
|
|
50
|
+
// 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
|
|
51
|
+
isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
|
|
52
|
+
isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
|
|
53
|
+
|
|
54
|
+
// iso time formats and regexes
|
|
55
|
+
isoTimes = [
|
|
56
|
+
['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/],
|
|
57
|
+
['HH:mm:ss', /T\d\d:\d\d:\d\d/],
|
|
58
|
+
['HH:mm', /T\d\d:\d\d/],
|
|
59
|
+
['HH', /T\d\d/]
|
|
60
|
+
],
|
|
61
|
+
|
|
62
|
+
// timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
|
|
63
|
+
parseTimezoneChunker = /([\+\-]|\d\d)/gi,
|
|
64
|
+
|
|
65
|
+
// getter and setter names
|
|
66
|
+
proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
|
|
67
|
+
unitMillisecondFactors = {
|
|
68
|
+
'Milliseconds' : 1,
|
|
69
|
+
'Seconds' : 1e3,
|
|
70
|
+
'Minutes' : 6e4,
|
|
71
|
+
'Hours' : 36e5,
|
|
72
|
+
'Days' : 864e5,
|
|
73
|
+
'Months' : 2592e6,
|
|
74
|
+
'Years' : 31536e6
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// format function strings
|
|
78
|
+
formatFunctions = {},
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* moment.fn.format uses new Function() to create an inlined formatting function.
|
|
82
|
+
* Results are a 3x speed boost
|
|
83
|
+
* http://jsperf.com/momentjs-cached-format-functions
|
|
84
|
+
*
|
|
85
|
+
* These strings are appended into a function using replaceFormatTokens and makeFormatFunction
|
|
86
|
+
*/
|
|
87
|
+
formatFunctionStrings = {
|
|
88
|
+
// a = placeholder
|
|
89
|
+
// b = placeholder
|
|
90
|
+
// t = the current moment being formatted
|
|
91
|
+
// v = getValueAtKey function
|
|
92
|
+
// o = language.ordinal function
|
|
93
|
+
// p = leftZeroFill function
|
|
94
|
+
// m = language.meridiem value or function
|
|
95
|
+
M : '(a=t.month()+1)',
|
|
96
|
+
MMM : 'v("monthsShort",t.month())',
|
|
97
|
+
MMMM : 'v("months",t.month())',
|
|
98
|
+
D : '(a=t.date())',
|
|
99
|
+
DDD : '(a=new Date(t.year(),t.month(),t.date()),b=new Date(t.year(),0,1),a=~~(((a-b)/864e5)+1.5))',
|
|
100
|
+
d : '(a=t.day())',
|
|
101
|
+
dd : 'v("weekdaysMin",t.day())',
|
|
102
|
+
ddd : 'v("weekdaysShort",t.day())',
|
|
103
|
+
dddd : 'v("weekdays",t.day())',
|
|
104
|
+
w : '(a=new Date(t.year(),t.month(),t.date()-t.day()+5),b=new Date(a.getFullYear(),0,4),a=~~((a-b)/864e5/7+1.5))',
|
|
105
|
+
YY : 'p(t.year()%100,2)',
|
|
106
|
+
YYYY : 'p(t.year(),4)',
|
|
107
|
+
a : 'm(t.hours(),t.minutes(),!0)',
|
|
108
|
+
A : 'm(t.hours(),t.minutes(),!1)',
|
|
109
|
+
H : 't.hours()',
|
|
110
|
+
h : 't.hours()%12||12',
|
|
111
|
+
m : 't.minutes()',
|
|
112
|
+
s : 't.seconds()',
|
|
113
|
+
S : '~~(t.milliseconds()/100)',
|
|
114
|
+
SS : 'p(~~(t.milliseconds()/10),2)',
|
|
115
|
+
SSS : 'p(t.milliseconds(),3)',
|
|
116
|
+
Z : '((a=-t.zone())<0?((a=-a),"-"):"+")+p(~~(a/60),2)+":"+p(~~a%60,2)',
|
|
117
|
+
ZZ : '((a=-t.zone())<0?((a=-a),"-"):"+")+p(~~(10*a/6),4)'
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
ordinalizeTokens = 'DDD w M D d'.split(' '),
|
|
121
|
+
paddedTokens = 'M D H h m s w'.split(' ');
|
|
122
|
+
|
|
123
|
+
while (ordinalizeTokens.length) {
|
|
124
|
+
i = ordinalizeTokens.pop();
|
|
125
|
+
formatFunctionStrings[i + 'o'] = formatFunctionStrings[i] + '+o(a)';
|
|
126
|
+
}
|
|
127
|
+
while (paddedTokens.length) {
|
|
128
|
+
i = paddedTokens.pop();
|
|
129
|
+
formatFunctionStrings[i + i] = 'p(' + formatFunctionStrings[i] + ',2)';
|
|
130
|
+
}
|
|
131
|
+
formatFunctionStrings.DDDD = 'p(' + formatFunctionStrings.DDD + ',3)';
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
/************************************
|
|
135
|
+
Constructors
|
|
136
|
+
************************************/
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
// Moment prototype object
|
|
140
|
+
function Moment(date, isUTC, lang) {
|
|
141
|
+
this._d = date;
|
|
142
|
+
this._isUTC = !!isUTC;
|
|
143
|
+
this._a = date._a || null;
|
|
144
|
+
date._a = null;
|
|
145
|
+
this._lang = lang || false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Duration Constructor
|
|
149
|
+
function Duration(duration) {
|
|
150
|
+
var data = this._data = {},
|
|
151
|
+
years = duration.years || duration.y || 0,
|
|
152
|
+
months = duration.months || duration.M || 0,
|
|
153
|
+
weeks = duration.weeks || duration.w || 0,
|
|
154
|
+
days = duration.days || duration.d || 0,
|
|
155
|
+
hours = duration.hours || duration.h || 0,
|
|
156
|
+
minutes = duration.minutes || duration.m || 0,
|
|
157
|
+
seconds = duration.seconds || duration.s || 0,
|
|
158
|
+
milliseconds = duration.milliseconds || duration.ms || 0;
|
|
159
|
+
|
|
160
|
+
// representation for dateAddRemove
|
|
161
|
+
this._milliseconds = milliseconds +
|
|
162
|
+
seconds * 1e3 + // 1000
|
|
163
|
+
minutes * 6e4 + // 1000 * 60
|
|
164
|
+
hours * 36e5; // 1000 * 60 * 60
|
|
165
|
+
// Because of dateAddRemove treats 24 hours as different from a
|
|
166
|
+
// day when working around DST, we need to store them separately
|
|
167
|
+
this._days = days +
|
|
168
|
+
weeks * 7;
|
|
169
|
+
// It is impossible translate months into days without knowing
|
|
170
|
+
// which months you are are talking about, so we have to store
|
|
171
|
+
// it separately.
|
|
172
|
+
this._months = months +
|
|
173
|
+
years * 12;
|
|
174
|
+
|
|
175
|
+
// The following code bubbles up values, see the tests for
|
|
176
|
+
// examples of what that means.
|
|
177
|
+
data.milliseconds = milliseconds % 1000;
|
|
178
|
+
seconds += absRound(milliseconds / 1000);
|
|
179
|
+
|
|
180
|
+
data.seconds = seconds % 60;
|
|
181
|
+
minutes += absRound(seconds / 60);
|
|
182
|
+
|
|
183
|
+
data.minutes = minutes % 60;
|
|
184
|
+
hours += absRound(minutes / 60);
|
|
185
|
+
|
|
186
|
+
data.hours = hours % 24;
|
|
187
|
+
days += absRound(hours / 24);
|
|
188
|
+
|
|
189
|
+
days += weeks * 7;
|
|
190
|
+
data.days = days % 30;
|
|
191
|
+
|
|
192
|
+
months += absRound(days / 30);
|
|
193
|
+
|
|
194
|
+
data.months = months % 12;
|
|
195
|
+
years += absRound(months / 12);
|
|
196
|
+
|
|
197
|
+
data.years = years;
|
|
198
|
+
|
|
199
|
+
this._lang = false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/************************************
|
|
204
|
+
Helpers
|
|
205
|
+
************************************/
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
function absRound(number) {
|
|
209
|
+
if (number < 0) {
|
|
210
|
+
return Math.ceil(number);
|
|
211
|
+
} else {
|
|
212
|
+
return Math.floor(number);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// left zero fill a number
|
|
217
|
+
// see http://jsperf.com/left-zero-filling for performance comparison
|
|
218
|
+
function leftZeroFill(number, targetLength) {
|
|
219
|
+
var output = number + '';
|
|
220
|
+
while (output.length < targetLength) {
|
|
221
|
+
output = '0' + output;
|
|
222
|
+
}
|
|
223
|
+
return output;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// helper function for _.addTime and _.subtractTime
|
|
227
|
+
function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
|
|
228
|
+
var ms = duration._milliseconds,
|
|
229
|
+
d = duration._days,
|
|
230
|
+
M = duration._months,
|
|
231
|
+
currentDate;
|
|
232
|
+
|
|
233
|
+
if (ms) {
|
|
234
|
+
mom._d.setTime(+mom + ms * isAdding);
|
|
235
|
+
}
|
|
236
|
+
if (d) {
|
|
237
|
+
mom.date(mom.date() + d * isAdding);
|
|
238
|
+
}
|
|
239
|
+
if (M) {
|
|
240
|
+
currentDate = mom.date();
|
|
241
|
+
mom.date(1)
|
|
242
|
+
.month(mom.month() + M * isAdding)
|
|
243
|
+
.date(Math.min(currentDate, mom.daysInMonth()));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// check if is an array
|
|
248
|
+
function isArray(input) {
|
|
249
|
+
return Object.prototype.toString.call(input) === '[object Array]';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// compare two arrays, return the number of differences
|
|
253
|
+
function compareArrays(array1, array2) {
|
|
254
|
+
var len = Math.min(array1.length, array2.length),
|
|
255
|
+
lengthDiff = Math.abs(array1.length - array2.length),
|
|
256
|
+
diffs = 0,
|
|
257
|
+
i;
|
|
258
|
+
for (i = 0; i < len; i++) {
|
|
259
|
+
if (~~array1[i] !== ~~array2[i]) {
|
|
260
|
+
diffs++;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return diffs + lengthDiff;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// convert an array to a date.
|
|
267
|
+
// the array should mirror the parameters below
|
|
268
|
+
// note: all values past the year are optional and will default to the lowest possible value.
|
|
269
|
+
// [year, month, day , hour, minute, second, millisecond]
|
|
270
|
+
function dateFromArray(input, asUTC) {
|
|
271
|
+
var i, date;
|
|
272
|
+
for (i = 1; i < 7; i++) {
|
|
273
|
+
input[i] = (input[i] == null) ? (i === 2 ? 1 : 0) : input[i];
|
|
274
|
+
}
|
|
275
|
+
// we store whether we used utc or not in the input array
|
|
276
|
+
input[7] = asUTC;
|
|
277
|
+
date = new Date(0);
|
|
278
|
+
if (asUTC) {
|
|
279
|
+
date.setUTCFullYear(input[0], input[1], input[2]);
|
|
280
|
+
date.setUTCHours(input[3], input[4], input[5], input[6]);
|
|
281
|
+
} else {
|
|
282
|
+
date.setFullYear(input[0], input[1], input[2]);
|
|
283
|
+
date.setHours(input[3], input[4], input[5], input[6]);
|
|
284
|
+
}
|
|
285
|
+
date._a = input;
|
|
286
|
+
return date;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Loads a language definition into the `languages` cache. The function
|
|
290
|
+
// takes a key and optionally values. If not in the browser and no values
|
|
291
|
+
// are provided, it will load the language file module. As a convenience,
|
|
292
|
+
// this function also returns the language values.
|
|
293
|
+
function loadLang(key, values) {
|
|
294
|
+
var i, m,
|
|
295
|
+
parse = [];
|
|
296
|
+
|
|
297
|
+
if (!values && hasModule) {
|
|
298
|
+
values = require('./lang/' + key);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (i = 0; i < langConfigProperties.length; i++) {
|
|
302
|
+
// If a language definition does not provide a value, inherit
|
|
303
|
+
// from English
|
|
304
|
+
values[langConfigProperties[i]] = values[langConfigProperties[i]] ||
|
|
305
|
+
languages.en[langConfigProperties[i]];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for (i = 0; i < 12; i++) {
|
|
309
|
+
m = moment([2000, i]);
|
|
310
|
+
parse[i] = new RegExp('^' + (values.months[i] || values.months(m, '')) +
|
|
311
|
+
'|^' + (values.monthsShort[i] || values.monthsShort(m, '')).replace('.', ''), 'i');
|
|
312
|
+
}
|
|
313
|
+
values.monthsParse = values.monthsParse || parse;
|
|
314
|
+
|
|
315
|
+
languages[key] = values;
|
|
316
|
+
|
|
317
|
+
return values;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Determines which language definition to use and returns it.
|
|
321
|
+
//
|
|
322
|
+
// With no parameters, it will return the global language. If you
|
|
323
|
+
// pass in a language key, such as 'en', it will return the
|
|
324
|
+
// definition for 'en', so long as 'en' has already been loaded using
|
|
325
|
+
// moment.lang. If you pass in a moment or duration instance, it
|
|
326
|
+
// will decide the language based on that, or default to the global
|
|
327
|
+
// language.
|
|
328
|
+
function getLangDefinition(m) {
|
|
329
|
+
var langKey = (typeof m === 'string') && m ||
|
|
330
|
+
m && m._lang ||
|
|
331
|
+
null;
|
|
332
|
+
|
|
333
|
+
return langKey ? (languages[langKey] || loadLang(langKey)) : moment;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
/************************************
|
|
338
|
+
Formatting
|
|
339
|
+
************************************/
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
// helper for building inline formatting functions
|
|
343
|
+
function replaceFormatTokens(token) {
|
|
344
|
+
return formatFunctionStrings[token] ?
|
|
345
|
+
("'+(" + formatFunctionStrings[token] + ")+'") :
|
|
346
|
+
token.replace(formattingRemoveEscapes, "").replace(/\\?'/g, "\\'");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// helper for recursing long date formatting tokens
|
|
350
|
+
function replaceLongDateFormatTokens(input) {
|
|
351
|
+
return getLangDefinition().longDateFormat[input] || input;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function makeFormatFunction(format) {
|
|
355
|
+
var output = "var a,b;return '" +
|
|
356
|
+
format.replace(formattingTokens, replaceFormatTokens) + "';",
|
|
357
|
+
Fn = Function; // get around jshint
|
|
358
|
+
// t = the current moment being formatted
|
|
359
|
+
// v = getValueAtKey function
|
|
360
|
+
// o = language.ordinal function
|
|
361
|
+
// p = leftZeroFill function
|
|
362
|
+
// m = language.meridiem value or function
|
|
363
|
+
return new Fn('t', 'v', 'o', 'p', 'm', output);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function makeOrGetFormatFunction(format) {
|
|
367
|
+
if (!formatFunctions[format]) {
|
|
368
|
+
formatFunctions[format] = makeFormatFunction(format);
|
|
369
|
+
}
|
|
370
|
+
return formatFunctions[format];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// format date using native date object
|
|
374
|
+
function formatMoment(m, format) {
|
|
375
|
+
var lang = getLangDefinition(m);
|
|
376
|
+
|
|
377
|
+
function getValueFromArray(key, index) {
|
|
378
|
+
return lang[key].call ? lang[key](m, format) : lang[key][index];
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
while (localFormattingTokens.test(format)) {
|
|
382
|
+
format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!formatFunctions[format]) {
|
|
386
|
+
formatFunctions[format] = makeFormatFunction(format);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return formatFunctions[format](m, getValueFromArray, lang.ordinal, leftZeroFill, lang.meridiem);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
/************************************
|
|
394
|
+
Parsing
|
|
395
|
+
************************************/
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
// get the regex to find the next token
|
|
399
|
+
function getParseRegexForToken(token) {
|
|
400
|
+
switch (token) {
|
|
401
|
+
case 'DDDD':
|
|
402
|
+
return parseTokenThreeDigits;
|
|
403
|
+
case 'YYYY':
|
|
404
|
+
return parseTokenFourDigits;
|
|
405
|
+
case 'S':
|
|
406
|
+
case 'SS':
|
|
407
|
+
case 'SSS':
|
|
408
|
+
case 'DDD':
|
|
409
|
+
return parseTokenOneToThreeDigits;
|
|
410
|
+
case 'MMM':
|
|
411
|
+
case 'MMMM':
|
|
412
|
+
case 'dd':
|
|
413
|
+
case 'ddd':
|
|
414
|
+
case 'dddd':
|
|
415
|
+
case 'a':
|
|
416
|
+
case 'A':
|
|
417
|
+
return parseTokenWord;
|
|
418
|
+
case 'Z':
|
|
419
|
+
case 'ZZ':
|
|
420
|
+
return parseTokenTimezone;
|
|
421
|
+
case 'T':
|
|
422
|
+
return parseTokenT;
|
|
423
|
+
case 'MM':
|
|
424
|
+
case 'DD':
|
|
425
|
+
case 'YY':
|
|
426
|
+
case 'HH':
|
|
427
|
+
case 'hh':
|
|
428
|
+
case 'mm':
|
|
429
|
+
case 'ss':
|
|
430
|
+
case 'M':
|
|
431
|
+
case 'D':
|
|
432
|
+
case 'd':
|
|
433
|
+
case 'H':
|
|
434
|
+
case 'h':
|
|
435
|
+
case 'm':
|
|
436
|
+
case 's':
|
|
437
|
+
return parseTokenOneOrTwoDigits;
|
|
438
|
+
default :
|
|
439
|
+
return new RegExp(token.replace('\\', ''));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// function to convert string input to date
|
|
444
|
+
function addTimeToArrayFromToken(token, input, datePartArray, config) {
|
|
445
|
+
var a;
|
|
446
|
+
//console.log('addTime', format, input);
|
|
447
|
+
switch (token) {
|
|
448
|
+
// MONTH
|
|
449
|
+
case 'M' : // fall through to MM
|
|
450
|
+
case 'MM' :
|
|
451
|
+
datePartArray[1] = (input == null) ? 0 : ~~input - 1;
|
|
452
|
+
break;
|
|
453
|
+
case 'MMM' : // fall through to MMMM
|
|
454
|
+
case 'MMMM' :
|
|
455
|
+
for (a = 0; a < 12; a++) {
|
|
456
|
+
if (getLangDefinition().monthsParse[a].test(input)) {
|
|
457
|
+
datePartArray[1] = a;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
// DAY OF MONTH
|
|
463
|
+
case 'D' : // fall through to DDDD
|
|
464
|
+
case 'DD' : // fall through to DDDD
|
|
465
|
+
case 'DDD' : // fall through to DDDD
|
|
466
|
+
case 'DDDD' :
|
|
467
|
+
if (input != null) {
|
|
468
|
+
datePartArray[2] = ~~input;
|
|
469
|
+
}
|
|
470
|
+
break;
|
|
471
|
+
// YEAR
|
|
472
|
+
case 'YY' :
|
|
473
|
+
input = ~~input;
|
|
474
|
+
datePartArray[0] = input + (input > 70 ? 1900 : 2000);
|
|
475
|
+
break;
|
|
476
|
+
case 'YYYY' :
|
|
477
|
+
datePartArray[0] = ~~Math.abs(input);
|
|
478
|
+
break;
|
|
479
|
+
// AM / PM
|
|
480
|
+
case 'a' : // fall through to A
|
|
481
|
+
case 'A' :
|
|
482
|
+
config.isPm = ((input + '').toLowerCase() === 'pm');
|
|
483
|
+
break;
|
|
484
|
+
// 24 HOUR
|
|
485
|
+
case 'H' : // fall through to hh
|
|
486
|
+
case 'HH' : // fall through to hh
|
|
487
|
+
case 'h' : // fall through to hh
|
|
488
|
+
case 'hh' :
|
|
489
|
+
datePartArray[3] = ~~input;
|
|
490
|
+
break;
|
|
491
|
+
// MINUTE
|
|
492
|
+
case 'm' : // fall through to mm
|
|
493
|
+
case 'mm' :
|
|
494
|
+
datePartArray[4] = ~~input;
|
|
495
|
+
break;
|
|
496
|
+
// SECOND
|
|
497
|
+
case 's' : // fall through to ss
|
|
498
|
+
case 'ss' :
|
|
499
|
+
datePartArray[5] = ~~input;
|
|
500
|
+
break;
|
|
501
|
+
// MILLISECOND
|
|
502
|
+
case 'S' :
|
|
503
|
+
case 'SS' :
|
|
504
|
+
case 'SSS' :
|
|
505
|
+
datePartArray[6] = ~~ (('0.' + input) * 1000);
|
|
506
|
+
break;
|
|
507
|
+
// TIMEZONE
|
|
508
|
+
case 'Z' : // fall through to ZZ
|
|
509
|
+
case 'ZZ' :
|
|
510
|
+
config.isUTC = true;
|
|
511
|
+
a = (input + '').match(parseTimezoneChunker);
|
|
512
|
+
if (a && a[1]) {
|
|
513
|
+
config.tzh = ~~a[1];
|
|
514
|
+
}
|
|
515
|
+
if (a && a[2]) {
|
|
516
|
+
config.tzm = ~~a[2];
|
|
517
|
+
}
|
|
518
|
+
// reverse offsets
|
|
519
|
+
if (a && a[0] === '+') {
|
|
520
|
+
config.tzh = -config.tzh;
|
|
521
|
+
config.tzm = -config.tzm;
|
|
522
|
+
}
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// date from string and format string
|
|
528
|
+
function makeDateFromStringAndFormat(string, format) {
|
|
529
|
+
var datePartArray = [0, 0, 1, 0, 0, 0, 0],
|
|
530
|
+
config = {
|
|
531
|
+
tzh : 0, // timezone hour offset
|
|
532
|
+
tzm : 0 // timezone minute offset
|
|
533
|
+
},
|
|
534
|
+
tokens = format.match(formattingTokens),
|
|
535
|
+
i, parsedInput;
|
|
536
|
+
|
|
537
|
+
for (i = 0; i < tokens.length; i++) {
|
|
538
|
+
parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
|
|
539
|
+
string = string.replace(getParseRegexForToken(tokens[i]), '');
|
|
540
|
+
addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config);
|
|
541
|
+
}
|
|
542
|
+
// handle am pm
|
|
543
|
+
if (config.isPm && datePartArray[3] < 12) {
|
|
544
|
+
datePartArray[3] += 12;
|
|
545
|
+
}
|
|
546
|
+
// if is 12 am, change hours to 0
|
|
547
|
+
if (config.isPm === false && datePartArray[3] === 12) {
|
|
548
|
+
datePartArray[3] = 0;
|
|
549
|
+
}
|
|
550
|
+
// handle timezone
|
|
551
|
+
datePartArray[3] += config.tzh;
|
|
552
|
+
datePartArray[4] += config.tzm;
|
|
553
|
+
// return
|
|
554
|
+
return dateFromArray(datePartArray, config.isUTC);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// date from string and array of format strings
|
|
558
|
+
function makeDateFromStringAndArray(string, formats) {
|
|
559
|
+
var output,
|
|
560
|
+
inputParts = string.match(parseMultipleFormatChunker) || [],
|
|
561
|
+
formattedInputParts,
|
|
562
|
+
scoreToBeat = 99,
|
|
563
|
+
i,
|
|
564
|
+
currentDate,
|
|
565
|
+
currentScore;
|
|
566
|
+
for (i = 0; i < formats.length; i++) {
|
|
567
|
+
currentDate = makeDateFromStringAndFormat(string, formats[i]);
|
|
568
|
+
formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || [];
|
|
569
|
+
currentScore = compareArrays(inputParts, formattedInputParts);
|
|
570
|
+
if (currentScore < scoreToBeat) {
|
|
571
|
+
scoreToBeat = currentScore;
|
|
572
|
+
output = currentDate;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return output;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// date from iso format
|
|
579
|
+
function makeDateFromString(string) {
|
|
580
|
+
var format = 'YYYY-MM-DDT',
|
|
581
|
+
i;
|
|
582
|
+
if (isoRegex.exec(string)) {
|
|
583
|
+
for (i = 0; i < 4; i++) {
|
|
584
|
+
if (isoTimes[i][1].exec(string)) {
|
|
585
|
+
format += isoTimes[i][0];
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return parseTokenTimezone.exec(string) ?
|
|
590
|
+
makeDateFromStringAndFormat(string, format + ' Z') :
|
|
591
|
+
makeDateFromStringAndFormat(string, format);
|
|
592
|
+
}
|
|
593
|
+
return new Date(string);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
/************************************
|
|
598
|
+
Relative Time
|
|
599
|
+
************************************/
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
|
|
603
|
+
function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) {
|
|
604
|
+
var rt = lang.relativeTime[string];
|
|
605
|
+
return (typeof rt === 'function') ?
|
|
606
|
+
rt(number || 1, !!withoutSuffix, string, isFuture) :
|
|
607
|
+
rt.replace(/%d/i, number || 1);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function relativeTime(milliseconds, withoutSuffix, lang) {
|
|
611
|
+
var seconds = round(Math.abs(milliseconds) / 1000),
|
|
612
|
+
minutes = round(seconds / 60),
|
|
613
|
+
hours = round(minutes / 60),
|
|
614
|
+
days = round(hours / 24),
|
|
615
|
+
years = round(days / 365),
|
|
616
|
+
args = seconds < 45 && ['s', seconds] ||
|
|
617
|
+
minutes === 1 && ['m'] ||
|
|
618
|
+
minutes < 45 && ['mm', minutes] ||
|
|
619
|
+
hours === 1 && ['h'] ||
|
|
620
|
+
hours < 22 && ['hh', hours] ||
|
|
621
|
+
days === 1 && ['d'] ||
|
|
622
|
+
days <= 25 && ['dd', days] ||
|
|
623
|
+
days <= 45 && ['M'] ||
|
|
624
|
+
days < 345 && ['MM', round(days / 30)] ||
|
|
625
|
+
years === 1 && ['y'] || ['yy', years];
|
|
626
|
+
args[2] = withoutSuffix;
|
|
627
|
+
args[3] = milliseconds > 0;
|
|
628
|
+
args[4] = lang;
|
|
629
|
+
return substituteTimeAgo.apply({}, args);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
/************************************
|
|
634
|
+
Top Level Functions
|
|
635
|
+
************************************/
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
moment = function (input, format) {
|
|
639
|
+
if (input === null || input === '') {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
var date,
|
|
643
|
+
matched;
|
|
644
|
+
// parse Moment object
|
|
645
|
+
if (moment.isMoment(input)) {
|
|
646
|
+
return new Moment(new Date(+input._d), input._isUTC, input._lang);
|
|
647
|
+
// parse string and format
|
|
648
|
+
} else if (format) {
|
|
649
|
+
if (isArray(format)) {
|
|
650
|
+
date = makeDateFromStringAndArray(input, format);
|
|
651
|
+
} else {
|
|
652
|
+
date = makeDateFromStringAndFormat(input, format);
|
|
653
|
+
}
|
|
654
|
+
// evaluate it as a JSON-encoded date
|
|
655
|
+
} else {
|
|
656
|
+
matched = aspNetJsonRegex.exec(input);
|
|
657
|
+
date = input === undefined ? new Date() :
|
|
658
|
+
matched ? new Date(+matched[1]) :
|
|
659
|
+
input instanceof Date ? input :
|
|
660
|
+
isArray(input) ? dateFromArray(input) :
|
|
661
|
+
typeof input === 'string' ? makeDateFromString(input) :
|
|
662
|
+
new Date(input);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return new Moment(date);
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
// creating with utc
|
|
669
|
+
moment.utc = function (input, format) {
|
|
670
|
+
if (isArray(input)) {
|
|
671
|
+
return new Moment(dateFromArray(input, true), true);
|
|
672
|
+
}
|
|
673
|
+
// if we don't have a timezone, we need to add one to trigger parsing into utc
|
|
674
|
+
if (typeof input === 'string' && !parseTokenTimezone.exec(input)) {
|
|
675
|
+
input += ' +0000';
|
|
676
|
+
if (format) {
|
|
677
|
+
format += ' Z';
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return moment(input, format).utc();
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// creating with unix timestamp (in seconds)
|
|
684
|
+
moment.unix = function (input) {
|
|
685
|
+
return moment(input * 1000);
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// duration
|
|
689
|
+
moment.duration = function (input, key) {
|
|
690
|
+
var isDuration = moment.isDuration(input),
|
|
691
|
+
isNumber = (typeof input === 'number'),
|
|
692
|
+
duration = (isDuration ? input._data : (isNumber ? {} : input)),
|
|
693
|
+
ret;
|
|
694
|
+
|
|
695
|
+
if (isNumber) {
|
|
696
|
+
if (key) {
|
|
697
|
+
duration[key] = input;
|
|
698
|
+
} else {
|
|
699
|
+
duration.milliseconds = input;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
ret = new Duration(duration);
|
|
704
|
+
|
|
705
|
+
if (isDuration) {
|
|
706
|
+
ret._lang = input._lang;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return ret;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// humanizeDuration
|
|
713
|
+
// This method is deprecated in favor of the new Duration object. Please
|
|
714
|
+
// see the moment.duration method.
|
|
715
|
+
moment.humanizeDuration = function (num, type, withSuffix) {
|
|
716
|
+
return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix);
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// version number
|
|
720
|
+
moment.version = VERSION;
|
|
721
|
+
|
|
722
|
+
// default format
|
|
723
|
+
moment.defaultFormat = isoFormat;
|
|
724
|
+
|
|
725
|
+
// This function will load languages and then set the global language. If
|
|
726
|
+
// no arguments are passed in, it will simply return the current global
|
|
727
|
+
// language key.
|
|
728
|
+
moment.lang = function (key, values) {
|
|
729
|
+
var i;
|
|
730
|
+
|
|
731
|
+
if (!key) {
|
|
732
|
+
return currentLanguage;
|
|
733
|
+
}
|
|
734
|
+
if (values || !languages[key]) {
|
|
735
|
+
loadLang(key, values);
|
|
736
|
+
}
|
|
737
|
+
if (languages[key]) {
|
|
738
|
+
// deprecated, to get the language definition variables, use the
|
|
739
|
+
// moment.fn.lang method or the getLangDefinition function.
|
|
740
|
+
for (i = 0; i < langConfigProperties.length; i++) {
|
|
741
|
+
moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]];
|
|
742
|
+
}
|
|
743
|
+
moment.monthsParse = languages[key].monthsParse;
|
|
744
|
+
currentLanguage = key;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
// returns language data
|
|
749
|
+
moment.langData = getLangDefinition;
|
|
750
|
+
|
|
751
|
+
// compare moment object
|
|
752
|
+
moment.isMoment = function (obj) {
|
|
753
|
+
return obj instanceof Moment;
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// for typechecking Duration objects
|
|
757
|
+
moment.isDuration = function (obj) {
|
|
758
|
+
return obj instanceof Duration;
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
// Set default language, other languages will inherit from English.
|
|
762
|
+
moment.lang('en', {
|
|
763
|
+
months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
|
|
764
|
+
monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
|
|
765
|
+
weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
|
|
766
|
+
weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
|
|
767
|
+
weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
|
|
768
|
+
longDateFormat : {
|
|
769
|
+
LT : "h:mm A",
|
|
770
|
+
L : "MM/DD/YYYY",
|
|
771
|
+
LL : "MMMM D YYYY",
|
|
772
|
+
LLL : "MMMM D YYYY LT",
|
|
773
|
+
LLLL : "dddd, MMMM D YYYY LT"
|
|
774
|
+
},
|
|
775
|
+
meridiem : function (hours, minutes, isLower) {
|
|
776
|
+
if (hours > 11) {
|
|
777
|
+
return isLower ? 'pm' : 'PM';
|
|
778
|
+
} else {
|
|
779
|
+
return isLower ? 'am' : 'AM';
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
calendar : {
|
|
783
|
+
sameDay : '[Today at] LT',
|
|
784
|
+
nextDay : '[Tomorrow at] LT',
|
|
785
|
+
nextWeek : 'dddd [at] LT',
|
|
786
|
+
lastDay : '[Yesterday at] LT',
|
|
787
|
+
lastWeek : '[last] dddd [at] LT',
|
|
788
|
+
sameElse : 'L'
|
|
789
|
+
},
|
|
790
|
+
relativeTime : {
|
|
791
|
+
future : "in %s",
|
|
792
|
+
past : "%s ago",
|
|
793
|
+
s : "a few seconds",
|
|
794
|
+
m : "a minute",
|
|
795
|
+
mm : "%d minutes",
|
|
796
|
+
h : "an hour",
|
|
797
|
+
hh : "%d hours",
|
|
798
|
+
d : "a day",
|
|
799
|
+
dd : "%d days",
|
|
800
|
+
M : "a month",
|
|
801
|
+
MM : "%d months",
|
|
802
|
+
y : "a year",
|
|
803
|
+
yy : "%d years"
|
|
804
|
+
},
|
|
805
|
+
ordinal : function (number) {
|
|
806
|
+
var b = number % 10;
|
|
807
|
+
return (~~ (number % 100 / 10) === 1) ? 'th' :
|
|
808
|
+
(b === 1) ? 'st' :
|
|
809
|
+
(b === 2) ? 'nd' :
|
|
810
|
+
(b === 3) ? 'rd' : 'th';
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
/************************************
|
|
816
|
+
Moment Prototype
|
|
817
|
+
************************************/
|
|
818
|
+
|
|
819
|
+
|
|
820
|
+
moment.fn = Moment.prototype = {
|
|
821
|
+
|
|
822
|
+
clone : function () {
|
|
823
|
+
return moment(this);
|
|
824
|
+
},
|
|
825
|
+
|
|
826
|
+
valueOf : function () {
|
|
827
|
+
return +this._d;
|
|
828
|
+
},
|
|
829
|
+
|
|
830
|
+
unix : function () {
|
|
831
|
+
return Math.floor(+this._d / 1000);
|
|
832
|
+
},
|
|
833
|
+
|
|
834
|
+
toString : function () {
|
|
835
|
+
return this._d.toString();
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
toDate : function () {
|
|
839
|
+
return this._d;
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
toArray : function () {
|
|
843
|
+
var m = this;
|
|
844
|
+
return [
|
|
845
|
+
m.year(),
|
|
846
|
+
m.month(),
|
|
847
|
+
m.date(),
|
|
848
|
+
m.hours(),
|
|
849
|
+
m.minutes(),
|
|
850
|
+
m.seconds(),
|
|
851
|
+
m.milliseconds(),
|
|
852
|
+
!!this._isUTC
|
|
853
|
+
];
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
isValid : function () {
|
|
857
|
+
if (this._a) {
|
|
858
|
+
return !compareArrays(this._a, (this._a[7] ? moment.utc(this) : this).toArray());
|
|
859
|
+
}
|
|
860
|
+
return !isNaN(this._d.getTime());
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
utc : function () {
|
|
864
|
+
this._isUTC = true;
|
|
865
|
+
return this;
|
|
866
|
+
},
|
|
867
|
+
|
|
868
|
+
local : function () {
|
|
869
|
+
this._isUTC = false;
|
|
870
|
+
return this;
|
|
871
|
+
},
|
|
872
|
+
|
|
873
|
+
format : function (inputString) {
|
|
874
|
+
return formatMoment(this, inputString ? inputString : moment.defaultFormat);
|
|
875
|
+
},
|
|
876
|
+
|
|
877
|
+
add : function (input, val) {
|
|
878
|
+
var dur = val ? moment.duration(+val, input) : moment.duration(input);
|
|
879
|
+
addOrSubtractDurationFromMoment(this, dur, 1);
|
|
880
|
+
return this;
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
subtract : function (input, val) {
|
|
884
|
+
var dur = val ? moment.duration(+val, input) : moment.duration(input);
|
|
885
|
+
addOrSubtractDurationFromMoment(this, dur, -1);
|
|
886
|
+
return this;
|
|
887
|
+
},
|
|
888
|
+
|
|
889
|
+
diff : function (input, val, asFloat) {
|
|
890
|
+
var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
|
|
891
|
+
zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
|
|
892
|
+
diff = this._d - inputMoment._d - zoneDiff,
|
|
893
|
+
year = this.year() - inputMoment.year(),
|
|
894
|
+
month = this.month() - inputMoment.month(),
|
|
895
|
+
date = this.date() - inputMoment.date(),
|
|
896
|
+
output;
|
|
897
|
+
if (val === 'months') {
|
|
898
|
+
output = year * 12 + month + date / 30;
|
|
899
|
+
} else if (val === 'years') {
|
|
900
|
+
output = year + (month + date / 30) / 12;
|
|
901
|
+
} else {
|
|
902
|
+
output = val === 'seconds' ? diff / 1e3 : // 1000
|
|
903
|
+
val === 'minutes' ? diff / 6e4 : // 1000 * 60
|
|
904
|
+
val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
|
|
905
|
+
val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
|
|
906
|
+
val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
|
|
907
|
+
diff;
|
|
908
|
+
}
|
|
909
|
+
return asFloat ? output : round(output);
|
|
910
|
+
},
|
|
911
|
+
|
|
912
|
+
from : function (time, withoutSuffix) {
|
|
913
|
+
return moment.duration(this.diff(time)).lang(this._lang).humanize(!withoutSuffix);
|
|
914
|
+
},
|
|
915
|
+
|
|
916
|
+
fromNow : function (withoutSuffix) {
|
|
917
|
+
return this.from(moment(), withoutSuffix);
|
|
918
|
+
},
|
|
919
|
+
|
|
920
|
+
calendar : function () {
|
|
921
|
+
var diff = this.diff(moment().sod(), 'days', true),
|
|
922
|
+
calendar = this.lang().calendar,
|
|
923
|
+
allElse = calendar.sameElse,
|
|
924
|
+
format = diff < -6 ? allElse :
|
|
925
|
+
diff < -1 ? calendar.lastWeek :
|
|
926
|
+
diff < 0 ? calendar.lastDay :
|
|
927
|
+
diff < 1 ? calendar.sameDay :
|
|
928
|
+
diff < 2 ? calendar.nextDay :
|
|
929
|
+
diff < 7 ? calendar.nextWeek : allElse;
|
|
930
|
+
return this.format(typeof format === 'function' ? format.apply(this) : format);
|
|
931
|
+
},
|
|
932
|
+
|
|
933
|
+
isLeapYear : function () {
|
|
934
|
+
var year = this.year();
|
|
935
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
936
|
+
},
|
|
937
|
+
|
|
938
|
+
isDST : function () {
|
|
939
|
+
return (this.zone() < moment([this.year()]).zone() ||
|
|
940
|
+
this.zone() < moment([this.year(), 5]).zone());
|
|
941
|
+
},
|
|
942
|
+
|
|
943
|
+
day : function (input) {
|
|
944
|
+
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
|
|
945
|
+
return input == null ? day :
|
|
946
|
+
this.add({ d : input - day });
|
|
947
|
+
},
|
|
948
|
+
|
|
949
|
+
startOf: function (val) {
|
|
950
|
+
// the following switch intentionally omits break keywords
|
|
951
|
+
// to utilize falling through the cases.
|
|
952
|
+
switch (val.replace(/s$/, '')) {
|
|
953
|
+
case 'year':
|
|
954
|
+
this.month(0);
|
|
955
|
+
/* falls through */
|
|
956
|
+
case 'month':
|
|
957
|
+
this.date(1);
|
|
958
|
+
/* falls through */
|
|
959
|
+
case 'day':
|
|
960
|
+
this.hours(0);
|
|
961
|
+
/* falls through */
|
|
962
|
+
case 'hour':
|
|
963
|
+
this.minutes(0);
|
|
964
|
+
/* falls through */
|
|
965
|
+
case 'minute':
|
|
966
|
+
this.seconds(0);
|
|
967
|
+
/* falls through */
|
|
968
|
+
case 'second':
|
|
969
|
+
this.milliseconds(0);
|
|
970
|
+
/* falls through */
|
|
971
|
+
}
|
|
972
|
+
return this;
|
|
973
|
+
},
|
|
974
|
+
|
|
975
|
+
endOf: function (val) {
|
|
976
|
+
return this.startOf(val).add(val.replace(/s?$/, 's'), 1).subtract('ms', 1);
|
|
977
|
+
},
|
|
978
|
+
|
|
979
|
+
sod: function () {
|
|
980
|
+
return this.clone().startOf('day');
|
|
981
|
+
},
|
|
982
|
+
|
|
983
|
+
eod: function () {
|
|
984
|
+
// end of day = start of day plus 1 day, minus 1 millisecond
|
|
985
|
+
return this.clone().endOf('day');
|
|
986
|
+
},
|
|
987
|
+
|
|
988
|
+
zone : function () {
|
|
989
|
+
return this._isUTC ? 0 : this._d.getTimezoneOffset();
|
|
990
|
+
},
|
|
991
|
+
|
|
992
|
+
daysInMonth : function () {
|
|
993
|
+
return moment.utc([this.year(), this.month() + 1, 0]).date();
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
// If passed a language key, it will set the language for this
|
|
997
|
+
// instance. Otherwise, it will return the language configuration
|
|
998
|
+
// variables for this instance.
|
|
999
|
+
lang : function (lang) {
|
|
1000
|
+
if (lang === undefined) {
|
|
1001
|
+
return getLangDefinition(this);
|
|
1002
|
+
} else {
|
|
1003
|
+
this._lang = lang;
|
|
1004
|
+
return this;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
// helper for adding shortcuts
|
|
1010
|
+
function makeGetterAndSetter(name, key) {
|
|
1011
|
+
moment.fn[name] = function (input) {
|
|
1012
|
+
var utc = this._isUTC ? 'UTC' : '';
|
|
1013
|
+
if (input != null) {
|
|
1014
|
+
this._d['set' + utc + key](input);
|
|
1015
|
+
return this;
|
|
1016
|
+
} else {
|
|
1017
|
+
return this._d['get' + utc + key]();
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
|
|
1023
|
+
for (i = 0; i < proxyGettersAndSetters.length; i ++) {
|
|
1024
|
+
makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
|
|
1028
|
+
makeGetterAndSetter('year', 'FullYear');
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
/************************************
|
|
1032
|
+
Duration Prototype
|
|
1033
|
+
************************************/
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
moment.duration.fn = Duration.prototype = {
|
|
1037
|
+
weeks : function () {
|
|
1038
|
+
return absRound(this.days() / 7);
|
|
1039
|
+
},
|
|
1040
|
+
|
|
1041
|
+
valueOf : function () {
|
|
1042
|
+
return this._milliseconds +
|
|
1043
|
+
this._days * 864e5 +
|
|
1044
|
+
this._months * 2592e6;
|
|
1045
|
+
},
|
|
1046
|
+
|
|
1047
|
+
humanize : function (withSuffix) {
|
|
1048
|
+
var difference = +this,
|
|
1049
|
+
rel = this.lang().relativeTime,
|
|
1050
|
+
output = relativeTime(difference, !withSuffix, this.lang());
|
|
1051
|
+
|
|
1052
|
+
if (withSuffix) {
|
|
1053
|
+
output = (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return output;
|
|
1057
|
+
},
|
|
1058
|
+
|
|
1059
|
+
lang : moment.fn.lang
|
|
1060
|
+
};
|
|
1061
|
+
|
|
1062
|
+
function makeDurationGetter(name) {
|
|
1063
|
+
moment.duration.fn[name] = function () {
|
|
1064
|
+
return this._data[name];
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function makeDurationAsGetter(name, factor) {
|
|
1069
|
+
moment.duration.fn['as' + name] = function () {
|
|
1070
|
+
return +this / factor;
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
for (i in unitMillisecondFactors) {
|
|
1075
|
+
if (unitMillisecondFactors.hasOwnProperty(i)) {
|
|
1076
|
+
makeDurationAsGetter(i, unitMillisecondFactors[i]);
|
|
1077
|
+
makeDurationGetter(i.toLowerCase());
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
makeDurationAsGetter('Weeks', 6048e5);
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
/************************************
|
|
1085
|
+
Exposing Moment
|
|
1086
|
+
************************************/
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
// CommonJS module is defined
|
|
1090
|
+
if (hasModule) {
|
|
1091
|
+
module.exports = moment;
|
|
1092
|
+
}
|
|
1093
|
+
/*global ender:false */
|
|
1094
|
+
if (typeof ender === 'undefined') {
|
|
1095
|
+
// here, `this` means `window` in the browser, or `global` on the server
|
|
1096
|
+
// add `moment` as a global object via a string identifier,
|
|
1097
|
+
// for Closure Compiler "advanced" mode
|
|
1098
|
+
this['moment'] = moment;
|
|
1099
|
+
}
|
|
1100
|
+
/*global define:false */
|
|
1101
|
+
if (typeof define === "function" && define.amd) {
|
|
1102
|
+
define("moment", [], function () {
|
|
1103
|
+
return moment;
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
}).call(this, Date);
|