/****************************************************************************************************** jQuery.cuteTime Author Jeremy Horn Version 1.1.1 Date: 2/1/2010 Copyright (c) 2009 Jeremy Horn- jeremydhorn(at)gmail(dot)c0m | http://tpgblog.com Dual licensed under MIT and GPL. DESCRIPTION It's CuteTime! CuteTime is a customizable jQuery plugin that automatically converts timestamps to formats much cuter. Also has the ability to dynamically re-update and/or automatically update timestamps on a controlled interval. If used by Selector, replaces the text of the provided object with a cuteTime. If used as a function, returns a string containing a cuteTime version of the provided timestamp. BY DEFAULT automatic updating is disabled and the following CuteTimes can be displayed... the future! just now a few seconds ago a minute ago x minutes ago an hour ago x hours ago yesterday x days ago last month x months ago last year x years ago IMPLEMENTATION $('.timestamp').cuteTime(); $('.timestamp').cuteTime({ / * OPTIONS * / }); cutetime_object = $('.timestamp').cuteTime(); cutetime_object.update_cuteness(); $.cuteTime('2009/10/12 22:11:19'); $.cuteTime({ / * OPTIONS * / }, '2009/10/12 22:11:19'); COMPATIBILITY Tested in FF3.5, IE7 With jQuery 1.3.2 METHODS When initialized the cuteTime variable either updates or assigns the TS_ATTR attribute to the provided objects. Method implementation supports chaining and returns jQuery object. e.g.
2009 10 12 22:11:19
If the cutetime attribute already exists within the provided object, then the text within the object is ignored in the cutification process. If the cutetime attribute does not exist or an invalid one is provided, then a valid cutetime attribute is assigned to the object. If the cutetime attribute is missing, then it is calculated from the text of the provided object. If neither cutetime attibute nor valid object text exist then the timestamp is assumed to by 'now'. stop_cuteness() stops all automatic updates of refresh enabled timestamps start_cuteness() starts the automatic updating of timestamps REMINDER: make sure refresh is set to > 0 update_cuteness() updates timestamps of the provided objects FUNCTIONS cuteTime() CUSTOMIZATION cuteTime(OPTIONS) e.g. $('.ts2').cuteTime({ refresh: 60000 }); refresh: time in milliseconds before next refresh of page data; -1 == no refresh time_ranges: array of bound_structures definining the cute descriptions associated with time ranges bound_structures consist of the following variables bound: lower inclusive bound, or starting point, for using the 'cuteness' string for describing the current timestamp the exclusive upper bound is defined by the next bound definition in the time_ranges array cuteness: string to use in place of the current timestamp the special keyword %CT% can be used within the cutetime string to override the prepending of the calculated difference, when called for e.g. "it was %CT% hours ago" unit_size: the divisor to apply to the calculated time difference; if unit_size > 0 then a number value is prepended to the cuteness string as calculated by time_difference / unit_size e.g. 4 hours ago if unit_size = 0, then no number is pre-pended to the cuteness string e.g. an hour ago EXAMPLE OPTIONS = { refresh: -1, time_ranges: [ {bound: NEG_INF, cuteness: 'the future!', unit_size: 0}, {bound: 0, cuteness: 'just now', unit_size: 0}, {bound: 60 * 1000, cuteness: 'a minute ago', unit_size: 0}, {bound: 60 * 1000 * 2, cuteness: ' minutes ago', unit_size: 60 * 1000}, {bound: 60 * 1000 * 60, cuteness: 'an hour ago', unit_size: 0}, {bound: 60 * 1000 * 60 * 2, cuteness: ' hours ago', unit_size: 60 * 1000 * 60}, {bound: POS_INF, cuteness: 'a blinkle ago', unit_size: 0} ] }; VALID TIMESTAMP FORMAT EXAMPLES 2009-10-15 14:06:23 *doesn't work in IE Thu Oct 15 2009 22:11:19 GMT-0400 (Eastern Daylight Time Oct 15 2009 22:11:19 2009 10 12 22:11:19 * only works in FF 10 15 2009 22:11:19 * only works in FF ALL ISO8601 Date/Time Formats Also Supported 2009-11-24T19:20:30+01:00 2009-11 2009-11-24T13:15:30Z ...etc... * if the TIMESTAMP can be recognized by the JavaScript Date() Object then it is VALID (i.e. if it can be parsed by Date.parse()) ** IE date parsing is VERY DIFFERENT (and more limiting) than FF :-( [not cute!] MORE For more usage and examples, go to: http://tpgblog.com/cutetime/ ******************************************************************************************************/ (function($) { // CONSTANTS var NEG_INF = Number.NEGATIVE_INFINITY; var POS_INF = Number.POSITIVE_INFINITY; var TS_ATTR = 'data-timestamp'; /********************************************************************************** FUNCTION cuteTime DESCRIPTION cuteTime method constructor allows for customization of refreh rate the time difference ranges and cute descriptions e.g. $(something).cuteTime(); **********************************************************************************/ $.fn.cuteTime = function(options) { var right_now = new Date().getTime(); var other_time; var curr_this; // check for new & valid options if ((typeof options == 'object') || (options == undefined)) { // then update the settings [destructive] $.fn.cuteTime.c_settings = $.extend({}, $.fn.cuteTime.settings, options); $.fn.cuteTime.the_selected = this; // process all provided objects this.each(function() { // element-specific code here curr_this = $(this); other_time = get_time_value(curr_this); curr_this.html(get_cuteness(right_now - other_time)); }); // check for and conditionally launch the automatic refreshing of timestamps $.fn.cuteTime.start_cuteness(); } return this; }; /********************************************************************************** FUNCTION cuteTime DESCRIPTION cuteTime function accepts a string representation of a timestamp as its parameter and returns a string version of its equivalent cutetime e.g. $.cuteTime('2009 10 12 22:11:19'); or e.g. $.cuteTime(SETTINGS, '2009 10 12 22:11:19'); can be customized by directly accessing the settings: $.fn.cuteTime.settings = ... **********************************************************************************/ $.cuteTime = function(options, val) { var right_now = new Date().getTime(); var other_time; var curr_this; var ts_string = null; if (typeof options == 'object') { $.fn.cuteTime.c_settings = $.extend({}, $.fn.cuteTime.settings, options); } if (typeof options == 'string') { ts_string = options; } else if (typeof val == 'string') { ts_string = val; } if (ts_string != null) { // then we will be returning a cutetime string and doing nothing else other_time = date_value(ts_string); if (!isNaN(other_time)) { return get_cuteness(right_now - other_time); } else { // on failure return error message return 'INVALID_DATETIME_FORMAT'; } } return this; }; /********************************************************************************** FUNCTION cuteTime.settings DESCRIPTION data stucture containing the refresh rate and time range specifications for the cuteTimes can be directly accessed by '$.fn.cuteTime.settings = ... ;' **********************************************************************************/ $.fn.cuteTime.settings = { refresh: -1, // time in milliseconds before next refresh of page data; -1 == no refresh time_ranges: [ {bound: NEG_INF, // IMPORANT: bounds MUST be in ascending order, from negative infinity to positive infinity cuteness: 'the future!', unit_size: 0}, {bound: 0, cuteness: 'just now', unit_size: 0}, {bound: 20 * 1000, cuteness: 'a few seconds ago', unit_size: 0}, {bound: 60 * 1000, cuteness: 'a minute ago', unit_size: 0}, {bound: 60 * 1000 * 2, cuteness: ' minutes ago', unit_size: 60 * 1000}, {bound: 60 * 1000 * 60, cuteness: 'an hour ago', unit_size: 0}, {bound: 60 * 1000 * 60 * 2, cuteness: ' hours ago', unit_size: 60 * 1000 * 60}, {bound: 60 * 1000 * 60 * 24, cuteness: 'yesterday', unit_size: 0}, {bound: 60 * 1000 * 60 * 24 * 2, cuteness: ' days ago', unit_size: 60 * 1000 * 60 * 24}, {bound: 60 * 1000 * 60 * 24 * 30, cuteness: 'last month', unit_size: 0}, {bound: 60 * 1000 * 60 * 24 * 30 * 2, cuteness: ' months ago', unit_size: 60 * 1000 * 60 * 24 * 30}, {bound: 60 * 1000 * 60 * 24 * 30 * 12, cuteness: 'last year', unit_size: 0}, {bound: 60 * 1000 * 60 * 24 * 30 * 12 * 2, cuteness: ' years ago', unit_size: 60 * 1000 * 60 * 24 * 30 * 12}, {bound: POS_INF, cuteness: 'a blinkle ago', unit_size: 0} ] }; /********************************************************************************** FUNCTION cuteTime.start_cuteness DESCRIPTION activates the recurring process to update the objects' timestamps IMPORTANT: make sure refresh has been set to > 0 TODO allow for the specifying of a new refresh rate when this function is called **********************************************************************************/ $.fn.cuteTime.start_cuteness = function() { var refresh_rate = $.fn.cuteTime.c_settings.refresh; if ($.fn.cuteTime.process_tracker == null) { if (refresh_rate > 0) { $.fn.cuteTime.process_tracker = setInterval( "$.fn.cuteTime.update_cuteness()", refresh_rate ); } } else { // ignore this call; auto-refresh is already running!! } return this; }; /********************************************************************************** FUNCTION cuteTime.update_cuteness DESCRIPTION updates the objects' timestamps **********************************************************************************/ $.fn.cuteTime.update_cuteness = function() { var right_now = new Date().getTime(); var curr_this; var other_time; $.fn.cuteTime.the_selected.each(function() { curr_this = $(this); other_time = get_time_value(curr_this); curr_this.html(get_cuteness(right_now - other_time)); }); } /********************************************************************************** FUNCTION cuteTime.stop_cuteness DESCRIPTION deactivates the recurring process that updates the objects' timestamps **********************************************************************************/ $.fn.cuteTime.stop_cuteness = function() { if ($.fn.cuteTime.process_tracker != null) { clearInterval($.fn.cuteTime.process_tracker); $.fn.cuteTime.process_tracker = null; } else { // ignore this call; there is nothing to stop!! } return this; }; ////////////////////////////////////////////////////////////////////////////////// // private functions and settings /********************************************************************************** FUNCTION get_cuteness DESCRIPTION based on passed in time_difference (in milliseconds) returns a string of the associated cuteness if a number should be insterted into the string (unit_size not empty) THEN if %CT% exists within the cuteness STRING THEN replace it with the calculated number ELSE prepend the calculated number to the front of the string (mostly for backwards compatibility) ON ERROR returns time in 'pookies' **********************************************************************************/ function get_cuteness(time_difference) { var time_ranges = $.fn.cuteTime.c_settings.time_ranges; var pre_calculated_time, calculated_time; var cute_time = ''; jQuery.each(time_ranges, function(i, timespan) { if (i < time_ranges.length-1) { if (( time_difference >= timespan['bound']) && ( time_difference < time_ranges[i+1]['bound'])) { if (timespan['unit_size'] > 0) { calculated_time = Math.floor(time_difference / timespan['unit_size']); } else { calculated_time = ''; } // allow for inline replacement pre_calculated_time = timespan['cuteness'].replace(/%CT%/, calculated_time); if (pre_calculated_time == timespan['cuteness']) { // nothing was replaced // prepend the value cute_time = calculated_time + timespan['cuteness']; } else { // inline replacement occurred cute_time = pre_calculated_time; } return false; } } else { return false; } }); // something is wrong with the time if (cute_time == '') { cute_time = '2 pookies ago'; // IMPORTANT: ALWAYS BE CUTE!!! } return cute_time; } /********************************************************************************** FUNCTION date_value DESCRIPTION returns the date in time measured since 1970 (see definition of Date.valueOf) if not ISO 8601 date format compliant, performs minimal date correction to expand the range of VALID date formats **********************************************************************************/ function date_value(the_date) { var the_value; if ((new_date = toISO8601(the_date)) != null) { the_value = new_date.valueOf(); } else { the_value = (new Date(the_date)).valueOf(); if (isNaN(the_value)) { // then the date must be the alternate db styled format the_value = new Date(the_date.replace(/-/g, " ")); } } return the_value; } /********************************************************************************** FUNCTION toISO8601 DESCRIPTION converts an ISO8601 formatted timestamp to the JavaScript Date() Object if the provided string is not in ISO8601 format, then null is returned ** Note to people who copy this function: If you like it, if you use it, please provide credit to Jeremy Horn, The Product Guy @ http://tpgblog.com and the jQuery CuteTime Plugin @ http://tpgblog.com/cutetime; Thanks. :-) ISO8601 http://www.w3.org/TR/NOTE-datetime Year: YYYY (eg 1997) Year and month: YYYY-MM (eg 1997-07) Complete date: YYYY-MM-DD (eg 1997-07-16) Complete date plus hours and minutes: YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00) Complete date plus hours, minutes and seconds: YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) Complete date plus hours, minutes, seconds and a decimal fraction of a second YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) Formatted REGEXP used within... /^(\d{4})( (-(\d{2}) (-(\d{2}) (T(\d{2}):(\d{2}) (:(\d{2}) (.(\d+))? )? (Z|( ([+-])((\d{2}):(\d{2})) )) )? )? )? )$/ NOTE String.match() returns: in FireFox, void(0) in Internet Explorer, "" <-- empty string ... for unmatched elements within the array **********************************************************************************/ function toISO8601(the_date){ var iso_date = the_date.match(/^(\d{4})((-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(.(\d+))?)?(Z|(([+-])((\d{2}):(\d{2})))))?)?)?)$/); if (iso_date != null) { var new_date = new Date(); var TZ_hour_offset = 0; var TZ_minute_offset = 0; new_date.setUTCFullYear(iso_date[1]); if (!isEmpty(iso_date[4])) { new_date.setUTCMonth(iso_date[4] - 1); if (!isEmpty(iso_date[6])) { new_date.setUTCDate(iso_date[6]); // check TZ first if (!isEmpty(iso_date[16])) { TZ_hour_offset = iso_date[18]; TZ_minute_offset = iso_date[19]; if (!isEmpty(iso_date[16])) { TZ_hour_offset *= -1; TZ_minute_offset *= -1; } } if (!isEmpty(iso_date[8])) { new_date.setUTCHours(iso_date[8] - TZ_hour_offset); new_date.setUTCMinutes(iso_date[9] - TZ_minute_offset) if (!isEmpty(iso_date[11])) { new_date.setUTCSeconds(iso_date[11]); if (!isEmpty(iso_date[13])) { new_date.setUTCMilliseconds(iso_date[13]*1000); } } } } } return new_date; } else { return null; } } /********************************************************************************** FUNCTION isEmpty DESCRIPTION determines whether or not the passed in string is EMPTY EMPTY = null OR "" {EMPTY STRING} **********************************************************************************/ function isEmpty( inputStr ) { if ( null == inputStr || "" == inputStr ) { return true; } return false; } /********************************************************************************** FUNCTION get_time_value DESCRIPTION get the time value specified either in the text or the cuteime attribute of the object and update the cutetime attribute whether initially present or not If the cutetime attribute already exists within the provided object, then the text within the object is ignored in the cutification process. If the cutetime attribute does not exist or an invalid one is provided, then a valid cutetime attribute is assigned to the object. If the cutetime attribute is missing, then it is calculated from the text of the provided object. If neither cutetime attibute nor valid object text exist then the timestamp is assumed to by 'now'. **********************************************************************************/ function get_time_value(obj) { var time_value = Number.NaN; time_string = get_cutetime_attr(obj); // returns string or NULL if (time_string != null) { time_value = date_value(time_string); } if (isNaN(time_value)) { time_string = get_object_text(obj); if (time_string != null) { time_value = date_value(time_string); } } // if nothing valid available then set time to RIGHT NOW if (isNaN(time_value)) { time_string = new Date().toString(); time_value = date_value(time_string); } // update cutetime attribute and return the time_value set_cutetime_attr(time_string, obj); return time_value; } /********************************************************************************** FUNCTION get_cutetime_attr DESCRIPTION returns the found value of the cutetime attribute of the specified object or NULL **********************************************************************************/ function get_cutetime_attr(obj) { var return_value = obj.attr(TS_ATTR); if (return_value != undefined) { return return_value; } else { return null; } } /********************************************************************************** FUNCTION set_cutetime_attr DESCRIPTION sets / updates the cutetime attribute of the object the cuteime attribute is set to be STARTING point against which all future updates are measured against **********************************************************************************/ function set_cutetime_attr(attr, obj) { // assume valid attr(ibute) value obj.attr(TS_ATTR, attr); } /********************************************************************************** FUNCTION get_object_text DESCRIPTION returns the text associated with the specified object (if any) **********************************************************************************/ function get_object_text(obj) { return obj.text(); } })(jQuery);