From a673f6ed56a3e8d034097eea765c133b3009790e Mon Sep 17 00:00:00 2001 From: Alexander Machehin Date: Sun, 1 Sep 2013 18:06:42 +0600 Subject: [PATCH] add time ago --- .../javascripts/angularjs/angular-moment.js | 82 ++++++++++++ .../javascripts/angularjs/config.js.erb | 2 +- app/assets/javascripts/application.js | 9 ++ app/assets/javascripts/extra/tracker.js | 1 + app/helpers/application_helper.rb | 6 + app/helpers/wiki_helper.rb | 2 +- app/presenters/comment_presenter.rb | 2 +- .../commit_as_message_presenter.rb | 2 +- app/views/layouts/application.html.haml | 3 +- .../_product_build_list.html.haml | 2 +- .../projects/build_lists/index.html.haml | 2 +- .../projects/build_lists/index.json.jbuilder | 3 +- app/views/projects/build_lists/show.html.haml | 4 +- .../projects/build_lists/show.json.jbuilder | 3 +- .../git/commits/_commits_small.html.haml | 3 +- app/views/projects/git/trees/_show.html.haml | 4 +- app/views/projects/issues/_header.html.haml | 2 +- app/views/projects/issues/_issue.html.haml | 2 +- app/views/projects/issues/_status.html.haml | 2 +- app/views/projects/wiki/_history.html.haml | 2 +- app/views/projects/wiki/_page.html.haml | 3 +- app/views/shared/_feed_message.html.haml | 2 +- vendor/assets/javascripts/moment/ru.js | 125 ++++++++++++++++++ 23 files changed, 245 insertions(+), 23 deletions(-) create mode 100644 app/assets/javascripts/angularjs/angular-moment.js create mode 100644 vendor/assets/javascripts/moment/ru.js diff --git a/app/assets/javascripts/angularjs/angular-moment.js b/app/assets/javascripts/angularjs/angular-moment.js new file mode 100644 index 000000000..939feb3fa --- /dev/null +++ b/app/assets/javascripts/angularjs/angular-moment.js @@ -0,0 +1,82 @@ +/* angular-moment.js / v0.2.0 / (c) 2013 Uri Shaked / MIT Licence */ + +angular.module('angularMoment', []) + .directive('amTimeAgo', ['$window', '$timeout', function ($window, $timeout) { + 'use strict'; + + return function (scope, element, attr) { + var activeTimeout = null; + var currentValue; + var currentFormat; + + function cancelTimer() { + if (activeTimeout) { + $timeout.cancel(activeTimeout); + activeTimeout = null; + } + } + + function updateTime(momentInstance) { + element.text(momentInstance.fromNow()); + var howOld = $window.moment().diff(momentInstance, 'minute'); + var secondsUntilUpdate = 3600; + if (howOld < 1) { + secondsUntilUpdate = 1; + } else if (howOld < 60) { + secondsUntilUpdate = 30; + } else if (howOld < 180) { + secondsUntilUpdate = 300; + } + + activeTimeout = $timeout(function () { + updateTime(momentInstance); + }, secondsUntilUpdate * 1000, false); + } + + function updateMoment() { + cancelTimer(); + updateTime($window.moment(currentValue, currentFormat)); + } + + scope.$watch(attr.amTimeAgo, function (value) { + if (typeof value === 'undefined' || value === null) { + return; + } + + if (angular.isNumber(value)) { + // Milliseconds since the epoch + value = new Date(value); + } + // else assume the given value is already a date + + currentValue = value; + updateMoment(); + }); + + attr.$observe('amFormat', function (format) { + currentFormat = format; + updateMoment(); + }); + + scope.$on('$destroy', function () { + cancelTimer(); + }); + }; + }]) + .filter('amDateFormat', ['$window', function ($window) { + 'use strict'; + + return function (value, format) { + if (typeof value === 'undefined' || value === null) { + return ''; + } + + if (angular.isNumber(value)) { + // Milliseconds since the epoch + value = new Date(value); + } + // else assume the given value is already a date + + return $window.moment(value).format(format); + }; + }]); \ No newline at end of file diff --git a/app/assets/javascripts/angularjs/config.js.erb b/app/assets/javascripts/angularjs/config.js.erb index 5cec60c56..f2fcf9ad9 100644 --- a/app/assets/javascripts/angularjs/config.js.erb +++ b/app/assets/javascripts/angularjs/config.js.erb @@ -1,4 +1,4 @@ -var RosaABF = angular.module('RosaABF', ['ngResource', 'ng-rails-csrf', 'angular-i18n']); +var RosaABF = angular.module('RosaABF', ['ngResource', 'ng-rails-csrf', 'angular-i18n', 'angularMoment']); var DateTimeFormatter = function() { diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index be4c26ff4..8b5bc3e8a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -90,4 +90,13 @@ $(document).ready(function() { } $('.md_and_cm code').each(function (code) { CodeMirrorRun(this); }); + + window.updateTime = function () { + $('.datetime_moment').each(function() { + $(this).html(moment($(this).attr('origin_datetime'), 'X').fromNow()); + }); + }; + + updateTime(); + setInterval( updateTime, 15000 ); }); diff --git a/app/assets/javascripts/extra/tracker.js b/app/assets/javascripts/extra/tracker.js index 66346cb27..94fe055d7 100644 --- a/app/assets/javascripts/extra/tracker.js +++ b/app/assets/javascripts/extra/tracker.js @@ -107,6 +107,7 @@ $(document).ready(function() { success: function(data){ $('article').html(data); $(".niceRadio").each(function() { changeRadioStart(jQuery(this)) }); + updateTime(); }, error: function(data){ alert('error') // TODO remove diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7fc3880a6..dee4d7339 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -48,4 +48,10 @@ module ApplicationHelper def short_message(message, length = 42) truncate(message, :length => length, :omission => '…') end + + def datetime_moment(date, options = {}) + tag = options[:tag] || :div + klass = "datetime_moment #{options[:class]}" + content_tag(tag, nil, :class => klass, :title => date.strftime('%Y-%m-%d %H:%M:%S UTC'), :origin_datetime => date.to_i) + end end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 98bf7f61f..9370be07c 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -98,7 +98,7 @@ module WikiHelper end def date - @page.version.authored_date.strftime("%Y-%m-%d %H:%M:%S") + @page.version.authored_date end def format diff --git a/app/presenters/comment_presenter.rb b/app/presenters/comment_presenter.rb index 10fb04f81..878d2437d 100644 --- a/app/presenters/comment_presenter.rb +++ b/app/presenters/comment_presenter.rb @@ -83,7 +83,7 @@ class CommentPresenter < ApplicationPresenter end def date - @date ||= I18n.l(@comment.created_at, :format => :long) + @date ||= @comment.created_at end def comment_id? diff --git a/app/presenters/git_presenters/commit_as_message_presenter.rb b/app/presenters/git_presenters/commit_as_message_presenter.rb index b44f31478..2e0aa7046 100644 --- a/app/presenters/git_presenters/commit_as_message_presenter.rb +++ b/app/presenters/git_presenters/commit_as_message_presenter.rb @@ -50,7 +50,7 @@ class GitPresenters::CommitAsMessagePresenter < ApplicationPresenter end def date - @date ||= I18n.l(@committed_date || @authored_date, :format => :long) + @date ||= @committed_date || @authored_date end def expandable? diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index a5ef6f216..6cdcfb6b7 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -6,7 +6,8 @@ = stylesheet_link_tag "application" /[if lt IE 9] = javascript_include_tag 'https://html5shiv.googlecode.com/svn/trunk/html5.js' - = javascript_include_tag "application" + = javascript_include_tag 'application' + = javascript_include_tag 'moment/ru.js' if I18n.locale == :ru = csrf_meta_tag = display_meta_tags :site => APP_CONFIG['project_name'], :reverse => true, :separator => '-' - if user_signed_in? diff --git a/app/views/platforms/product_build_lists/_product_build_list.html.haml b/app/views/platforms/product_build_lists/_product_build_list.html.haml index 3b1765fc1..dd5bd8107 100644 --- a/app/views/platforms/product_build_lists/_product_build_list.html.haml +++ b/app/views/platforms/product_build_lists/_product_build_list.html.haml @@ -10,4 +10,4 @@ %td= link_to(nil, pbl.container_path) unless pbl.project %td= link_to pbl.product.name, platform_product_path(platform, product) %td= link_to image_tag('x.png'), platform_product_product_build_list_path(platform, product, pbl), :method => :delete, :confirm => t("layout.confirm") if can?(:destroy, pbl) && pbl.can_destroy? && !pbl.project - %td= l(pbl.updated_at, :format => :long) \ No newline at end of file + = datetime_moment pbl.updated_at, :tag => :td \ No newline at end of file diff --git a/app/views/projects/build_lists/index.html.haml b/app/views/projects/build_lists/index.html.haml index d53699cdf..e24b6b7ea 100644 --- a/app/views/projects/build_lists/index.html.haml +++ b/app/views/projects/build_lists/index.html.haml @@ -64,7 +64,7 @@ %a{'ng-href' => '{{bl.user.url}}' } {{bl.user.fullname}} / updated_at - %td {{bl.updated_at}} + %td{'am-time-ago' => 'bl.updated_at', :title => '{{bl.updated_at_utc}}'} .both diff --git a/app/views/projects/build_lists/index.json.jbuilder b/app/views/projects/build_lists/index.json.jbuilder index 47fa83883..15ce57a8b 100644 --- a/app/views/projects/build_lists/index.json.jbuilder +++ b/app/views/projects/build_lists/index.json.jbuilder @@ -10,7 +10,8 @@ json.build_lists @build_lists do |build_list| end json.version_release get_version_release(build_list) - json.updated_at build_list.updated_at.strftime('%d/%m/%Y') + json.updated_at build_list.updated_at + json.updated_at_utc build_list.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') end json.dictionary do diff --git a/app/views/projects/build_lists/show.html.haml b/app/views/projects/build_lists/show.html.haml index 9f80f0b08..eded79080 100644 --- a/app/views/projects/build_lists/show.html.haml +++ b/app/views/projects/build_lists/show.html.haml @@ -3,7 +3,7 @@ .notify.blue %div{:class => '{{build_list.status_color}}'} %p {{build_list.human_status | i18n}} - %p {{build_list.updated_at}} + %p{'am-time-ago' => 'build_list.updated_at', :title => '{{build_list.updated_at_utc}}'} .both =form_for @build_list, :url => publish_build_list_path(@build_list), :html => { :class => :form } do |f| %h3= t("layout.build_lists.main_data") @@ -65,7 +65,7 @@ .rightlist= @build_list.arch.name .both .leftlist= t("activerecord.attributes.build_list.updated_at") - .rightlist {{build_list.updated_at}} + .rightlist {{build_list.updated_at_utc}} .both .leftlist= t("activerecord.attributes.build_list.is_circle") .rightlist= t("layout.#{@build_list.is_circle?}_") diff --git a/app/views/projects/build_lists/show.json.jbuilder b/app/views/projects/build_lists/show.json.jbuilder index ede8d5cf3..49fb18855 100644 --- a/app/views/projects/build_lists/show.json.jbuilder +++ b/app/views/projects/build_lists/show.json.jbuilder @@ -1,7 +1,8 @@ json.build_list do json.(@build_list, :id, :container_status, :status) json.(@build_list, :update_type) - json.updated_at @build_list.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') + json.updated_at @build_list.updated_at + json.updated_at_utc @build_list.updated_at.strftime('%Y-%m-%d %H:%M:%S UTC') if !@build_list.in_work? && @build_list.started_at json.human_duration @build_list.human_duration diff --git a/app/views/projects/git/commits/_commits_small.html.haml b/app/views/projects/git/commits/_commits_small.html.haml index 3302753fb..dc4b84831 100644 --- a/app/views/projects/git/commits/_commits_small.html.haml +++ b/app/views/projects/git/commits/_commits_small.html.haml @@ -7,8 +7,7 @@ %tr %td %img{:height => 16, :alt => "avatar", :src => presenter.image} - %td.date - = presenter.date + = datetime_moment presenter.date, :tag => :td, :class => :date %td.name = presenter.header %td.subject diff --git a/app/views/projects/git/trees/_show.html.haml b/app/views/projects/git/trees/_show.html.haml index 90d2a6afb..88e88fe1f 100644 --- a/app/views/projects/git/trees/_show.html.haml +++ b/app/views/projects/git/trees/_show.html.haml @@ -41,9 +41,7 @@ .name= link_to(node.name, path || blob_path(*options), :class => 'files-see') - if commit - %td - %span{:style => "display: none;"}= date = commit.committed_date || commit.authored_date - = l(date, :format => :short) + = datetime_moment(commit.committed_date || commit.authored_date, :tag => :td) %td= commit.short_message %td= (commit.committer || commit.author).name - else diff --git a/app/views/projects/issues/_header.html.haml b/app/views/projects/issues/_header.html.haml index 549f3e6a4..b6895a9b3 100644 --- a/app/views/projects/issues/_header.html.haml +++ b/app/views/projects/issues/_header.html.haml @@ -3,7 +3,7 @@ .image =image_tag(avatar_url(@issue.user, :medium), :alt => 'avatar') if @issue.user .created - %span= I18n.l(@issue.created_at, :format => :long) + = datetime_moment @issue.created_at, :tag => :span - if @issue.user %span= t('layout.by') %span.name=link_to(@issue.user.fullname, user_path(@issue.user)) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index e020323b4..cb7cdc363 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -10,7 +10,7 @@ -prefix = @project ? '' : "#{issue.project.name}: " ="#{prefix} #{issue.title}".html_safe .smalltext - =I18n.l(issue.send(@sort == :created ? :created_at : :updated_at), :format => :long) + = datetime_moment issue.send(@sort == :created ? :created_at : :updated_at) =t("layout.by") if issue.user =link_to(issue.user.uname, user_path(issue.user)) if issue.user -issue.labels.each do |label| diff --git a/app/views/projects/issues/_status.html.haml b/app/views/projects/issues/_status.html.haml index 721ae5b1d..1bf7b4602 100644 --- a/app/views/projects/issues/_status.html.haml +++ b/app/views/projects/issues/_status.html.haml @@ -4,6 +4,6 @@ .state=t('layout.issues.status.closed') .text .avatar= image_tag(avatar_url(@issue.closer), :alt => 'avatar') - .name="#{@issue.closer.fullname} #{t('layout.issues.at')} #{I18n.l(@issue.closed_at, :format => :long)}" + .name="#{@issue.closer.fullname} #{datetime_moment(@issue.closed_at, :tag => :span)}".html_safe .both %br/ diff --git a/app/views/projects/wiki/_history.html.haml b/app/views/projects/wiki/_history.html.haml index 253717874..6b7c91127 100644 --- a/app/views/projects/wiki/_history.html.haml +++ b/app/views/projects/wiki/_history.html.haml @@ -16,7 +16,7 @@ %span.username= user.present? ? user.fullname : v.author.name .both %td.td3 - %span.wiki-gray= "#{l v.committed_date.to_date, :format => :long}:" + = datetime_moment v.committed_date, :tag => :span, :class => 'wiki-gray' = v.message - if @name = raw "[#{link_to v.id[0..6], versioned_project_wiki_path(@project, escaped_name, v.id), :title => t("wiki.view_commit")}]" diff --git a/app/views/projects/wiki/_page.html.haml b/app/views/projects/wiki/_page.html.haml index e7b51c2e0..e759c99ff 100644 --- a/app/views/projects/wiki/_page.html.haml +++ b/app/views/projects/wiki/_page.html.haml @@ -12,8 +12,7 @@ %p#last-edit = t("wiki.last_edited_by") %b= user_link_by_user User.where(:email => author_email).first - = time_ago_in_words date.to_time - 4.hours, true - = t("layout.time.ago") + = datetime_moment date, :tag => :span - unless action_name == 'preview' or cannot? :write, @project %p#delete-link = link_to project_wiki_path(@project, escaped_name), :method => :delete, :confirm => t("layout.confirm") do diff --git a/app/views/shared/_feed_message.html.haml b/app/views/shared/_feed_message.html.haml index f3c26aa68..c498b07a2 100644 --- a/app/views/shared/_feed_message.html.haml +++ b/app/views/shared/_feed_message.html.haml @@ -9,7 +9,7 @@ -#.imaged move up a line. %span.name= presenter.header .both - %span.date= presenter.date + = datetime_moment presenter.date, :tag => :span, :class => :date .both - if presenter.caption? %span.subject= presenter.caption diff --git a/vendor/assets/javascripts/moment/ru.js b/vendor/assets/javascripts/moment/ru.js new file mode 100644 index 000000000..ce93b6375 --- /dev/null +++ b/vendor/assets/javascripts/moment/ru.js @@ -0,0 +1,125 @@ +// moment.js language configuration +// language : russian (ru) +// author : Viktorminator : https://github.com/Viktorminator +// Author : Menelion Elensúle : https://github.com/Oire + +function plural(word, num) { + var forms = word.split('_'); + return num % 10 === 1 && num % 100 !== 11 ? forms[0] : (num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20) ? forms[1] : forms[2]); +} + +function relativeTimeWithPlural(number, withoutSuffix, key) { + var format = { + 'mm': 'минута_минуты_минут', + 'hh': 'час_часа_часов', + 'dd': 'день_дня_дней', + 'MM': 'месяц_месяца_месяцев', + 'yy': 'год_года_лет' + }; + if (key === 'm') { + return withoutSuffix ? 'минута' : 'минуту'; + } + else { + return number + ' ' + plural(format[key], +number); + } +} + +function monthsCaseReplace(m, format) { + var months = { + 'nominative': 'январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь'.split('_'), + 'accusative': 'января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря'.split('_') + }, + + nounCase = (/D[oD]? *MMMM?/).test(format) ? + 'accusative' : + 'nominative'; + + return months[nounCase][m.month()]; +} + +function weekdaysCaseReplace(m, format) { + var weekdays = { + 'nominative': 'воскресенье_понедельник_вторник_среда_четверг_пятница_суббота'.split('_'), + 'accusative': 'воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу'.split('_') + }, + + nounCase = (/\[ ?[Вв] ?(?:прошлую|следующую)? ?\] ?dddd/).test(format) ? + 'accusative' : + 'nominative'; + + return weekdays[nounCase][m.day()]; +} + +moment.lang('ru', { + months : monthsCaseReplace, + monthsShort : "янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"), + weekdays : weekdaysCaseReplace, + weekdaysShort : "вск_пнд_втр_срд_чтв_птн_сбт".split("_"), + weekdaysMin : "вс_пн_вт_ср_чт_пт_сб".split("_"), + longDateFormat : { + LT : "HH:mm", + L : "DD.MM.YYYY", + LL : "D MMMM YYYY г.", + LLL : "D MMMM YYYY г., LT", + LLLL : "dddd, D MMMM YYYY г., LT" + }, + calendar : { + sameDay: '[Сегодня в] LT', + nextDay: '[Завтра в] LT', + lastDay: '[Вчера в] LT', + nextWeek: function () { + return this.day() === 2 ? '[Во] dddd [в] LT' : '[В] dddd [в] LT'; + }, + lastWeek: function () { + switch (this.day()) { + case 0: + return '[В прошлое] dddd [в] LT'; + case 1: + case 2: + case 4: + return '[В прошлый] dddd [в] LT'; + case 3: + case 5: + case 6: + return '[В прошлую] dddd [в] LT'; + } + }, + sameElse: 'L' + }, + relativeTime : { + future : "через %s", + past : "%s назад", + s : "несколько секунд", + m : relativeTimeWithPlural, + mm : relativeTimeWithPlural, + h : "час", + hh : relativeTimeWithPlural, + d : "день", + dd : relativeTimeWithPlural, + M : "месяц", + MM : relativeTimeWithPlural, + y : "год", + yy : relativeTimeWithPlural + }, + + ordinal: function (number, period) { + switch (period) { + case 'M': + case 'd': + case 'DDD': + return number + '-й'; + case 'D': + return number + '-го'; + case 'w': + case 'W': + return number + '-я'; + default: + return number; + } + }, + + week : { + dow : 1, // Monday is the first day of the week. + doy : 7 // The week that contains Jan 1st is the first week of the year. + } +});