├── .gitignore
├── bower.json
├── package.json
├── LICENSE
├── README.md
├── specs
├── dateData.js
└── humanize.spec.js
└── humanize.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | node_modules
4 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "humanize",
3 | "version": "0.0.9",
4 | "repository": {
5 | "type": "git",
6 | "url": "git://github.com/taijinlee/humanize"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "humanize",
3 | "description": "Javascript string formatter for human readability",
4 | "homepage": "https://github.com/taijinlee/humanize",
5 | "keywords": [
6 | "util",
7 | "client",
8 | "browser"
9 | ],
10 | "author": {
11 | "name": "Tai-Jin Lee",
12 | "email": "taijin@gmail.com"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/taijinlee/humanize.git"
17 | },
18 | "main": "humanize.js",
19 | "version": "0.0.9-discord",
20 | "dependencies": {},
21 | "devDependencies": {
22 | "mocha": "1.0.3",
23 | "should": "0.6.3",
24 | "jshint": "0.7.1"
25 | },
26 | "optionalDependencies": {},
27 | "engines": {
28 | "node": "*"
29 | },
30 | "scripts": {
31 | "test": "./node_modules/jshint/bin/hint humanize.js; find specs -type f -a -name *.spec.js -exec ./node_modules/mocha/bin/mocha --globals requirejsVars -R list --require should {} \\;"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Tai-Jin Lee
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # humanize #
2 |
3 | Javascript data formatter for human readability.
4 |
5 | Idea, name, and initial code blatently stolen from [milanvrekic/JS-humanize](http://github.com/milanvrekic/JS-humanize)
6 |
7 | Can be loaded via AMD or in node directly.
8 |
9 | ## Installation ##
10 |
11 | npm install humanize
12 |
13 | ## Usage: ##
14 | ```javascript
15 | var humanize = require('humanize');
16 | humanize.date('Y-m-d'); // 'yyyy-mm-dd'
17 | humanize.filesize(1234567890); // '1.15 Gb'
18 | ```
19 |
20 | ## Functions available: ##
21 |
22 | ####humanize.noConflict()####
23 | Give control of the "humanize" variable back to its previous owner. Returns a reference to the humanize object.
24 |
25 | ####humanize.time()####
26 | Retrieves the current time in seconds
27 |
28 | ####humanize.date(format [, timestamp / JS Date Object = new Date()])####
29 | This is a port of [php.js date](http://phpjs.org/functions/date:380) and behaves exactly like [PHP's date](http://php.net/manual/en/function.date.php)
30 |
31 | ####humanize.numberFormat(number [, decimals = 2, decPoint = '.', thousandsSep = ','])####
32 | Format a number to have decimal significant decimal places, using decPoint as the decimal separator, and thousandsSep as thousands separater
33 |
34 | ####humanize.naturalDay(timestamp [, format = 'Y-m-d'])####
35 | Returns 'today', 'tomorrow' or 'yesterday', as appropriate, otherwise format the date using the passed format with humanize.date()
36 |
37 | ####humanize.relativeTime(timestamp)####
38 | Returns a relative time to the current time, seconds as the most granular up to years to the least granular.
39 |
40 | ####humanize.ordinal(integer)####
41 | Converts a number into its [ordinal representation](http://en.wikipedia.org/wiki/Ordinal_number_\(linguistics\)).
42 |
43 | ####humanize.filesize(filesize [, kilo = 1024, decimals = 2, decPoint = '.', thousandsSep = ',']) ####
44 | Converts a byte count to a human readable value using kilo as the basis, and numberFormat formatting
45 |
46 | ####humanize.linebreaks(string)####
47 | Converts a string's newlines into properly formatted html ie. one new line -> br, two new lines -> p, entire thing wrapped in p
48 |
49 | ####humanize.nl2br(string)####
50 | Converts a string's newlines into br's
51 |
52 | ####humanize.truncatechars(string, length)####
53 | Truncates a string to length-1 and appends '…'. If string is shorter than length, then no-op
54 |
55 | ####humanize.truncatewords(string, numWords)####
56 | Truncates a string to only include the first numWords words and appends '…'. If string has fewer words than numWords, then no-op
57 |
--------------------------------------------------------------------------------
/specs/dateData.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 |
3 | // all in America/Los_Angeles timezone
4 |
5 | var timestamps = {
6 | // 04/16/1986 18:23:47
7 | '514088627': {
8 | d: '16',
9 | D: 'Wed',
10 | j: '16',
11 | l: 'Wednesday',
12 | N: '3',
13 | S: 'th',
14 | w: '3',
15 | z: '105',
16 | W: '16',
17 | F: 'April',
18 | m: '04',
19 | M: 'Apr',
20 | n: '4',
21 | t: '30',
22 | L: '0',
23 | o: '1986',
24 | Y: '1986',
25 | y: '86',
26 | a: 'pm',
27 | A: 'PM',
28 | B: '141',
29 | g: '6',
30 | G: '18',
31 | h: '06',
32 | H: '18',
33 | i: '23',
34 | s: '47',
35 | u: '000000',
36 | // I: '0',
37 | O: '-0800',
38 | P: '-08:00',
39 | Z: '-28800',
40 | c: '1986-04-16T18:23:47-08:00',
41 | r: 'Wed, 16 Apr 1986 18:23:47 -0800',
42 | U: '514088627'
43 | },
44 |
45 | // 12/25/1999 01:09:03
46 | '946112943': {
47 | d: '25',
48 | D: 'Sat',
49 | j: '25',
50 | l: 'Saturday',
51 | N: '6',
52 | S: 'th',
53 | w: '6',
54 | z: '358',
55 | W: '51',
56 | F: 'December',
57 | m: '12',
58 | M: 'Dec',
59 | n: '12',
60 | t: '31',
61 | L: '0',
62 | o: '1999',
63 | Y: '1999',
64 | y: '99',
65 | a: 'am',
66 | A: 'AM',
67 | B: '422',
68 | g: '1',
69 | G: '1',
70 | h: '01',
71 | H: '01',
72 | i: '09',
73 | s: '03',
74 | u: '000000',
75 | // I: '0',
76 | O: '-0800',
77 | P: '-08:00',
78 | Z: '-28800',
79 | c: '1999-12-25T01:09:03-08:00',
80 | r: 'Sat, 25 Dec 1999 01:09:03 -0800',
81 | U: '946112943'
82 | },
83 |
84 | // 07/10/2011 12:23:58
85 | '1310325838': {
86 | d: '10',
87 | D: 'Sun',
88 | j: '10',
89 | l: 'Sunday',
90 | N: '7',
91 | S: 'th',
92 | w: '0',
93 | z: '190',
94 | W: '27',
95 | F: 'July',
96 | m: '07',
97 | M: 'Jul',
98 | n: '7',
99 | t: '31',
100 | L: '0',
101 | o: '2011',
102 | Y: '2011',
103 | y: '11',
104 | a: 'pm',
105 | A: 'PM',
106 | B: '849',
107 | g: '12',
108 | G: '12',
109 | h: '12',
110 | H: '12',
111 | i: '23',
112 | s: '58',
113 | u: '000000',
114 | // I: '1',
115 | O: '-0700',
116 | P: '-07:00',
117 | Z: '-25200',
118 | c: '2011-07-10T12:23:58-07:00',
119 | r: 'Sun, 10 Jul 2011 12:23:58 -0700',
120 | U: '1310325838'
121 | },
122 |
123 | // 10/05/2015 00:00:00
124 | '1444028400': {
125 | d: '05',
126 | D: 'Mon',
127 | j: '5',
128 | l: 'Monday',
129 | N: '1',
130 | S: 'th',
131 | w: '1',
132 | z: '277',
133 | W: '41',
134 | F: 'October',
135 | m: '10',
136 | M: 'Oct',
137 | n: '10',
138 | t: '31',
139 | L: '0',
140 | o: '2015',
141 | Y: '2015',
142 | y: '15',
143 | a: 'am',
144 | A: 'AM',
145 | B: '333',
146 | g: '12',
147 | G: '0',
148 | h: '12',
149 | H: '00',
150 | i: '00',
151 | s: '00',
152 | u: '000000',
153 | // I: '1',
154 | O: '-0700',
155 | P: '-07:00',
156 | Z: '-25200',
157 | c: '2015-10-05T00:00:00-07:00',
158 | r: 'Mon, 05 Oct 2015 00:00:00 -0700',
159 | U: '1444028400'
160 | },
161 |
162 | // 03/31/2000 13:25:14
163 | '954537914': {
164 | d: '31',
165 | D: 'Fri',
166 | j: '31',
167 | l: 'Friday',
168 | N: '5',
169 | S: 'st',
170 | w: '5',
171 | z: '90',
172 | W: '13',
173 | F: 'March',
174 | m: '03',
175 | M: 'Mar',
176 | n: '3',
177 | t: '31',
178 | L: '1',
179 | o: '2000',
180 | Y: '2000',
181 | y: '00',
182 | a: 'pm',
183 | A: 'PM',
184 | B: '934',
185 | g: '1',
186 | G: '13',
187 | h: '01',
188 | H: '13',
189 | i: '25',
190 | s: '14',
191 | u: '000000',
192 | // I: '0',
193 | O: '-0800',
194 | P: '-08:00',
195 | Z: '-28800',
196 | c: '2000-03-31T13:25:14-08:00',
197 | r: 'Fri, 31 Mar 2000 13:25:14 -0800',
198 | U: '954537914'
199 | },
200 |
201 | // 01/02/3456 12:34:56
202 | '46893760496': {
203 | d: '02',
204 | D: 'Wed',
205 | j: '2',
206 | l: 'Wednesday',
207 | N: '3',
208 | S: 'nd',
209 | w: '3',
210 | z: '1',
211 | W: '01',
212 | F: 'January',
213 | m: '01',
214 | M: 'Jan',
215 | n: '1',
216 | t: '31',
217 | L: '1',
218 | o: '3456',
219 | Y: '3456',
220 | y: '56',
221 | a: 'pm',
222 | A: 'PM',
223 | B: '899',
224 | g: '12',
225 | G: '12',
226 | h: '12',
227 | H: '12',
228 | i: '34',
229 | s: '56',
230 | u: '000000',
231 | // I: '0',
232 | O: '-0800',
233 | P: '-08:00',
234 | Z: '-28800',
235 | c: '3456-01-02T12:34:56-08:00',
236 | r: 'Wed, 02 Jan 3456 12:34:56 -0800',
237 | U: '46893760496'
238 | },
239 |
240 | // 01/11/1840 11:56:14
241 | '-4101509026': {
242 | d: '11',
243 | D: 'Sat',
244 | j: '11',
245 | l: 'Saturday',
246 | N: '6',
247 | S: 'th',
248 | w: '6',
249 | z: '10',
250 | W: '02',
251 | F: 'January',
252 | m: '01',
253 | M: 'Jan',
254 | n: '1',
255 | t: '31',
256 | L: '1',
257 | o: '1840',
258 | Y: '1840',
259 | y: '40',
260 | a: 'am',
261 | A: 'AM',
262 | B: '873',
263 | g: '11',
264 | G: '11',
265 | h: '11',
266 | H: '11',
267 | i: '56',
268 | s: '14',
269 | u: '000000',
270 | // I: '0',
271 | O: '-0800',
272 | P: '-08:00',
273 | Z: '-28800',
274 | c: '1840-01-11T11:56:14-08:00',
275 | r: 'Sat, 11 Jan 1840 11:56:14 -0800',
276 | U: '-4101509026'
277 | }
278 | };
279 |
280 | return {
281 | timestamps: timestamps
282 | };
283 |
284 | };
285 |
--------------------------------------------------------------------------------
/humanize.js:
--------------------------------------------------------------------------------
1 |
2 | (function() {
3 |
4 | // Baseline setup
5 | // --------------
6 |
7 | // Establish the root object, `window` in the browser, or `global` on the server.
8 | var root = this;
9 |
10 | // Save the previous value of the `humanize` variable.
11 | var previousHumanize = root.humanize;
12 |
13 | var humanize = {};
14 |
15 | if (typeof exports !== 'undefined') {
16 | if (typeof module !== 'undefined' && module.exports) {
17 | exports = module.exports = humanize;
18 | }
19 | exports.humanize = humanize;
20 | } else {
21 | if (typeof define === 'function' && define.amd) {
22 | define('humanize', function() {
23 | return humanize;
24 | });
25 | }
26 | root.humanize = humanize;
27 | }
28 |
29 | humanize.noConflict = function() {
30 | root.humanize = previousHumanize;
31 | return this;
32 | };
33 |
34 | humanize.pad = function(str, count, padChar, type) {
35 | str += '';
36 | if (!padChar) {
37 | padChar = ' ';
38 | } else if (padChar.length > 1) {
39 | padChar = padChar.charAt(0);
40 | }
41 | type = (type === undefined) ? 'left' : 'right';
42 |
43 | if (type === 'right') {
44 | while (str.length < count) {
45 | str = str + padChar;
46 | }
47 | } else {
48 | // default to left
49 | while (str.length < count) {
50 | str = padChar + str;
51 | }
52 | }
53 |
54 | return str;
55 | };
56 |
57 | // gets current unix time
58 | humanize.time = function() {
59 | return new Date().getTime() / 1000;
60 | };
61 |
62 | /**
63 | * PHP-inspired date
64 | */
65 |
66 | /* jan feb mar apr may jun jul aug sep oct nov dec */
67 | var dayTableCommon = [ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ];
68 | var dayTableLeap = [ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ];
69 | // var mtable_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
70 | // static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
71 |
72 |
73 | humanize.date = function(format, timestamp) {
74 | var jsdate = ((timestamp === undefined) ? new Date() : // Not provided
75 | (timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
76 | new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
77 | );
78 |
79 | var formatChr = /\\?([a-z])/gi;
80 | var formatChrCb = function (t, s) {
81 | return f[t] ? f[t]() : s;
82 | };
83 |
84 | var shortDayTxt = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
85 | var monthTxt = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
86 |
87 | var f = {
88 | /* Day */
89 | // Day of month w/leading 0; 01..31
90 | d: function () { return humanize.pad(f.j(), 2, '0'); },
91 |
92 | // Shorthand day name; Mon..Sun
93 | D: function () { return f.l().slice(0, 3); },
94 |
95 | // Day of month; 1..31
96 | j: function () { return jsdate.getDate(); },
97 |
98 | // Full day name; Monday..Sunday
99 | l: function () { return shortDayTxt[f.w()]; },
100 |
101 | // ISO-8601 day of week; 1[Mon]..7[Sun]
102 | N: function () { return f.w() || 7; },
103 |
104 | // Ordinal suffix for day of month; st, nd, rd, th
105 | S: function () {
106 | var j = f.j();
107 | return j > 4 && j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th';
108 | },
109 |
110 | // Day of week; 0[Sun]..6[Sat]
111 | w: function () { return jsdate.getDay(); },
112 |
113 | // Day of year; 0..365
114 | z: function () {
115 | return (f.L() ? dayTableLeap[f.n()] : dayTableCommon[f.n()]) + f.j() - 1;
116 | },
117 |
118 | /* Week */
119 | // ISO-8601 week number
120 | W: function () {
121 | // days between midweek of this week and jan 4
122 | // (f.z() - f.N() + 1 + 3.5) - 3
123 | var midWeekDaysFromJan4 = f.z() - f.N() + 1.5;
124 | // 1 + number of weeks + rounded week
125 | return humanize.pad(1 + Math.floor(Math.abs(midWeekDaysFromJan4) / 7) + (midWeekDaysFromJan4 % 7 > 3.5 ? 1 : 0), 2, '0');
126 | },
127 |
128 | /* Month */
129 | // Full month name; January..December
130 | F: function () { return monthTxt[jsdate.getMonth()]; },
131 |
132 | // Month w/leading 0; 01..12
133 | m: function () { return humanize.pad(f.n(), 2, '0'); },
134 |
135 | // Shorthand month name; Jan..Dec
136 | M: function () { return f.F().slice(0, 3); },
137 |
138 | // Month; 1..12
139 | n: function () { return jsdate.getMonth() + 1; },
140 |
141 | // Days in month; 28..31
142 | t: function () { return (new Date(f.Y(), f.n(), 0)).getDate(); },
143 |
144 | /* Year */
145 | // Is leap year?; 0 or 1
146 | L: function () { return new Date(f.Y(), 1, 29).getMonth() === 1 ? 1 : 0; },
147 |
148 | // ISO-8601 year
149 | o: function () {
150 | var n = f.n();
151 | var W = f.W();
152 | return f.Y() + (n === 12 && W < 9 ? -1 : n === 1 && W > 9);
153 | },
154 |
155 | // Full year; e.g. 1980..2010
156 | Y: function () { return jsdate.getFullYear(); },
157 |
158 | // Last two digits of year; 00..99
159 | y: function () { return (String(f.Y())).slice(-2); },
160 |
161 | /* Time */
162 | // am or pm
163 | a: function () { return jsdate.getHours() > 11 ? 'pm' : 'am'; },
164 |
165 | // AM or PM
166 | A: function () { return f.a().toUpperCase(); },
167 |
168 | // Swatch Internet time; 000..999
169 | B: function () {
170 | var unixTime = jsdate.getTime() / 1000;
171 | var secondsPassedToday = unixTime % 86400 + 3600; // since it's based off of UTC+1
172 | if (secondsPassedToday < 0) { secondsPassedToday += 86400; }
173 | var beats = ((secondsPassedToday) / 86.4) % 1000;
174 | if (unixTime < 0) {
175 | return Math.ceil(beats);
176 | }
177 | return Math.floor(beats);
178 | },
179 |
180 | // 12-Hours; 1..12
181 | g: function () { return f.G() % 12 || 12; },
182 |
183 | // 24-Hours; 0..23
184 | G: function () { return jsdate.getHours(); },
185 |
186 | // 12-Hours w/leading 0; 01..12
187 | h: function () { return humanize.pad(f.g(), 2, '0'); },
188 |
189 | // 24-Hours w/leading 0; 00..23
190 | H: function () { return humanize.pad(f.G(), 2, '0'); },
191 |
192 | // Minutes w/leading 0; 00..59
193 | i: function () { return humanize.pad(jsdate.getMinutes(), 2, '0'); },
194 |
195 | // Seconds w/leading 0; 00..59
196 | s: function () { return humanize.pad(jsdate.getSeconds(), 2, '0'); },
197 |
198 | // Microseconds; 000000-999000
199 | u: function () { return humanize.pad(jsdate.getMilliseconds() * 1000, 6, '0'); },
200 |
201 | // Whether or not the date is in daylight savings time
202 | /*
203 | I: function () {
204 | // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
205 | // If they are not equal, then DST is observed.
206 | var Y = f.Y();
207 | return 0 + ((new Date(Y, 0) - Date.UTC(Y, 0)) !== (new Date(Y, 6) - Date.UTC(Y, 6)));
208 | },
209 | */
210 |
211 | // Difference to GMT in hour format; e.g. +0200
212 | O: function () {
213 | var tzo = jsdate.getTimezoneOffset();
214 | var tzoNum = Math.abs(tzo);
215 | return (tzo > 0 ? '-' : '+') + humanize.pad(Math.floor(tzoNum / 60) * 100 + tzoNum % 60, 4, '0');
216 | },
217 |
218 | // Difference to GMT w/colon; e.g. +02:00
219 | P: function () {
220 | var O = f.O();
221 | return (O.substr(0, 3) + ':' + O.substr(3, 2));
222 | },
223 |
224 | // Timezone offset in seconds (-43200..50400)
225 | Z: function () { return -jsdate.getTimezoneOffset() * 60; },
226 |
227 | // Full Date/Time, ISO-8601 date
228 | c: function () { return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); },
229 |
230 | // RFC 2822
231 | r: function () { return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); },
232 |
233 | // Seconds since UNIX epoch
234 | U: function () { return jsdate.getTime() / 1000 || 0; }
235 | };
236 |
237 | return format.replace(formatChr, formatChrCb);
238 | };
239 |
240 |
241 | /**
242 | * format number by adding thousands separaters and significant digits while rounding
243 | */
244 | humanize.numberFormat = function(number, decimals, decPoint, thousandsSep) {
245 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals);
246 | decPoint = (decPoint === undefined) ? '.' : decPoint;
247 | thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep;
248 |
249 | var sign = number < 0 ? '-' : '';
250 | number = Math.abs(+number || 0);
251 |
252 | var intPart = parseInt(number.toFixed(decimals), 10) + '';
253 | var j = intPart.length > 3 ? intPart.length % 3 : 0;
254 |
255 | return sign + (j ? intPart.substr(0, j) + thousandsSep : '') + intPart.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep) + (decimals ? decPoint + Math.abs(number - intPart).toFixed(decimals).slice(2) : '');
256 | };
257 |
258 |
259 | /**
260 | * For dates that are the current day or within one day, return 'today', 'tomorrow' or 'yesterday', as appropriate.
261 | * Otherwise, format the date using the passed in format string.
262 | *
263 | * Examples (when 'today' is 17 Feb 2007):
264 | * 16 Feb 2007 becomes yesterday.
265 | * 17 Feb 2007 becomes today.
266 | * 18 Feb 2007 becomes tomorrow.
267 | * Any other day is formatted according to given argument or the DATE_FORMAT setting if no argument is given.
268 | */
269 | humanize.naturalDay = function(timestamp, format) {
270 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
271 | format = (format === undefined) ? 'Y-m-d' : format;
272 |
273 | var oneDay = 86400;
274 | var d = new Date();
275 | var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000;
276 |
277 | if (timestamp < today && timestamp >= today - oneDay) {
278 | return 'yesterday';
279 | } else if (timestamp >= today && timestamp < today + oneDay) {
280 | return 'today';
281 | } else if (timestamp >= today + oneDay && timestamp < today + 2 * oneDay) {
282 | return 'tomorrow';
283 | }
284 |
285 | return humanize.date(format, timestamp);
286 | };
287 |
288 | /**
289 | * returns a string representing how many seconds, minutes or hours ago it was or will be in the future
290 | * Will always return a relative time, most granular of seconds to least granular of years. See unit tests for more details
291 | */
292 | humanize.relativeTime = function(timestamp) {
293 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
294 |
295 | var currTime = humanize.time();
296 | var timeDiff = currTime - timestamp;
297 |
298 | // within 2 seconds
299 | if (timeDiff < 2 && timeDiff > -2) {
300 | return (timeDiff >= 0 ? 'just ' : '') + 'now';
301 | }
302 |
303 | // within a minute
304 | if (timeDiff < 60 && timeDiff > -60) {
305 | return (timeDiff >= 0 ? Math.floor(timeDiff) + ' seconds ago' : 'in ' + Math.floor(-timeDiff) + ' seconds');
306 | }
307 |
308 | // within 2 minutes
309 | if (timeDiff < 120 && timeDiff > -120) {
310 | return (timeDiff >= 0 ? 'about a minute ago' : 'in about a minute');
311 | }
312 |
313 | // within an hour
314 | if (timeDiff < 3600 && timeDiff > -3600) {
315 | return (timeDiff >= 0 ? Math.floor(timeDiff / 60) + ' minutes ago' : 'in ' + Math.floor(-timeDiff / 60) + ' minutes');
316 | }
317 |
318 | // within 2 hours
319 | if (timeDiff < 7200 && timeDiff > -7200) {
320 | return (timeDiff >= 0 ? 'about an hour ago' : 'in about an hour');
321 | }
322 |
323 | // within 24 hours
324 | if (timeDiff < 86400 && timeDiff > -86400) {
325 | return (timeDiff >= 0 ? Math.floor(timeDiff / 3600) + ' hours ago' : 'in ' + Math.floor(-timeDiff / 3600) + ' hours');
326 | }
327 |
328 | // within 2 days
329 | var days2 = 2 * 86400;
330 | if (timeDiff < days2 && timeDiff > -days2) {
331 | return (timeDiff >= 0 ? '1 day ago' : 'in 1 day');
332 | }
333 |
334 | // within 29 days
335 | var days29 = 29 * 86400;
336 | if (timeDiff < days29 && timeDiff > -days29) {
337 | return (timeDiff >= 0 ? Math.floor(timeDiff / 86400) + ' days ago' : 'in ' + Math.floor(-timeDiff / 86400) + ' days');
338 | }
339 |
340 | // within 60 days
341 | var days60 = 60 * 86400;
342 | if (timeDiff < days60 && timeDiff > -days60) {
343 | return (timeDiff >= 0 ? 'about a month ago' : 'in about a month');
344 | }
345 |
346 | var currTimeYears = parseInt(humanize.date('Y', currTime), 10);
347 | var timestampYears = parseInt(humanize.date('Y', timestamp), 10);
348 | var currTimeMonths = currTimeYears * 12 + parseInt(humanize.date('n', currTime), 10);
349 | var timestampMonths = timestampYears * 12 + parseInt(humanize.date('n', timestamp), 10);
350 |
351 | // within a year
352 | var monthDiff = currTimeMonths - timestampMonths;
353 | if (monthDiff < 12 && monthDiff > -12) {
354 | return (monthDiff >= 0 ? monthDiff + ' months ago' : 'in ' + (-monthDiff) + ' months');
355 | }
356 |
357 | var yearDiff = currTimeYears - timestampYears;
358 | if (yearDiff < 2 && yearDiff > -2) {
359 | return (yearDiff >= 0 ? 'a year ago' : 'in a year');
360 | }
361 |
362 | return (yearDiff >= 0 ? yearDiff + ' years ago' : 'in ' + (-yearDiff) + ' years');
363 | };
364 |
365 | /**
366 | * Converts an integer to its ordinal as a string.
367 | *
368 | * 1 becomes 1st
369 | * 2 becomes 2nd
370 | * 3 becomes 3rd etc
371 | */
372 | humanize.ordinal = function(number) {
373 | number = parseInt(number, 10);
374 | number = isNaN(number) ? 0 : number;
375 | var sign = number < 0 ? '-' : '';
376 | number = Math.abs(number);
377 | var tens = number % 100;
378 |
379 | return sign + number + (tens > 4 && tens < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[number % 10] || 'th');
380 | };
381 |
382 | /**
383 | * Formats the value like a 'human-readable' file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc).
384 | *
385 | * For example:
386 | * If value is 123456789, the output would be 117.7 MB.
387 | */
388 | humanize.filesize = function(filesize, kilo, decimals, decPoint, thousandsSep, suffixSep) {
389 | kilo = (kilo === undefined) ? 1024 : kilo;
390 | if (filesize <= 0) { return '0 bytes'; }
391 | if (filesize < kilo && decimals === undefined) { decimals = 0; }
392 | if (suffixSep === undefined) { suffixSep = ' '; }
393 | return humanize.intword(filesize, ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'], kilo, decimals, decPoint, thousandsSep, suffixSep);
394 | };
395 |
396 | /**
397 | * Formats the value like a 'human-readable' number (i.e. '13 K', '4.1 M', '102', etc).
398 | *
399 | * For example:
400 | * If value is 123456789, the output would be 117.7 M.
401 | */
402 | humanize.intword = function(number, units, kilo, decimals, decPoint, thousandsSep, suffixSep) {
403 | var humanized, unit;
404 |
405 | units = units || ['', 'K', 'M', 'B', 'T'],
406 | unit = units.length - 1,
407 | kilo = kilo || 1000,
408 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals),
409 | decPoint = decPoint || '.',
410 | thousandsSep = thousandsSep || ',',
411 | suffixSep = suffixSep || '';
412 |
413 | for (var i=0; i < units.length; i++) {
414 | if (number < Math.pow(kilo, i+1)) {
415 | unit = i;
416 | break;
417 | }
418 | }
419 | humanized = number / Math.pow(kilo, unit);
420 |
421 | var suffix = units[unit] ? suffixSep + units[unit] : '';
422 | return humanize.numberFormat(humanized, decimals, decPoint, thousandsSep) + suffix;
423 | };
424 |
425 | /**
426 | * Replaces line breaks in plain text with appropriate HTML
427 | * A single newline becomes an HTML line break (
) and a new line followed by a blank line becomes a paragraph break (
Joel
is a
slug
431 | */ 432 | humanize.linebreaks = function(str) { 433 | // remove beginning and ending newlines 434 | str = str.replace(/^([\n|\r]*)/, ''); 435 | str = str.replace(/([\n|\r]*)$/, ''); 436 | 437 | // normalize all to \n 438 | str = str.replace(/(\r\n|\n|\r)/g, "\n"); 439 | 440 | // any consecutive new lines more than 2 gets turned into p tags 441 | str = str.replace(/(\n{2,})/g, '');
442 |
443 | // any that are singletons get turned into br
444 | str = str.replace(/\n/g, '
');
445 | return '
' + str + '
'; 446 | }; 447 | 448 | /** 449 | * Converts all newlines in a piece of plain text to HTML line breaks (tags', function() { 359 | humanize.linebreaks('').should.equal('
'); 360 | }); 361 | 362 | it('should remove new lines at beginning and end', function() { 363 | humanize.linebreaks("Foo\n\nBar\n\n\n").should.equal('Foo
Bar
'); 364 | humanize.linebreaks("\n\r\n\rFoo\n\nBar").should.equal('Foo
Bar
'); 365 | }); 366 | 367 | it('should change all new lines intoFoo
Bar
Foo
Bar
Blah
tags', function() { 373 | humanize.linebreaks("Foo\n\nBar").should.equal('
Foo
Bar
'); 374 | humanize.linebreaks("Foo\n\n\nBar").should.equal('Foo
Bar
'); 375 | humanize.linebreaks("Foo\n\n\r\nBar").should.equal('Foo
Bar
'); 376 | humanize.linebreaks("Foo\n\n\r\n\rBar").should.equal('Foo
Bar
'); 377 | }); 378 | }); 379 | 380 | describe('#nl2br', function() { 381 | it('should change any type of new line into a