diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 00000000..cb2efdd2 --- /dev/null +++ b/locales/en.json @@ -0,0 +1,42 @@ +{ + "ep_comments_page.comment" : "Comment", + "ep_comments_page.comments" : "Comments", + "ep_comments_page.cancel" : "Cancel", + "ep_comments_page.add_comment.title" : "Add new comment on selection", + "ep_comments_page.show_comments" : "Show Comments", + "ep_comments_page.comments_template.suggested_change" : "Suggested Change:", + "ep_comments_page.comments_template.accept_change" : "Accept Change", + "ep_comments_page.comments_template.suggest_change_from" : "Suggest change From:", + "ep_comments_page.comments_template.to" : "To:", + "ep_comments_page.comments_template.include_suggestion" : "Include suggested change", + "ep_comments_page.time.seconds_ago" : "{{count}} seconds ago", + "ep_comments_page.time.seconds_from_now" : "{{count}} seconds from now", + "ep_comments_page.time.one_minute_ago" : "1 minute ago", + "ep_comments_page.time.one_minute_from_now" : "1 minute from now", + "ep_comments_page.time.minutes_ago" : "{{count}} minutes ago", + "ep_comments_page.time.minutes_from_now" : "{{count}} minutes from now", + "ep_comments_page.time.one_hour_ago" : "1 hour ago", + "ep_comments_page.time.one_hour_from_now" : "1 hour from now", + "ep_comments_page.time.hours_ago" : "{{count}} hours ago", + "ep_comments_page.time.hours_from_now" : "{{count}} hours from now", + "ep_comments_page.time.yesterday" : "yesterday", + "ep_comments_page.time.tomorrow" : "tomorrow", + "ep_comments_page.time.days_ago" : "{{count}} days ago", + "ep_comments_page.time.days_from_now" : "{{count}} days from now", + "ep_comments_page.time.last_week" : "last week", + "ep_comments_page.time.next_week" : "next week", + "ep_comments_page.time.weeks_ago" : "{{count}} weeks ago", + "ep_comments_page.time.weeks_from_now" : "{{count}} weeks from now", + "ep_comments_page.time.last_month" : "last month", + "ep_comments_page.time.next_month" : "next month", + "ep_comments_page.time.months_ago" : "{{count}} months ago", + "ep_comments_page.time.months_from_now" : "{{count}} months from now", + "ep_comments_page.time.last_year" : "last year", + "ep_comments_page.time.next_year" : "next year", + "ep_comments_page.time.years_ago" : "{{count}} years ago", + "ep_comments_page.time.years_from_now" : "{{count}} years from now", + "ep_comments_page.time.last_century" : "last century", + "ep_comments_page.time.next_century" : "next century", + "ep_comments_page.time.centuries_ago" : "{{count}} centuries ago", + "ep_comments_page.time.centuries_from_now" : "{{count}} centuries from now" +} \ No newline at end of file diff --git a/package.json b/package.json index 6f845fb2..1cd97541 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ ], "dependencies": { "formidable": "*", - "socket.io-client": "*" + "socket.io-client": "*", + "expect": "*" }, "engines": { "node": "*" diff --git a/static/js/index.js b/static/js/index.js index 8933433b..dbfbb1f5 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -3,13 +3,14 @@ var _, $, jQuery; var $ = require('ep_etherpad-lite/static/js/rjquery').$; var _ = require('ep_etherpad-lite/static/js/underscore'); var padcookie = require('ep_etherpad-lite/static/js/pad_cookie').padcookie; +var prettyDate = require('ep_comments_page/static/js/timeFormat').prettyDate; var cssFiles = ['ep_comments_page/static/css/comment.css']; /************************************************************************/ /* ep_comments Plugin */ /************************************************************************/ -// Container +// Container function ep_comments(context){ this.container = null; this.padOuter = null; @@ -84,7 +85,7 @@ ep_comments.prototype.init = function(){ if( count_comments > padComment.length ) { window.setTimeout(function() { self.collectComments(); - + }, 9000); } }, 3000); @@ -94,13 +95,13 @@ ep_comments.prototype.init = function(){ }, 300); }); - // On click comment icon toolbar + // On click comment icon toolbar $('.addComment').on('click', function(e){ $('iframe[name="ace_outer"]').contents().find('#comments').show(); $('iframe[name="ace_outer"]').contents().find('#comments').addClass("active"); e.preventDefault(); // stops focus from being lost - // If a new comment box doesn't already exist - // Add a new comment and link it to the selection + // If a new comment box doesn't already exist + // Add a new comment and link it to the selection // $('iframe[name="ace_outer"]').contents().find('#sidediv').removeClass('sidedivhidden'); if (self.container.find('#newComment').length == 0) self.addComment(); // console.log("setting focus to .comment-content"); @@ -199,7 +200,7 @@ ep_comments.prototype.init = function(){ }; -// Insert comments container on element use for linenumbers +// Insert comments container on element use for linenumbers ep_comments.prototype.findContainers = function(){ var padOuter = $('iframe[name="ace_outer"]').contents(); this.padOuter = padOuter; @@ -258,10 +259,10 @@ ep_comments.prototype.collectComments = function(callback){ } else { var prevCommentPos = prevCommentElm.css('top'); var prevCommentHeight = prevCommentElm.innerHeight(); - + commentPos = parseInt(prevCommentPos) + prevCommentHeight + 30; } - + commentElm.css({ 'top': commentPos }); }); @@ -276,7 +277,7 @@ ep_comments.prototype.collectComments = function(callback){ // on hover we should show the reply option }).on("mouseout", ".sidebar-comment", function(e){ var commentId = e.currentTarget.id; - var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]'); + var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]'); inner.contents().find("head").append(""); // TODO this could potentially break ep_font_color }); @@ -347,7 +348,7 @@ ep_comments.prototype.removeComment = function(className, id){ // Insert comment container in sidebar ep_comments.prototype.insertContainer = function(){ - // Add comments + // Add comments $('iframe[name="ace_outer"]').contents().find("#outerdocbody").prepend('
'); this.container = this.padOuter.find('#comments'); }; @@ -399,7 +400,7 @@ ep_comments.prototype.insertNewComment = function(comment, callback){ },'getYofRep', true); }; -// Insert a comment node +// Insert a comment node ep_comments.prototype.insertComment = function(commentId, comment, index, isNew){ var template = (isNew === true) ? 'newCommentTemplate' : 'commentsTemplate'; var content = null; @@ -478,13 +479,13 @@ ep_comments.prototype.getCommentReplies = function (callback){ ep_comments.prototype.getCommentData = function (){ var data = {}; - // Insert comment data + // Insert comment data data.padId = this.padId; data.comment = {}; data.comment.author = clientVars.userId; data.comment.name = clientVars.userName; data.comment.timestamp = new Date().getTime(); - + // Si le client est Anonyme // In English please? :P if(data.comment.name === undefined){ @@ -494,7 +495,7 @@ ep_comments.prototype.getCommentData = function (){ return data; } -// Add a pad comment +// Add a pad comment ep_comments.prototype.addComment = function (callback){ var socket = this.socket; var data = this.getCommentData(); @@ -550,7 +551,7 @@ ep_comments.prototype.addComment = function (callback){ // Save comment socket.emit('addComment', data, function (commentId, comment){ comment.commentId = commentId; - + //callback(commentId); ace.callWithAce(function (ace){ // console.log('addComment :: ', commentId); @@ -630,7 +631,7 @@ ep_comments.prototype.pushComment = function(eventType, callback){ var hooks = { - // Init pad comments + // Init pad comments postAceInit: function(hook, context){ if(!pad.plugins) pad.plugins = {}; var Comments = new ep_comments(context); @@ -671,46 +672,6 @@ var hooks = { }; -function prettyDate(time){ - var time_formats = [ - [60, 'seconds', 1], // 60 - [120, '1 minute ago', '1 minute from now'], // 60*2 - [3600, 'minutes', 60], // 60*60, 60 - [7200, '1 hour ago', '1 hour from now'], // 60*60*2 - [86400, 'hours', 3600], // 60*60*24, 60*60 - [172800, 'yesterday', 'tomorrow'], // 60*60*24*2 - [604800, 'days', 86400], // 60*60*24*7, 60*60*24 - [1209600, 'last week', 'next week'], // 60*60*24*7*4*2 - [2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7 - [4838400, 'last month', 'next month'], // 60*60*24*7*4*2 - [29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4 - [58060800, 'last year', 'next year'], // 60*60*24*7*4*12*2 - [2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12 - [5806080000, 'last century', 'next century'], // 60*60*24*7*4*12*100*2 - [58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100 - ]; - /* - var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," ").replace(/^\s\s*/ /*rappel , '').replace(/\s\s*$/, ''); - if(time.substr(time.length-4,1)==".") time =time.substr(0,time.length-4); - */ - var seconds = (new Date - new Date(time)) / 1000; - var token = 'ago', list_choice = 1; - if (seconds < 0) { - seconds = Math.abs(seconds); - token = 'from now'; - list_choice = 2; - } - var i = 0, format; - while (format = time_formats[i++]) - if (seconds < format[0]) { - if (typeof format[2] == 'string') - return format[list_choice]; - else - return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token; - } - return time; -}; - exports.aceEditorCSS = hooks.aceEditorCSS; exports.postAceInit = hooks.postAceInit; exports.aceAttribsToClasses = hooks.aceAttribsToClasses; diff --git a/static/js/timeFormat.js b/static/js/timeFormat.js new file mode 100644 index 00000000..4f11816e --- /dev/null +++ b/static/js/timeFormat.js @@ -0,0 +1,85 @@ +var localizable = typeof html10n !== "undefined"; + +l10nKeys = { + "seconds" : "ep_comments_page.time.seconds", + "1 minute ago" : "ep_comments_page.time.one_minute_ago", + "1 minute from now" : "ep_comments_page.time.one_minute_from_now", + "minutes" : "ep_comments_page.time.minutes", + "1 hour ago" : "ep_comments_page.time.one_hour_ago", + "1 hour from now" : "ep_comments_page.time.one_hour_from_now", + "hours" : "ep_comments_page.time.hours", + "yesterday" : "ep_comments_page.time.yesterday", + "tomorrow" : "ep_comments_page.time.tomorrow", + "days" : "ep_comments_page.time.days", + "last week" : "ep_comments_page.time.last_week", + "next week" : "ep_comments_page.time.next_week", + "weeks" : "ep_comments_page.time.weeks", + "last month" : "ep_comments_page.time.last_month", + "next month" : "ep_comments_page.time.next_month", + "months" : "ep_comments_page.time.months", + "last year" : "ep_comments_page.time.last_year", + "next year" : "ep_comments_page.time.next_year", + "years" : "ep_comments_page.time.years", + "last century" : "ep_comments_page.time.last_century", + "next century" : "ep_comments_page.time.next_century", + "centuries" : "ep_comments_page.time.centuries" +} + +var time_formats = [ + [60, 'seconds', 1], // 60 + [120, '1 minute ago', '1 minute from now'], // 60*2 + [3600, 'minutes', 60], // 60*60, 60 + [7200, '1 hour ago', '1 hour from now'], // 60*60*2 + [86400, 'hours', 3600], // 60*60*24, 60*60 + [172800, 'yesterday', 'tomorrow'], // 60*60*24*2 + [604800, 'days', 86400], // 60*60*24*7, 60*60*24 + [1209600, 'last week', 'next week'], // 60*60*24*7*4*2 + [2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7 + [4838400, 'last month', 'next month'], // 60*60*24*7*4*2 + [29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4 + [58060800, 'last year', 'next year'], // 60*60*24*7*4*12*2 + [2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12 + [5806080000, 'last century', 'next century'], // 60*60*24*7*4*12*100*2 + [58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100 +]; + +function prettyDate(time){ + /* + var time = ('' + date_str).replace(/-/g,"/").replace(/[TZ]/g," ").replace(/^\s\s*/ /*rappel , '').replace(/\s\s*$/, ''); + if(time.substr(time.length-4,1)==".") time =time.substr(0,time.length-4); + */ + var seconds = (new Date - new Date(time)) / 1000; + var token = 'ago', + list_choice = 1, + l10n_appendix = '_ago'; + + if (seconds < 0) { + seconds = Math.abs(seconds); + token = 'from now'; + l10n_appendix = '_from_now'; + list_choice = 2; + } + + var i = 0, format; + while (format = time_formats[i++]) + if (seconds < format[0]) { + var count = Math.floor(seconds / format[2]); + var formatted_time; + if (localizable) { + var key = l10nKeys[format[list_choice]] + l10n_appendix; + formatted_time = html10n.get(key, { count: count }); + } + + // Wasn't able to localize properly the date, so use the default: + if (formatted_time === undefined) { + if (typeof format[2] == 'string') + formatted_time = format[list_choice]; + else + formatted_time = count + ' ' + format[1] + ' ' + token; + } + return formatted_time; + } + return time; +}; + +exports.prettyDate = prettyDate; diff --git a/static/tests/backend/specs/timeFormat.js b/static/tests/backend/specs/timeFormat.js new file mode 100644 index 00000000..bba3befb --- /dev/null +++ b/static/tests/backend/specs/timeFormat.js @@ -0,0 +1,161 @@ +var prettyDate = require("../../../js/timeFormat").prettyDate, + expect = require("expect"); + +describe('time formatting', function() { + it("returns '12 seconds ago' when time is 12 seconds in the past", function() { + expect(prettyDate(secondsInThePast(12))).toBe('12 seconds ago'); + }); + + it("returns '12 seconds from now' when time is 12 seconds in the future", function() { + expect(prettyDate(secondsInTheFuture(12))).toBe('12 seconds from now'); + }); + + it("returns '1 minute ago' when time is 75 seconds in the past", function() { + expect(prettyDate(secondsInThePast(75))).toBe('1 minute ago'); + }); + + it("returns '1 minute from now' when time is 75 seconds in the future", function() { + expect(prettyDate(secondsInTheFuture(75))).toBe('1 minute from now'); + }); + + it("returns '17 minute ago' when time is some seconds before 17 minutes in the past", function() { + expect(prettyDate(secondsInThePast(minutes(17) + 2))).toBe('17 minutes ago'); + }); + + it("returns '17 minute from now' when time is some seconds after 17 minutes in the future", function() { + expect(prettyDate(secondsInTheFuture(minutes(17) + 2))).toBe('17 minutes from now'); + }); + + it("returns '1 hour ago' when time is some seconds before 1 hour in the past", function() { + expect(prettyDate(secondsInThePast(hours(1) + 3))).toBe('1 hour ago'); + }); + + it("returns '1 hour from now' when time is some seconds after 1 hour in the future", function() { + expect(prettyDate(secondsInTheFuture(hours(1) + 3))).toBe('1 hour from now'); + }); + + it("returns '2 hours ago' when time is some seconds before 2 hours in the past", function() { + expect(prettyDate(secondsInThePast(hours(2) + 4))).toBe('2 hours ago'); + }); + + it("returns '2 hours from now' when time is some seconds after 2 hours in the future", function() { + expect(prettyDate(secondsInTheFuture(hours(2) + 4))).toBe('2 hours from now'); + }); + + it("returns 'yesterday' when time is some seconds before 24 hours in the past", function() { + expect(prettyDate(secondsInThePast(hours(24) + 5))).toBe('yesterday'); + }); + + it("returns 'tomorrow' when time is some seconds after 24 hours in the future", function() { + expect(prettyDate(secondsInTheFuture(hours(24) + 5))).toBe('tomorrow'); + }); + + it("returns '6 days ago' when time is some seconds before 6 days in the past", function() { + expect(prettyDate(secondsInThePast(days(6) + 6))).toBe('6 days ago'); + }); + + it("returns '6 days from now' when time is some seconds after 6 days in the future", function() { + expect(prettyDate(secondsInTheFuture(days(6) + 6))).toBe('6 days from now'); + }); + + it("returns 'last week' when time is some seconds before 7 days in the past", function() { + expect(prettyDate(secondsInThePast(days(7) + 7))).toBe('last week'); + }); + + it("returns 'next week' when time is some seconds after 7 days in the future", function() { + expect(prettyDate(secondsInTheFuture(days(7) + 7))).toBe('next week'); + }); + + it("returns '2 weeks ago' when time is some seconds before 2 weeks in the past", function() { + expect(prettyDate(secondsInThePast(weeks(2) + 8))).toBe('2 weeks ago'); + }); + + it("returns '2 weeks from now' when time is some seconds after 2 weeks in the future", function() { + expect(prettyDate(secondsInTheFuture(weeks(2) + 8))).toBe('2 weeks from now'); + }); + + it("returns 'last month' when time is some seconds before 4 weeks in the past", function() { + expect(prettyDate(secondsInThePast(weeks(4) + 9))).toBe('last month'); + }); + + it("returns 'next month' when time is some seconds after 4 weeks in the future", function() { + expect(prettyDate(secondsInTheFuture(weeks(4) + 9))).toBe('next month'); + }); + + it("returns '9 months ago' when time is some seconds before 9 months in the past", function() { + expect(prettyDate(secondsInThePast(months(9) + 10))).toBe('9 months ago'); + }); + + it("returns '9 months from now' when time is some seconds after 9 months in the future", function() { + expect(prettyDate(secondsInTheFuture(months(9) + 10))).toBe('9 months from now'); + }); + + it("returns 'last year' when time is some seconds before 12 months in the past", function() { + expect(prettyDate(secondsInThePast(months(12) + 11))).toBe('last year'); + }); + + it("returns 'next year' when time is some seconds after 12 months in the future", function() { + expect(prettyDate(secondsInTheFuture(months(12) + 11))).toBe('next year'); + }); + + it("returns '15 years ago' when time is some seconds before 15 years in the past", function() { + expect(prettyDate(secondsInThePast(years(15) + 12))).toBe('15 years ago'); + }); + + it("returns '15 years from now' when time is some seconds after 15 years in the future", function() { + expect(prettyDate(secondsInTheFuture(years(15) + 12))).toBe('15 years from now'); + }); + + it("returns 'last century' when time is some seconds before 100 years in the past", function() { + expect(prettyDate(secondsInThePast(years(100) + 13))).toBe('last century'); + }); + + it("returns 'next century' when time is some seconds after 100 years in the future", function() { + expect(prettyDate(secondsInTheFuture(years(100) + 13))).toBe('next century'); + }); + + it("returns '2 centuries ago' when time is some seconds before 2 centuries in the past", function() { + expect(prettyDate(secondsInThePast(centuries(2) + 14))).toBe('2 centuries ago'); + }); + + it("returns '2 centuries from now' when time is some seconds after 2 centuries in the future", function() { + expect(prettyDate(secondsInTheFuture(centuries(2) + 14))).toBe('2 centuries from now'); + }); + +}) + +function secondsInThePast(seconds) { + return Date.now() - seconds * 1000; +} + +function secondsInTheFuture(seconds) { + return Date.now() + seconds * 1000; +} + +function minutes(count) { + return 60 * count; +} + +function hours(count) { + return 60 * minutes(count); +} + +function days(count) { + return 24 * hours(count); +} + +function weeks(count) { + return 7 * days(count); +} + +function months(count) { + return 4 * weeks(count); +} + +function years(count) { + return 12 * months(count); +} + +function centuries(count) { + return 100 * years(count); +} diff --git a/templates/commentBarButtons.ejs b/templates/commentBarButtons.ejs index a9657499..bdc2067f 100644 --- a/templates/commentBarButtons.ejs +++ b/templates/commentBarButtons.ejs @@ -1,4 +1,4 @@Suggest change From:
+To:
-- +