From c2cfbce9ce601fc52b17e77bc40e21ae2f18485f Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 11 Jun 2013 17:25:50 +1000 Subject: [PATCH] automatically updating times for posts on topic moved moment.js into localization file (we need to localize it) added helpers for date formatting use, moment().shortDate() moment().longDate() moment().shortDateNoYear() --- .../discourse/components/formatter.js | 27 ++++++++++++------- .../discourse/helpers/application_helpers.js | 4 +-- config/locales/client.en.yml | 3 --- config/locales/server.en.yml | 4 +++ .../external => lib/javascripts}/moment.js | 0 lib/js_locale_helper.rb | 17 +++++++++++- spec/javascripts/components/formatter_spec.js | 22 +++++++++++++-- spec/javascripts/spec.js | 10 ++++--- 8 files changed, 65 insertions(+), 22 deletions(-) rename {app/assets/javascripts/external => lib/javascripts}/moment.js (100%) diff --git a/app/assets/javascripts/discourse/components/formatter.js b/app/assets/javascripts/discourse/components/formatter.js index 891aed9daab..c7a28510674 100644 --- a/app/assets/javascripts/discourse/components/formatter.js +++ b/app/assets/javascripts/discourse/components/formatter.js @@ -4,15 +4,10 @@ Discourse.Formatter = (function(){ relativeAgeMedium, relativeAgeMediumSpan, longDate, toTitleCase, shortDate; - var shortDateNoYearFormat = Ember.String.i18n("dates.short_date_no_year"); - var longDateFormat = Ember.String.i18n("dates.long_date"); - var shortDateFormat = Ember.String.i18n("dates.short_date"); - shortDate = function(date){ - return moment(date).format(shortDateFormat); + return moment(date).shortDate(); }; - // http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript // TODO: locale support ? toTitleCase = function toTitleCase(str) @@ -23,14 +18,14 @@ Discourse.Formatter = (function(){ } longDate = function(dt) { - return moment(dt).format(longDateFormat); + return moment(dt).longDate(); }; updateRelativeAge = function(elems) { // jQuery .each elems.each(function(){ var $this = $(this); - $this.html(relativeAge(new Date($this.data('time')), $this.data('format'))); + $this.html(relativeAge(new Date($this.data('time')), {format: $this.data('format'), wrapInSpan: false})); }); }; @@ -38,7 +33,17 @@ Discourse.Formatter = (function(){ options = options || {}; var format = options.format || "tiny"; - return "" + relativeAge(date, options) + ""; + var append = ""; + + if(format === 'medium') { + append = " date' title='" + longDate(date); + if(options.leaveAgo) { + format = 'medium-with-ago'; + } + options.wrapInSpan = false; + } + + return "" + relativeAge(date, options) + ""; }; @@ -139,7 +144,7 @@ Discourse.Formatter = (function(){ if ((new Date()).getFullYear() !== date.getFullYear()) { displayDate = shortDate(date); } else { - displayDate = moment(date).format(shortDateNoYearFormat); + displayDate = moment(date).shortDateNoYear(); } } else { displayDate = relativeAgeMediumSpan(distance, leaveAgo); @@ -160,6 +165,8 @@ Discourse.Formatter = (function(){ return relativeAgeTiny(date, options); } else if (format === "medium") { return relativeAgeMedium(date, options); + } else if (format === 'medium-with-ago') { + return relativeAgeMedium(date, _.extend(options, {format: 'medium', leaveAgo: true})); } return "UNKNOWN FORMAT"; diff --git a/app/assets/javascripts/discourse/helpers/application_helpers.js b/app/assets/javascripts/discourse/helpers/application_helpers.js index d95b4fdd15f..37385404e4a 100644 --- a/app/assets/javascripts/discourse/helpers/application_helpers.js +++ b/app/assets/javascripts/discourse/helpers/application_helpers.js @@ -212,7 +212,7 @@ Handlebars.registerHelper('unboundAge', function(property, options) { Handlebars.registerHelper('editDate', function(property, options) { // autoupdating this is going to be painful var date = new Date(Ember.Handlebars.get(this, property, options)); - return new Handlebars.SafeString(Discourse.Formatter.relativeAge(date, {format: 'medium', leaveAgo: true, wrapInSpan: false})); + return new Handlebars.SafeString(Discourse.Formatter.autoUpdatingRelativeAge(date, {format: 'medium', leaveAgo: true, wrapInSpan: false})); }); /** @@ -286,6 +286,6 @@ Handlebars.registerHelper('date', function(property, options) { if (val) { date = new Date(val); } - return new Handlebars.SafeString(Discourse.Formatter.relativeAge(date, {format: 'medium', leaveAgo: leaveAgo})); + return new Handlebars.SafeString(Discourse.Formatter.autoUpdatingRelativeAge(date, {format: 'medium', leaveAgo: leaveAgo})); }); diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 0343ca8346f..1a57027aeee 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -8,9 +8,6 @@ en: js: dates: - short_date_no_year: "D MMM" - short_date: "D MMM, YYYY" - long_date: "MMMM D, YYYY h:mma" tiny: half_a_minute: "< 1m" less_than_x_seconds: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index c634369b255..537902f47aa 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -5,6 +5,10 @@ # http://yamllint.com/ en: + dates: + short_date_no_year: "D MMM" + short_date: "D MMM, YYYY" + long_date: "MMMM D, YYYY h:mma" time: formats: short: "%m-%d-%Y" diff --git a/app/assets/javascripts/external/moment.js b/lib/javascripts/moment.js similarity index 100% rename from app/assets/javascripts/external/moment.js rename to lib/javascripts/moment.js diff --git a/lib/js_locale_helper.rb b/lib/js_locale_helper.rb index 2fc948efc94..f08cae73a70 100644 --- a/lib/js_locale_helper.rb +++ b/lib/js_locale_helper.rb @@ -18,11 +18,26 @@ module JsLocaleHelper result = generate_message_format(message_formats, locale_str) result << "I18n.translations = #{translations.to_json};\n" - result << "I18n.locale = '#{locale_str}'\n" + result << "I18n.locale = '#{locale_str}';\n" + # loading moment here cause we must customize it + result << File.read("#{Rails.root}/lib/javascripts/moment.js") result << moment_locale(locale_str) + result << moment_formats result end + def self.moment_formats + result = "" + result << moment_format_function('short_date_no_year') + result << moment_format_function('short_date') + result << moment_format_function('long_date') + end + + def self.moment_format_function(name) + format = I18n.t("dates." << name) + result = "moment.fn.#{name.camelize(:lower)} = function(){ return this.format('#{format}'); };\n" + end + def self.moment_locale(locale_str) filename = Rails.root + "lib/javascript/moment_locale/#{locale_str}.js" if File.exists?(filename) diff --git a/spec/javascripts/components/formatter_spec.js b/spec/javascripts/components/formatter_spec.js index c543bf8c371..b0dcafdebc5 100644 --- a/spec/javascripts/components/formatter_spec.js +++ b/spec/javascripts/components/formatter_spec.js @@ -85,12 +85,22 @@ describe("Discourse.Formatter", function() { describe("autoUpdatingRelativeAge", function(){ it("can format dates", function(){ - var d = new Date(); + var d = moment().subtract('days',1).toDate(); var $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d)); - expect($elem.data('format')).toBe("tiny"); expect($elem.data('time')).toBe(d.getTime()); + + $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d,{format: 'medium', leaveAgo: true})); + expect($elem.data('format')).toBe("medium-with-ago"); + expect($elem.data('time')).toBe(d.getTime()); + expect($elem.attr('title')).toBe(moment(d).longDate()); + expect($elem.html()).toBe('1 day ago'); + + $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d,{format: 'medium'})); + expect($elem.data('format')).toBe("medium"); + expect($elem.data('time')).toBe(d.getTime()); + expect($elem.html()).toBe('1 day'); }); }); @@ -105,6 +115,14 @@ describe("Discourse.Formatter", function() { expect($elem.html()).toBe("2m"); + + d = new Date(); + $elem = $(Discourse.Formatter.autoUpdatingRelativeAge(d, {format: 'medium', leaveAgo: true})); + $elem.data('time', d.getTime() - 2 * 60 * 1000); + + Discourse.Formatter.updateRelativeAge($elem); + + expect($elem.html()).toBe("2 minutes ago"); }); }); }); diff --git a/spec/javascripts/spec.js b/spec/javascripts/spec.js index c3beb26bf65..e353b71a8ba 100644 --- a/spec/javascripts/spec.js +++ b/spec/javascripts/spec.js @@ -1,3 +1,4 @@ + //= require env //= require ../../app/assets/javascripts/preload_store.js @@ -13,16 +14,17 @@ //= require ../../app/assets/javascripts/external_production/ember.js //= require ../../app/assets/javascripts/external_production/group-helper.js +//= require ../../app/assets/javascripts/locales/i18n +//= require ../../app/assets/javascripts/locales/date_locales.js +//= require ../../app/assets/javascripts/discourse/helpers/i18n_helpers +//= require ../../app/assets/javascripts/locales/en +// // Pagedown customizations //= require ../../app/assets/javascripts/pagedown_custom.js // The rest of the externals //= require_tree ../../app/assets/javascripts/external -//= require ../../app/assets/javascripts/locales/i18n -//= require ../../app/assets/javascripts/locales/date_locales.js -//= require ../../app/assets/javascripts/discourse/helpers/i18n_helpers -//= require ../../app/assets/javascripts/locales/en //= require ../../app/assets/javascripts/discourse // Stuff we need to load first