PDA

View Full Version : Ext.fc.fuzzyDate - a nice way to present date and time



francodacosta
24 Apr 2009, 8:00 AM
http://francodacosta.com/demos/extjs/fuzzydate/logo.png

About

The FuzzyDate plugin was inspired by the TimeAgo plugin for jQuery . I needed something similar for ExtJs. Since i didn't find it I decided to build one for ExtJs.

The ideia is instead of showing a timestamp to the user show a more friendly message like one hour ago or 3 months from now

Features

Updates timestamps automatically, avoiding timestamps dated "1 minute ago" even though the page was opened 10 minutes ago
Multiple languages supported
Fully Customizable
You can define several date formats that will be used to convert the date string to an Ext Date object
if don't need automatic updates neither want to have it atached to an element you can simply get the translated date with the translate() function


Download

http://francodacosta.com/demos/extjs/fuzzydate/


Cheers

Nuno

BIS
24 Apr 2009, 3:57 PM
thnx for sharing,

i once stumbled on this and it certainly impressed me
thnx for porting this nice widget


+1 =D>

MD
1 May 2009, 12:30 PM
Awesome! A perfect fit with TwitterPanel ux =D>

dawesi
5 May 2009, 2:29 AM
Sweet... this will great for last login wording!!

@MD... ah so sad... please don't degrade Ext to use with Flutter... er twitter!

francodacosta
16 Jul 2009, 2:16 PM
Awesome! A perfect fit with TwitterPanel ux =D>



Sweet... this will great for last login wording!!

@MD... ah so sad... please don't degrade Ext to use with Flutter... er twitter!

I'm glad you liked it

steffenk
17 Jul 2009, 2:46 AM
Awesome! A perfect fit with TwitterPanel ux =D>

Hi - wondering, i didn't see that. I just write a twitter ux as i used it in my applications (TYPO3), it's like this: http://dev.sk-typo3.de/cetest/web-widgets/twitter.html

@francodacosta thx, i will test. One problem i see is the localization. This is a tricky one.

Dumbledore
21 Jul 2009, 10:25 PM
something is wrong here...

When i try following sample:


var f = new Ext.fc.fuzzyDate();
alert ( f.translate('2009-12-25') );

firebug says : TypeError: options is undefined Line 176

is options a required parameter when use translate()?

Bye, Dumbledore

francodacosta
24 Jul 2009, 7:20 AM
Hi Dumbledore,

options should be optional


just add this after line 175

if(!options) options = processOptions({}) ;


cheers

Dumbledore
28 Jul 2009, 8:47 AM
is it possible to make a config a la:

show me fuzzydates about now - 3 month, else show normal dates?

Bye, Dumbledore

Dumbledore
4 Aug 2009, 8:40 PM
i change the translate function to this:


,
translate: function(str, options) {
var opt = {};
Ext.apply(opt, options, processOptions({}));

var d = parseDate(str, opt.dateFormats);
if (d) {
return (dateTimeToString(d, opt));
} else {
if (opt.onErrorWriteTitle) return (str);
}
return false;
}

so it is possible to set only a small set of options like the translation. In the original i must set all options...
Perhaps it helps...


Bye Dumbledore

interfasys
23 May 2011, 10:19 AM
Here is a version that works with Sencha touch. I've also added a cutoff time after which a normal date will be displayed.



