Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Fuzzy time #341

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion r2/example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ template_debug = false
reload_templates = true
# use uncompressed static files (out of /static/js and /static/css)
# rather than compressed files out of /static (for development if true)
uncompressedJS = true
uncompressedJS = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be sure to undo this! :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be some checks in place before accessing the r.strings array then? In dev mode it will throw a few errors because r.strings would not have been defined.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running with uncompressedJS == True worked for me. Perhaps you forgot to run make?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted back to true

# enable/disable verbose SQL printing
sqlprinting = false
# enable/disable writing errors as they occur to a rabbit-mq queue
Expand Down
1 change: 1 addition & 0 deletions r2/r2/lib/js.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def outputs(self):
"lib/json2.js",
"lib/jquery.cookie.js",
"lib/jquery.url.js",
"lib/jquery.timeago.js",
"jquery.reddit.js",
"base.js",
"ui.js",
Expand Down
17 changes: 17 additions & 0 deletions r2/r2/lib/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@
# dictionary is not used directly but rather is managed by the single
# StringHandler instance strings
string_dict = dict(


# time ago values (jquery.timeago.js)
ta_ago = "ago",
ta_suffixFromNow = "from now",
ta_seconds = "less than a minute",
ta_minute = "about a minute",
ta_minutes = "%d minutes",
ta_hour = "about an hour",
ta_hours = "about %d hours",
ta_day = "a day",
ta_days = "%d days",
ta_month = "about a month",
ta_months = "%d months",
ta_year = "about a year",
ta_years = "%d years",


banned_by = "removed by %s",
banned = "removed",
Expand Down
150 changes: 150 additions & 0 deletions r2/r2/public/static/js/lib/jquery.timeago.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* Timeago is a jQuery plugin that makes it easy to support automatically
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
*
* @name timeago
* @version 0.10.0
* @requires jQuery v1.2.3+
* @author Ryan McGeary
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
*
* For usage and examples, visit:
* http://timeago.yarp.com/
*
* Copyright (c) 2008-2011, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
*/
(function($) {
$.timeago = function(timestamp) {
if (timestamp instanceof Date) {
return inWords(timestamp);
} else if (typeof timestamp === "string") {
return inWords($.timeago.parse(timestamp));
} else {
return inWords($.timeago.datetime(timestamp));
}
};
var $t = $.timeago;

$.extend($.timeago, {
//these will be overwritten in ../reddit.js (jquery document ready function)
//based on i18n settings
settings: {
refreshMillis: 60000,
allowFuture: false,
strings: {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: "ago",
suffixFromNow: "from now",
seconds: "less than a minute",
minute: "about a minute",
minutes: "%d minutes",
hour: "about an hour",
hours: "about %d hours",
day: "a day",
days: "%d days",
month: "about a month",
months: "%d months",
year: "about a year",
years: "%d years",
numbers: []
}
},
inWords: function(distanceMillis) {
var $l = this.settings.strings;
var prefix = $l.prefixAgo;
var suffix = $l.suffixAgo;
if (this.settings.allowFuture) {
if (distanceMillis < 0) {
prefix = $l.prefixFromNow;
suffix = $l.suffixFromNow;
}
}

var seconds = Math.abs(distanceMillis) / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
var days = hours / 24;
var years = days / 365;

function substitute(stringOrFunction, number) {
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
var value = ($l.numbers && $l.numbers[number]) || number;
return string.replace(/%d/i, value);
}

var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
seconds < 90 && substitute($l.minute, 1) ||
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
minutes < 90 && substitute($l.hour, 1) ||
hours < 24 && substitute($l.hours, Math.round(hours)) ||
hours < 48 && substitute($l.day, 1) ||
days < 30 && substitute($l.days, Math.floor(days)) ||
days < 60 && substitute($l.month, 1) ||
days < 365 && substitute($l.months, Math.floor(days / 30)) ||
years < 2 && substitute($l.year, 1) ||
substitute($l.years, Math.floor(years));

return $.trim([prefix, words, suffix].join(" "));
},
parse: function(iso8601) {
var s = $.trim(iso8601);
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
s = s.replace(/T/," ").replace(/Z/," UTC");
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
return new Date(s);
},
datetime: function(elem) {
// jQuery's `is()` doesn't play well with HTML5 in IE
var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
return $t.parse(iso8601);
}
});

$.fn.timeago = function() {
var self = this;
self.each(refresh);

var $s = $t.settings;
if ($s.refreshMillis > 0) {
setInterval(function() { self.each(refresh); }, $s.refreshMillis);
}
return self;
};

function refresh() {
var data = prepareData(this);
if (!isNaN(data.datetime)) {
$(this).text(inWords(data.datetime));
}
return this;
}

function prepareData(element) {
element = $(element);
if (!element.data("timeago")) {
element.data("timeago", { datetime: $t.datetime(element) });
/* Stops the title being overridden
var text = $.trim(element.text());
if (text.length > 0) {
element.attr("title", text);
}
*/
}
return element.data("timeago");
}

function inWords(date) {
return $t.inWords(distance(date));
}

function distance(date) {
return (new Date().getTime() - date.getTime());
}

// fix for IE6 suckage
document.createElement("abbr");
document.createElement("time");
}(jQuery));
22 changes: 22 additions & 0 deletions r2/r2/public/static/js/reddit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,28 @@ $(function() {
$("#shortlink-text").click(function() {
$(this).select();
});

/* Load fuzzy time i18n strings */
$.timeago.settings.strings = {
prefixAgo: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the prefix strings to strings.py so that they can be used by localizations?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! :)

prefixFromNow: null,
suffixAgo: r.strings.ta_ago,
suffixFromNow: r.strings.ta_suffixFromNow,
seconds: r.strings.ta_seconds,
minute: r.strings.ta_minute,
minutes: r.strings.ta_minutes,
hour: r.strings.ta_hour,
hours: r.strings.ta_hours,
day: r.strings.ta_day,
days: r.strings.ta_days,
month: r.strings.ta_month,
months: r.strings.ta_months,
year: r.strings.ta_year,
years: r.strings.ta_years
};

/* Init fuzzy time on time elements */
$("time.fuzzy-time").timeago();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been working on overhauling the JS code organization, and the mishmash of initialization code in reddit.js is best considered legacy. Could you please move this initialization code to its own file/object like r.timeago (see r.analytics as an example) and init it at the bottom of base.js?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you prefer the file name to be r.timeago.js or just timeago.js?
The code for timeago is in lib/jquery.timeago.js - should I move that into the same file as the init function or leave it as it is?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest defining an object named r.timeago in a file named something like fuzzytime.js (to match the class name). Since the fuzzy time behavior is being provided by a library, I'd leave it as is in the js/lib dir.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool - that's done now.

});

