Last active
September 24, 2015 00:49
-
-
Save jakerb/8b329401a2ea51f80b9a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 1.4.2 | |
* @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-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) | |
*/ | |
/* | |
* Tense Color for Timeago.js by @jakebown1 | |
* Example: https://jsfiddle.net/638h7b41/ | |
* Thoughts and comments welcome: https://gist.github.com/jakerb/8b329401a2ea51f80b9a | |
* Note: Classes added to timeago element for past and future - you can change the | |
* class names from line 57. | |
*/ | |
(function (factory) { | |
if (typeof define === 'function' && define.amd) { | |
// AMD. Register as an anonymous module. | |
define(['jquery'], factory); | |
} if (typeof module === 'object' && typeof module.exports === 'object') { | |
factory(require('jquery')); | |
} else { | |
// Browser globals | |
factory(jQuery); | |
} | |
}(function ($) { | |
$.timeago = function(timestamp) { | |
if (timestamp instanceof Date) { | |
return inWords(timestamp); | |
} else if (typeof timestamp === "string") { | |
return inWords($.timeago.parse(timestamp)); | |
} else if (typeof timestamp === "number") { | |
return inWords(new Date(timestamp)); | |
} else { | |
return inWords($.timeago.datetime(timestamp)); | |
} | |
}; | |
var $t = $.timeago; | |
$.extend($.timeago, { | |
settings: { | |
refreshMillis: 60000, | |
allowPast: true, | |
allowFuture: true, | |
localeTitle: false, | |
allowColors: true, | |
inPast: true, | |
inPastColor: { past: 'timeago-past', future: 'timeago-future' }, | |
cutoff: 0, | |
strings: { | |
prefixAgo: null, | |
prefixFromNow: null, | |
suffixAgo: "ago", | |
suffixFromNow: "from now", | |
inPast: 'any moment 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", | |
wordSeparator: " ", | |
numbers: [] | |
} | |
}, | |
inWords: function(distanceMillis) { | |
if(!this.settings.allowPast && ! this.settings.allowFuture) { | |
throw 'timeago allowPast and allowFuture settings can not both be set to false.'; | |
} | |
var $l = this.settings.strings; | |
var prefix = $l.prefixAgo; | |
var suffix = $l.suffixAgo; | |
inPast = true; | |
if (this.settings.allowFuture) { | |
if (distanceMillis < 0) { | |
prefix = $l.prefixFromNow; | |
suffix = $l.suffixFromNow; | |
inPast = false; | |
} | |
} | |
this.settings.inPast = inPast; | |
if(!this.settings.allowPast && distanceMillis >= 0) { | |
return this.settings.strings.inPast; | |
} | |
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 < 42 && substitute($l.day, 1) || | |
days < 30 && substitute($l.days, Math.round(days)) || | |
days < 45 && substitute($l.month, 1) || | |
days < 365 && substitute($l.months, Math.round(days / 30)) || | |
years < 1.5 && substitute($l.year, 1) || | |
substitute($l.years, Math.round(years)); | |
var separator = $l.wordSeparator || ""; | |
if ($l.wordSeparator === undefined) { separator = " "; } | |
return $.trim([prefix, words, suffix].join(separator)); | |
}, | |
parse: function(iso8601) { | |
var s = $.trim(iso8601); | |
s = s.replace(/\.\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 | |
s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900 | |
return new Date(s); | |
}, | |
datetime: function(elem) { | |
var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); | |
return $t.parse(iso8601); | |
}, | |
isTime: function(elem) { | |
// jQuery's `is()` doesn't play well with HTML5 in IE | |
return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); | |
} | |
}); | |
// functions that can be called via $(el).timeago('action') | |
// init is default when no action is given | |
// functions are called with context of a single element | |
var functions = { | |
init: function(){ | |
var refresh_el = $.proxy(refresh, this); | |
refresh_el(); | |
refresh.apply(this); | |
var $s = $t.settings; | |
if ($s.refreshMillis > 0) { | |
this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis); | |
} | |
}, | |
update: function(time){ | |
var parsedTime = $t.parse(time); | |
$(this).data('timeago', { datetime: parsedTime }); | |
if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString()); | |
refresh.apply(this); | |
}, | |
updateFromDOM: function(){ | |
$(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) }); | |
refresh.apply(this); | |
}, | |
dispose: function () { | |
if (this._timeagoInterval) { | |
window.clearInterval(this._timeagoInterval); | |
this._timeagoInterval = null; | |
} | |
} | |
}; | |
$.fn.timeago = function(action, options) { | |
var fn = action ? functions[action] : functions.init; | |
if(!fn){ | |
throw new Error("Unknown function name '"+ action +"' for timeago"); | |
} | |
// each over objects here and call the requested function | |
this.each(function(){ | |
fn.call(this, options); | |
}); | |
return this; | |
}; | |
function getTime(set, tense) { | |
if(!set) { | |
return $.timeago.settings.inPast; | |
} else { | |
$.timeago.settings.inPast = tense; | |
} | |
}; | |
function refresh() { | |
//check if it's still visible | |
if(!$.contains(document.documentElement,this)){ | |
//stop if it has been removed | |
$(this).timeago("dispose"); | |
return this; | |
} | |
var data = prepareData(this); | |
var $s = $t.settings; | |
if (!isNaN(data.datetime)) { | |
if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) { | |
$(this).text(inWords(data.datetime)); | |
} | |
} | |
return this; | |
} | |
function prepareData(element) { | |
element = $(element); | |
if (!element.data("timeago")) { | |
element.data("timeago", { datetime: $t.datetime(element) }); | |
var text = $.trim(element.text()); | |
if ($t.settings.localeTitle) { | |
element.attr("title", element.data('timeago').datetime.toLocaleString()); | |
} else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { | |
element.attr("title", text); | |
} | |
} | |
if($t.settings.allowColors) { | |
var colorClass; | |
if($t.settings.inPast) { | |
colorClass = $t.settings.inPastColor.past; | |
} else { | |
colorClass = $t.settings.inPastColor.future; | |
} | |
element.addClass(colorClass); | |
} | |
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"); | |
})); |
Hey @chaimleib, it has been updated now to add a class rather than a color (see fiddle https://jsfiddle.net/638h7b41/).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On line 237, could we instead use css classes for more flexibility? For example, 'timeago_past' or 'timeago_future'. Looks great!