Ext.fc.fuzzyDate = function() {
var defaultConfig = function() {
return{
refreshInterval: 60,
onErrorWriteTitle: true,
itemSelector: 'span.fuzzyDate',
cutoff: 172800,
defaultReturnFormat: 'd/m/Y, H:i',
dateFormats: [
"Y-m-d H:i:s", //ISO8601Long
"Y-m-d", //ISO8601Short
"h:i:s", //time
"H:i:s", //time
"n/j/Y", //ShortDate
"l, F d, Y", //LongDate
"l, F d, Y g:i:s A", //FullDateTime
"F d", //MonthDay
"g:i A", //ShortTime
"g:i:s A", //LongTime
"Y-m-d\\TH:i:s", //SortableDateTime
"Y-m-d H:i:sO", //UniversalSortableDateTime
"F, Y", //YearMonth
"d/m/Y, H:i:s", // Euro format
"D, d F Y G:i:s O" // Twitter format
],
translation: {
prefixAgo : '',
prefixFromNow : '',
suffixAgo : "ago",
suffixFromNow : "from now",

now : "seconds",
seconds : "less than a minute",
minute : "about a minute",
minutes : "%d minutes",
hour : "about an hour",
hours : "about %d hours",
day : "a day",
days : "about %d days",
month : "about a month",
months : "about %d months",
year : "about a year",
years : "about %d years"
},
offsets: {
//values are exclusive, they will be evaluated as date < now or date < oneYear
now : 15, //number of seconds a date is considered seconds
minute : 45, //number of seconds a date is considered less than a minute
minuteOffset: 90, //number of seconds a date is considered about a minute
xminutes : 50, //number of minutes a date should be represented as x minutes
oneHour : 80, //number of minutes a date should be represented as one hour
xHours : 24, //number of hours a date should be represented as x hours
oneDay : 48, //number of hours a date should be represented as one day
xDays : 30, //number of days a date should be represented as X days
oneMonth : 60, //number of days a date should be represented as one month
xMonths : 365, //number of days a date should be represented as x months
oneYear : 2 //number of years a date should be represented as one year
}
}
};

/**
* Adds the number of minutes or seconds or days, etc.
* @param text
* @param value
*/
function sprintf(text, value) {
//I just need %d for one parameter
return text.replace(/%d/, value);
}

/**
* Determines which format to use
* @param str
* @param formats
*/
function parseDate(str, formats) {
var d = null;
for (i = 0; i < formats.length; i++) {
d = Date.parseDate(str, formats[i]);
if (d) {
return d
}
}

return false;
}

/**
* Returns the relative time
* @param dateObject
* @param options
*/
var dateTimeToString = function (dateObject, options) {
var t = options.translation,
cutoff = options.cutoff,
defaultReturnFormat = options.defaultReturnFormat,
now = new Date(),
offset = now.format('U') - dateObject.format('U');
//if < 0 then date is in future

if (Math.abs(offset) < cutoff) {
if (offset < 0) {
suffix = t.suffixFromNow;
prefix = t.prefixFromNow;
} else {
suffix = t.suffixAgo;
prefix = t.prefixAgo;
}

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

var fuzzy = false ||
seconds < options.offsets.now && sprintf(t.seconds, Math.round(now)) || //seconds
seconds < options.offsets.minute && sprintf(t.seconds, Math.round(seconds)) || //about 1 minute
seconds < options.offsets.minuteOffset && sprintf(t.minute, 1) || //about x minutes
minutes < options.offsets.xminutes && sprintf(t.minutes, Math.round(minutes)) || //about 1 hour
minutes < options.offsets.oneHour && sprintf(t.hour, 1) || //about x hours
hours < options.offsets.xHours && sprintf(t.hours, Math.round(hours)) || //about 1 day
hours < options.offsets.oneDay && sprintf(t.day, 1) || //about x days
days < options.offsets.xDays && sprintf(t.days, Math.floor(days)) || //about 1 month
days < options.offsets.oneMonth && sprintf(t.month, 1) || //about x months
days < options.offsets.xMonths && sprintf(t.months, Math.floor(days / 30)) || //about 1 year
years < options.offsets.oneYear && sprintf(t.year, 1) || //about x years
sprintf(t.years, Math.floor(years));

return prefix + " " + fuzzy + " " + suffix;
} else {
return dateObject.format(defaultReturnFormat);
}
};

/**
* Parses the custom options
* @param config
*/
var processOptions = function(config) {
var o = defaultConfig();
var options = {};
Ext.apply(options, config, o);

return options;
};

//public start
return{
options: null,
items: [],

/**
* Constructor
* @param config
*/
init : function (config) {
this.options = processOptions(config);
this.refresh();
},

/**
* Returns the translated date
* @param str
* @param options
*/
translate : function(str, options) {
var d = parseDate(str, options.dateFormats);
if (d) {
return (dateTimeToString(d, options));
} else {
if (options.onErrorWriteTitle) {
return (str);
}
}
return false;
},

/**
* Refreshes the relative time of all the Components matching the itemSelector
*/
refresh: function() {
Ext.select(this.options.itemSelector).each(function(el, c, idx) {
el.update(this.translate(el.dom.title, this.options));
}, this);

if (this.options.refreshInterval > 0) {
Ext.defer(this.refresh, this.options.refreshInterval * 1000, this);
}
},

/**
* Translates the date contained in a specific Component, identified by its id
* @param domId
* @param config
*/
applyTo: function (domId, config) {
var options = processOptions(config),
el = Ext.get(domId);

el.update(this.translate(el.dom.title, options));

if (options.refreshInterval > 0) {
Ext.defer(this.applyTo, options.refreshInterval * 1000, this, [domId, config]);
}
}

};//public -- end
};