function show_friend(account_fullname) {
Expand Down
2 changes: 1 addition & 1 deletion r2/r2/templates/comment.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
%if show:
${unsafe(self.score(thing, likes = thing.likes))}&#32;
%endif
${thing_timestamp(thing, thing.timesince)}&#32;${_("ago")}
${thing_timestamp(thing, thing.timesince)}&#32;
%if thing.editted:
<em>*</em>&nbsp;
%endif
Expand Down
6 changes: 3 additions & 3 deletions r2/r2/templates/link.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@
<%def name="tagline()">
<%
if thing.score_fmt == Score.points:
taglinetext = "<span>" + _("%(score)s submitted %(when)s ago") + "</span>"
taglinetext = "<span>" + _("%(score)s submitted %(when)s") + "</span>"
if thing.different_sr:
taglinetext += " <span>" + _("by %(author)s to %(reddit)s") + "</span>"
else:
taglinetext += " <span>" + _("by %(author)s") + "</span>"
elif thing.different_sr:
taglinetext = _("submitted %(when)s ago by %(author)s to %(reddit)s")
taglinetext = _("submitted %(when)s by %(author)s to %(reddit)s")
else:
taglinetext = _("submitted %(when)s ago by %(author)s")
taglinetext = _("submitted %(when)s by %(author)s")
taglinetext = taglinetext.replace(" ", "&#32;")
%>

Expand Down
2 changes: 1 addition & 1 deletion r2/r2/templates/utils.html
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@
<%def name="timestamp(date, since=None)">
## todo: use pubdate attribute once things are <article> tags.
## note: comment and link templates will pass a CachedVariable stub as since.
<time title="${long_datetime(date)}" datetime="${date.isoformat()}">
<time class="fuzzy-time" title="${long_datetime(date)}" datetime="${date.isoformat()}">
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it... because all the elements will have a datetime field - does it need class="fuzzy-time" ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is sensible to include a class to avoid any other time elements being unintentionally affected someday.

${unsafe(since or timesince(date))}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could place the "ago" string here so that pages are rendered with "posted 37 seconds ago" and therefore will be a less noticeable jump when the plugin changes it to "about a minute ago"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like timesince itself has support for adding an "ago" suffix if you pass bare=False. It might make sense to add support for a language-specific prefix there to mirror timeago. Then you could just pass bare=False in this function.

What do you think @kemitche? Is a language-specific prefix and suffix sufficient for translators to handle fuzzy times, taking them out of the containing translatable string? (see the strings changes above)

</time>
</%def>
Expand Down