Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add timesince/until and urlize/urlizetrunc filters

  • Loading branch information...
commit 87c7718cf96da21e6c15486f1894f9aceca4b4ff 1 parent ece9831
Anders Hellerup Madsen authored
View
5 template/template.test.js
@@ -97,6 +97,11 @@ testcase('Filter Expression tests');
var expr = new FilterExpression("test|escape|upper");
assertEquals('<SCRIPT>', expr.resolve(context));
});
+ test('filterexpression should work with variable as arg', function () {
+ var context = new Context({test: 4, arg: 38 });
+ var expr = new FilterExpression("test|add:arg");
+ assertEquals(42, expr.resolve(context));
+ });
testcase('Context test');
setup( function () {
View
32 template/template_defaults.js
@@ -10,11 +10,7 @@ var utils = require('utils/utils');
iriencode
Not implemented (yet):
- timesince
- timeuntil
unordered_list
- urlize
- urlizetrunc
wordcount
wordwrap
yesno
@@ -246,6 +242,16 @@ var filters = exports.filters = {
// TODO: this filter may not be safe
return (value instanceof Date) ? utils.date.format_time(value, arg) : '';
},
+ timesince: function (value, arg) {
+ value = new Date(value), arg = new Date(arg);
+ if (isNaN(value) || isNaN(arg)) { return ''; }
+ return utils.date.timesince(value, arg);
+ },
+ timeuntil: function (value, arg) {
+ value = new Date(value), arg = new Date(arg);
+ if (isNaN(value) || isNaN(arg)) { return ''; }
+ return utils.date.timeuntil(value, arg);
+ },
truncatewords: function (value, arg) {
return String(value).split(/\s+/g).slice(0, arg).join(' ') + ' ...';
},
@@ -258,6 +264,22 @@ var filters = exports.filters = {
},
urlencode: function (value, arg) {
return escape(value);
+ },
+ urlize: function (value, arg, safety) {
+ if (!safety.is_safe && safety.must_escape) {
+ var out = utils.html.urlize(value + "", { escape: true });
+ safety.is_safe = true;
+ return out;
+ }
+ return utils.html.urlize(value + "");
+ },
+ urlizetrunc: function (value, arg, safety) {
+ if (!safety.is_safe && safety.must_escape) {
+ var out = utils.html.urlize(value + "", { escape: true, limit: arg });
+ safety.is_safe = true;
+ return out;
+ }
+ return utils.html.urlize(value + "", { limit: arg });
}
};
@@ -600,5 +622,3 @@ var callbacks = exports.callbacks = {
-
-
View
46 template/template_defaults.test.js
@@ -396,5 +396,51 @@ testcase('time');
assertEquals('18:12:14', filters.time(t, 'H:i:s'));
assertEquals('', filters.date('hest', 'H:i:s'));
});
+testcase('timesince');
+ test('should return time since', function () {
+ var blog_date = new Date("1 June 2006 00:00:00");
+ var comment_date = new Date("1 June 2006 08:00:00");
+ assertEquals('8 hours', filters.timesince(blog_date, comment_date));
+ });
+testcase('timeuntil');
+ test('should return time since', function () {
+ var today = new Date("1 June 2006");
+ var from_date = new Date("22 June 2006");
+ var conference_date = new Date("29 June 2006");
+ assertEquals('4 weeks', filters.timeuntil(conference_date, today));
+ assertEquals('1 week', filters.timeuntil(conference_date, from_date));
+ });
+testcase('urlize');
+ test('should urlize text', function () {
+ assertEquals(
+ 'Check out <a href="http://www.djangoproject.com">www.djangoproject.com</a>',
+ filters.urlize('Check out www.djangoproject.com', null, {})
+ );
+ });
+ test('should escape if required', function () {
+ var safety = { must_escape: true };
+ assertEquals('hest &amp; giraf', filters.urlize('hest & giraf', null, safety));
+ });
+ test('should mark output as safe if escaped', function () {
+ var safety = { must_escape: true };
+ filters.urlize('hest', null, safety);
+ assertEquals(true, safety.is_safe);
+ });
+testcase('urlizetrunc');
+ test('should urlize text and truncate', function () {
+ assertEquals(
+ 'Check out <a href="http://www.djangoproject.com">www.djangopr...</a>',
+ filters.urlizetrunc('Check out www.djangoproject.com', 15, {})
+ );
+ });
+ test('should escape if required', function () {
+ var safety = { must_escape: true };
+ assertEquals('hest &amp; giraf', filters.urlizetrunc('hest & giraf', 15, safety));
+ });
+ test('should mark output as safe if escaped', function () {
+ var safety = { must_escape: true };
+ filters.urlizetrunc('hest', 15, safety);
+ assertEquals(true, safety.is_safe);
+ });
run();
View
59 utils/date.js
@@ -9,7 +9,8 @@ var i18n = {
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
months_ap: ['Jan.', 'Feb.', 'March', 'April', 'May', 'June', 'July', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.'],
ordinal_suffix: { 1: 'st', 2: 'nd', 3: 'rd', 21: 'st', 22: 'nd', 23: 'rd', 31: 'st', 'default': 'th' },
- special_times: ['midnight', 'noon']
+ special_times: ['midnight', 'noon'],
+ timespan: ['year', 'years', 'month', 'months', 'week', 'weeks', 'day', 'days', 'hour', 'hours', 'minute', 'minutes']
}
};
@@ -149,6 +150,8 @@ function format_date(date, format) {
});
}
+exports.format_date = format_date;
+
function format_time(time, format) {
return format.replace(time_flags_re, function (s, escape_val) {
if (escape_val) { return escape_val.replace(/\\/g, ''); }
@@ -160,8 +163,60 @@ function format_time(time, format) {
});
}
-exports.format_date = format_date;
exports.format_time = format_time;
+/*************** TIMESPAN ********************************************/
+
+
+function timespan_to_str(timespan) {
+
+ function map_chunk(cnt, idx) {
+ return cnt + ' ' + cur_i18n.timespan[ cnt === 1 ? idx * 2 : idx * 2 + 1]
+ }
+
+ var chunks = [
+ // years
+ 1000 * 60 * 60 * 24 * 365,
+ // months
+ 1000 * 60 * 60 * 24 * 30,
+ // weeks
+ 1000 * 60 * 60 * 24 * 7,
+ // days
+ 1000 * 60 * 60 * 24,
+ // hours
+ 1000 * 60 * 60,
+ // minutes
+ 1000 * 60,
+ ];
+
+ chunks.forEach(function (x, idx) {
+ chunks[idx] = Math.floor(timespan / x);
+ timespan -= chunks[idx] * x;
+ });
+
+ for (var i = 0; i < chunks.length; i++) {
+ if (chunks[i]) {
+ return map_chunk(chunks[i], i) + (chunks[i+1] ? ', ' + map_chunk(chunks[i+1], i+1) : '');
+ }
+ }
+ return map_chunk(0, chunks.length - 1);
+}
+
+function timesince(date, now) {
+ if (!now) { now = new Date(); }
+ var timespan = now - date;
+ if (timespan < 0) { timespan = 0; }
+ return timespan_to_str(timespan);
+}
+exports.timesince = timesince;
+
+function timeuntil(date, now) {
+ if (!now) { now = new Date(); }
+ var timespan = date - now;
+ if (timespan < 0) { timespan = 0; }
+ return timespan_to_str(timespan);
+}
+exports.timeuntil = timeuntil;
+
View
26 utils/date.test.js
@@ -60,4 +60,30 @@ testcase('longer formats');
assertEquals('Wednesday 2nd of December 1981 06:31:45 PM', format_date(d, 'l jS \\o\\f F Y h:i:s A'));
});
+testcase('timesince');
+ test('correct results for known values', function () {
+ var now = new Date("Wed Dec 02 1981 18:31:45 GMT+0100 (CET)"); // Random time on Britney Spears birthday :-)
+
+ var date = new Date("Wed Dec 02 1981 15:15:45 GMT+0100 (CET)");
+ assertEquals('3 hours, 16 minutes', timesince(date, now));
+
+ date = new Date("Wed Nov 22 1981 15:15:45 GMT+0100 (CET)");
+ assertEquals('1 week, 3 days', timesince(date, now));
+
+ date = new Date("Sun Oct 19 1981 18:10:53 GMT+0100 (CET)");
+ assertEquals('1 month, 2 weeks', timesince(date, now));
+
+ date = new Date("Sat Dec 29 1970 04:52:13 GMT+0100 (CET)");
+ assertEquals('10 years, 11 months', timesince(date, now));
+
+ date = new Date("Wed Nov 13 1980 10:36:13 GMT+0100 (CET)");
+ assertEquals('1 year', timesince(date, now));
+
+ date = new Date("Wed Dec 02 1981 18:29:40 GMT+0100 (CET)"); // Random time on Britney Spears birthday :-)
+ assertEquals('2 minutes', timesince(date, now));
+
+ date = new Date("Wed Dec 02 1983 18:29:40 GMT+0100 (CET)"); // Random time on Britney Spears birthday :-)
+ assertEquals('0 minutes', timesince(date, now));
+ });
+
run();
View
70 utils/html.js
@@ -101,3 +101,73 @@ var truncate_html_words = exports.truncate_html_words = function (input, cnt) {
+var punctuation_re = /^((?:\(|<|&lt;)*)(.*?)((?:\.|,|\)|>|\n|&gt;)*)$/;
+var simple_email_re = /^\S+@[a-zA-Z0-9._\-]+\.[a-zA-Z0-9._\-]+$/;
+
+function trim_url(url, limit) {
+ if (limit === undefined || limit > url.length) { return url; }
+ return url.substr(0, limit - 3 > 0 ? limit - 3 : 0) + '...';
+}
+
+/* Function: urlize(text, options)
+ Converts all urls found in text into links (<a href="URL">URL</a>).
+ Arguments:
+ text - string, the text to convert.
+ options - optional, see options
+ Options:
+ escape - boolean, if true pass the string through escape()
+ limit - number, if defined the shown urls will be truncated with '...' at this length
+ nofollow - boolean, if true add rel="nofollow" to <a> tags
+*/
+function urlize(text, options) {
+ options = options || {};
+
+ var words = text.split(/(\s+)/g);
+ var nofollow = options.nofollow ? ' rel="nofollow"' : '';
+
+ words.forEach( function (word, i, words) {
+ var match;
+ if (word.indexOf('.') > -1 || word.indexOf('@') > -1 || word.indexOf(':') > -1) {
+ match = punctuation_re(word);
+ }
+
+ if (match) {
+ var url, lead = match[1], middle = match[2], trail = match[3];
+ if (middle.substr(0,7) === 'http://' || middle.substr(0,8) === 'https://') {
+ url = encodeURI(middle);
+ } else if (middle.substr(0,4) === 'www.' || (
+ middle.indexOf('@') === -1 && middle && middle[0].match(/[a-z0-9]/i) &&
+ (middle.substr(-4) === '.org' || middle.substr(-4) === '.net' || middle.substr(-4) === '.com'))) {
+ url = encodeURI('http://' + middle);
+ } else if (middle.indexOf('@') > -1 && middle.indexOf(':') === -1 && simple_email_re(middle)) {
+ url = 'mailto:' + middle;
+ nofollow = '';
+ }
+
+ if (url) {
+ var trimmed = trim_url(middle, options.limit);
+ if (options.escape) {
+ lead = escape(lead);
+ trail = escape(trail);
+ url = escape(url);
+ trimmed = escape(trimmed);
+ }
+ middle = '<a href="' + url + '"' + nofollow + '>' + trimmed + '</a>';
+ words[i] = lead + middle + trail;
+ }
+ } else if (options.escape) {
+ words[i] = escape(word);
+ }
+ });
+ return words.join('');
+}
+
+exports.urlize = urlize;
+
+
+
+
+
+
+
+
View
37 utils/html.test.js
@@ -25,5 +25,42 @@ testcase('truncate_html_words');
test('should close tags on truncate', function () {
assertEquals('<p>Joel is ...</p>', truncate_html_words('<p>Joel is a slug</p>', 2));
});
+testcase('urlize')
+ test('should urlize urls in text', function () {
+ assertEquals(
+ 'Check out <a href="http://www.djangoproject.com">www.djangoproject.com</a>',
+ urlize('Check out www.djangoproject.com')
+ );
+ assertEquals(
+ 'Check out (<a href="http://www.djangoproject.com">www.djangoproject.com</a>)',
+ urlize('Check out (www.djangoproject.com)')
+ );
+ assertEquals(
+ 'Skriv til <a href="mailto:test@test.se">test@test.se</a>',
+ urlize('Skriv til test@test.se')
+ );
+ assertEquals(
+ 'Check out (<a href="http://www.djangoproject.com">www.djangoproject.com</a>)\n' +
+ 'Skriv til <a href="mailto:test@test.se">test@test.se</a>',
+ urlize('Check out (www.djangoproject.com)\nSkriv til test@test.se')
+ );
+ assertEquals(
+ 'Check out <a href="http://www.djangoproject.com">www.djangopr...</a>',
+ urlize('Check out www.djangoproject.com', {limit: 15})
+ );
+ assertEquals(
+ 'Se her: (<a href="http://www.dr.dk">www.dr.dk</a> &amp; ' +
+ '<a href="http://www.djangoproject.com">http://www.djangoproject.com</a>)',
+ urlize('Se her: (www.dr.dk & http://www.djangoproject.com)', { escape: true })
+ );
+ assertEquals(
+ 'Se her: <a href="http://www.dr.dk?hest=4&amp;test=tolv">www.dr.dk?hest=4&amp;test=tolv</a>.',
+ urlize('Se her: www.dr.dk?hest=4&test=tolv.', { escape: true })
+ );
+ assertEquals(
+ 'Check out (<a href="http://www.djangoproject.com" rel="nofollow">www.djangoproject.com</a>)',
+ urlize('Check out (www.djangoproject.com)', { nofollow: true })
+ );
+ });
run();
Please sign in to comment.
Something went wrong with that request. Please try again.