add time ago

This commit is contained in:
Alexander Machehin 2013-09-01 18:06:42 +06:00
parent 5689e36890
commit a673f6ed56
23 changed files with 245 additions and 23 deletions

View File

@ -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);
};
}]);

View File

@ -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() {

View File

@ -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 );
});

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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?

View File

@ -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?

View File

@ -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?

View File

@ -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)
= datetime_moment pbl.updated_at, :tag => :td

View File

@ -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

View File

@ -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

View File

@ -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?}_")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -10,7 +10,7 @@
-prefix = @project ? '' : "<span style=\"color: #999;\">#{issue.project.name}</span>: "
="#{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|

View File

@ -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="<b>#{@issue.closer.fullname}</b> #{datetime_moment(@issue.closed_at, :tag => :span)}".html_safe
.both
%br/

View File

@ -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")}]"

View File

@ -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

View File

@ -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

125
vendor/assets/javascripts/moment/ru.js vendored Normal file
View File

@ -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.
}
});