New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

ep_comments_page

Package Overview
Dependencies
Maintainers
5
Versions
142
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ep_comments_page - npm Package Compare versions

Comparing version 0.0.35 to 0.1.0

static/tests/backend/specs/padCopy.js

110

commentManager.js

@@ -6,2 +6,3 @@ var _ = require('ep_etherpad-lite/static/js/underscore');

var readOnlyManager = require("ep_etherpad-lite/node/db/ReadOnlyManager.js");
var shared = require('./static/js/shared');

@@ -30,2 +31,28 @@ exports.getComments = function (padId, callback)

exports.deleteComment = function (padId, commentId, callback)
{
db.get('comments:' + padId, function(err, comments)
{
if(ERR(err, callback)) return;
// the entry doesn't exist so far, let's create it
if(comments == null) comments = {};
delete comments[commentId];
db.set("comments:" + padId, comments);
callback(padId, commentId);
});
};
exports.deleteComments = function (padId, callback)
{
db.remove('comments:' + padId, function(err)
{
if(ERR(err, callback)) return;
callback(null);
});
};
exports.addComment = function(padId, data, callback)

@@ -61,4 +88,4 @@ {

var commentIds = _.map(data, function(commentData) {
//create the new comment
var commentId = "c-" + randomString(16);
//if the comment was copied it already has a commentID, so we don't need create one
var commentId = commentData.commentId || shared.generateCommentId();

@@ -73,3 +100,2 @@ var comment = {

};
//add the entry for this pad

@@ -89,2 +115,20 @@ comments[commentId] = comment;

exports.copyComments = function(originalPadId, newPadID, callback)
{
//get the comments of original pad
db.get('comments:' + originalPadId, function(err, originalComments) {
if(ERR(err, callback)) return;
var copiedComments = _.mapObject(originalComments, function(thisComment, thisCommentId) {
// make sure we have different copies of the comment between pads
return _.clone(thisComment);
});
//save the comments on new pad
db.set('comments:' + newPadID, copiedComments);
callback(null);
});
};
exports.getCommentReplies = function (padId, callback){

@@ -109,2 +153,9 @@ // We need to change readOnly PadIds to Normal PadIds

exports.deleteCommentReplies = function (padId, callback){
db.remove('comment-replies:' + padId, function(err)
{
if(ERR(err, callback)) return;
callback(null);
});
};

@@ -168,2 +219,19 @@ exports.addCommentReply = function(padId, data, callback){

exports.copyCommentReplies = function(originalPadId, newPadID, callback){
//get the replies of original pad
db.get('comment-replies:' + originalPadId, function(err, originalReplies){
if(ERR(err, callback)) return;
var copiedReplies = _.mapObject(originalReplies, function(thisReply, thisReplyId) {
// make sure we have different copies of the reply between pads
return _.clone(thisReply);
});
//save the comment replies on new pad
db.set('comment-replies:' + newPadID, copiedReplies);
callback(null);
});
};
exports.changeAcceptedState = function(padId, commentId, state, callback){

@@ -210,1 +278,37 @@ // Given a comment we update that comment to say the change was accepted or reverted

}
exports.changeCommentText = function(padId, commentId, commentText, callback){
var commentTextIsNotEmpty = commentText.length > 0;
if(commentTextIsNotEmpty){
// Given a comment we update the comment text
// We need to change readOnly PadIds to Normal PadIds
var isReadOnly = padId.indexOf("r.") === 0;
if(isReadOnly){
readOnlyManager.getPadId(padId, function(err, rwPadId){
padId = rwPadId;
});
};
// If we're dealing with comment replies we need to a different query
var prefix = "comments:";
if(commentId.substring(0,7) === "c-reply"){
prefix = "comment-replies:";
}
//get the entry
db.get(prefix + padId, function(err, comments){
if(ERR(err, callback)) return;
//update the comment text
comments[commentId].text = commentText;
//save the comment updated back
db.set(prefix + padId, comments);
callback(null);
});
}else{// don't save comment text blank
callback(true);
}
}

6

comments.js

@@ -6,8 +6,2 @@

function padExists(padID){
padManager.doesPadExists(padID, function(err, exists){
return exists;
});
}
exports.getPadComments = function(padID, callback)

@@ -14,0 +8,0 @@ {

@@ -16,2 +16,4 @@ {

"hooks": {
"padRemove": "ep_comments_page/index",
"padCopy": "ep_comments_page/index",
"socketio": "ep_comments_page/index",

@@ -18,0 +20,0 @@ "expressCreateServer": "ep_comments_page/index",

@@ -8,3 +8,15 @@ var eejs = require('ep_etherpad-lite/node/eejs/');

var apiUtils = require('./apiUtils');
var _ = require('ep_etherpad-lite/static/js/underscore');
exports.padRemove = function(hook_name, context, callback) {
commentManager.deleteCommentReplies(context.padID, function() {
commentManager.deleteComments(context.padID, callback);
});
}
exports.padCopy = function(hook_name, context, callback) {
commentManager.copyComments(context.originalPad.id, context.destinationID, function() {
commentManager.copyCommentReplies(context.originalPad.id, context.destinationID, callback);
});
}
exports.handleMessageSecurity = function(hook_name, context, callback){

@@ -64,2 +76,11 @@ if(context.message && context.message.data && context.message.data.apool){

socket.on('deleteComment', function(data, callback) {
// delete the comment on the database
commentManager.deleteComment(data.padId, data.commentId, function (){
// Broadcast to all other users that this comment was deleted
socket.broadcast.to(data.padId).emit('commentDeleted', data.commentId);
});
});
socket.on('revertChange', function(data, callback) {

@@ -83,2 +104,32 @@ // Broadcast to all other users that this change was accepted.

socket.on('bulkAddComment', function (padId, data, callback) {
commentManager.bulkAddComments(padId, data, function(error, commentsId, comments){
socket.broadcast.to(padId).emit('pushAddCommentInBulk');
var commentWithCommentId = _.object(commentsId, comments); // {c-123:data, c-124:data}
callback(commentWithCommentId)
});
});
socket.on('bulkAddCommentReplies', function(padId, data, callback){
commentManager.bulkAddCommentReplies(padId, data, function (err, repliesId, replies){
socket.broadcast.to(padId).emit('pushAddCommentReply', repliesId, replies);
var repliesWithReplyId = _.zip(repliesId, replies);
callback(repliesWithReplyId);
});
});
socket.on('updateCommentText', function(data, callback) {
// Broadcast to all other users that the comment text was changed.
// Note that commentId here can either be the commentId or replyId..
var padId = data.padId;
var commentId = data.commentId;
var commentText = data.commentText;
commentManager.changeCommentText(padId, commentId, commentText, function(err) {
if(!err){
socket.broadcast.to(padId).emit('textCommentUpdated', commentId, commentText);
}
callback(err);
});
});
socket.on('addCommentReply', function (data, callback) {

@@ -85,0 +136,0 @@ var padId = data.padId;

@@ -5,15 +5,17 @@ {

"ep_comments_page.add_comment.title" : "Kommentar zur Auswahl hinzufügen",
"ep_comments_page.add_comment.hint" : "Bitte wählen Sie zuerst den zu kommentierenden Text aus",
"ep_comments_page.delete_comment.title" : "Diesen Kommentar löschen",
"ep_comments_page.show_comments" : "Kommentare anzeigen",
"ep_comments_page.comments_template.suggested_change" : "Vorgeschlagene Änderung:",
"ep_comments_page.comments_template.from" : "von:",
"ep_comments_page.comments_template.suggested_change" : "Vorgeschlagene Änderung",
"ep_comments_page.comments_template.from" : "von",
"ep_comments_page.comments_template.accept_change.value" : "Änderung akzeptieren",
"ep_comments_page.comments_template.revert_change.value" : "Änderung zurücknehmen",
"ep_comments_page.comments_template.suggested_change_from" : "Vorgeschlagene Änderung von:",
"ep_comments_page.comments_template.suggest_change_from" : "von:",
"ep_comments_page.comments_template.to" : "zu:",
"ep_comments_page.comments_template.suggested_change_from" : "Vorgeschlagene Änderung von",
"ep_comments_page.comments_template.suggest_change_from" : "von",
"ep_comments_page.comments_template.to" : "zu",
"ep_comments_page.comments_template.include_suggestion" : "Änderung vorschlagen",
"ep_comments_page.comments_template.comment.value" : "Kommentar",
"ep_comments_page.comments_template.cancel.value" : "Abbrechen",
"ep_comments_page.comments_template.reply_input_label":"antworten: (mit ENTER)",
"ep_comments_page.comments_template.reply.value": "Antworten",
"ep_comments_page.comments_template.reply.placeholder": "Antworten",
"ep_comments_page.time.seconds.past" : "vor {{count}} Sekunden",

@@ -20,0 +22,0 @@ "ep_comments_page.time.seconds.future" : "{{count}} Sekunden von jetzt an",

@@ -5,15 +5,18 @@ {

"ep_comments_page.add_comment.title" : "Add new comment on selection",
"ep_comments_page.add_comment.hint" : "Please first select the text to comment",
"ep_comments_page.delete_comment.title" : "Delete this comment",
"ep_comments_page.edit_comment.title" : "Edit this comment",
"ep_comments_page.show_comments" : "Show Comments",
"ep_comments_page.comments_template.suggested_change" : "Suggested Change:",
"ep_comments_page.comments_template.from" : "From:",
"ep_comments_page.comments_template.suggested_change" : "Suggested Change",
"ep_comments_page.comments_template.from" : "From",
"ep_comments_page.comments_template.accept_change.value" : "Accept Change",
"ep_comments_page.comments_template.revert_change.value" : "Revert Change",
"ep_comments_page.comments_template.suggested_change_from" : "Suggested change From:",
"ep_comments_page.comments_template.suggest_change_from" : "Suggest change From:",
"ep_comments_page.comments_template.to" : "To:",
"ep_comments_page.comments_template.suggested_change_from" : "Suggested change From",
"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.comments_template.comment.value" : "Comment",
"ep_comments_page.comments_template.cancel.value" : "Cancel",
"ep_comments_page.comments_template.reply_input_label":"Your Reply (hit ENTER to send)",
"ep_comments_page.comments_template.reply.value" : "Reply",
"ep_comments_page.comments_template.reply.placeholder" : "Reply",
"ep_comments_page.time.seconds.past" : "{{count}} seconds ago",

@@ -48,3 +51,5 @@ "ep_comments_page.time.seconds.future" : "{{count}} seconds from now",

"ep_comments_page.time.centuries.past" : "{{count}} centuries ago",
"ep_comments_page.time.centuries.future" : "{{count}} centuries from now"
"ep_comments_page.time.centuries.future" : "{{count}} centuries from now",
"ep_comments_page.comments_template.edit_comment.save" : "save",
"ep_comments_page.comments_template.edit_comment.cancel" :"cancel"
}

@@ -5,15 +5,17 @@ {

"ep_comments_page.add_comment.title" : "Annoter la sélection",
"ep_comments_page.add_comment.hint" : "Vous devez d'abord sélectionner un texte à annoter",
"ep_comments_page.delete_comment.title" : "Supprimer cette annotation",
"ep_comments_page.show_comments" : "Afficher les annotations",
"ep_comments_page.comments_template.suggested_change" : "Modification proposée :",
"ep_comments_page.comments_template.from" : "Remplacer :",
"ep_comments_page.comments_template.accept_change.value" : "Appliquer la modification",
"ep_comments_page.comments_template.revert_change.value" : "Annuler la modification",
"ep_comments_page.comments_template.suggested_change_from" : "Modification proposée par :",
"ep_comments_page.comments_template.suggest_change_from" : "Proposer une modification de :",
"ep_comments_page.comments_template.to" : "Par :",
"ep_comments_page.comments_template.suggested_change" : "Modification proposée",
"ep_comments_page.comments_template.from" : "Remplacer",
"ep_comments_page.comments_template.accept_change.value" : "Appliquer la proposition",
"ep_comments_page.comments_template.revert_change.value" : "Annuler la proposition",
"ep_comments_page.comments_template.suggested_change_from" : "Propose de remplacer",
"ep_comments_page.comments_template.suggest_change_from" : "Remplacer",
"ep_comments_page.comments_template.to" : "Par",
"ep_comments_page.comments_template.include_suggestion" : "Proposer une modification",
"ep_comments_page.comments_template.comment.value" : "Annotation",
"ep_comments_page.comments_template.cancel.value" : "Annuler",
"ep_comments_page.comments_template.reply_input_label":"Votre réponse (pressez ENTRÉE pour valider)",
"ep_comments_page.comments_template.reply.value":"Répondre",
"ep_comments_page.comments_template.reply.placeholder":"Répondre",
"ep_comments_page.time.seconds.past" : "il y a {{count}} secondes",

@@ -20,0 +22,0 @@ "ep_comments_page.time.seconds.future" : "dans {{count}} secondes",

@@ -5,15 +5,17 @@ {

"ep_comments_page.add_comment.title" : "Dodaj nowy komentarz do sekcji",
"ep_comments_page.add_comment.hint" : "Najpierw wybierz tekst do skomentowania",
"ep_comments_page.delete_comment.title" : "Usuń komentarz",
"ep_comments_page.show_comments" : "Pokaż komentarze",
"ep_comments_page.comments_template.suggested_change" : "Sugerowane zmiany:",
"ep_comments_page.comments_template.from" : "Od:",
"ep_comments_page.comments_template.suggested_change" : "Sugerowane zmiany",
"ep_comments_page.comments_template.from" : "Od",
"ep_comments_page.comments_template.accept_change.value" : "Zaakceptuj zmiany",
"ep_comments_page.comments_template.revert_change.value" : "Przywróc zmiany",
"ep_comments_page.comments_template.suggested_change_from" : "Sugerowana zmiana z:",
"ep_comments_page.comments_template.suggest_change_from" : "Zaproponuj zmiane z:",
"ep_comments_page.comments_template.to" : "Do:",
"ep_comments_page.comments_template.suggested_change_from" : "Sugerowana zmiana z",
"ep_comments_page.comments_template.suggest_change_from" : "Zaproponuj zmiane z",
"ep_comments_page.comments_template.to" : "Do",
"ep_comments_page.comments_template.include_suggestion" : "Dołącz sugestie",
"ep_comments_page.comments_template.comment.value" : "Komentarz",
"ep_comments_page.comments_template.cancel.value" : "Anuluj",
"ep_comments_page.comments_template.reply_input_label":"Odpowiedź (wciśnij ENTER aby wysłać)",
"ep_comments_page.comments_template.reply.value": "Odpowiedź",
"ep_comments_page.comments_template.reply.placeholder": "Odpowiedź",
"ep_comments_page.time.seconds.past" : "{{count}} sekund temu",

@@ -20,0 +22,0 @@ "ep_comments_page.time.seconds.future" : "{{count}} sekund od teraz",

@@ -5,15 +5,16 @@ {

"ep_comments_page.add_comment.title" : "Adicionar novo comentário ao texto selecionado",
"ep_comments_page.add_comment.hint" : "Por favor, selecione primeiro o texto para comentar",
"ep_comments_page.delete_comment.title" : "Apagar este comentário",
"ep_comments_page.edit_comment.title" : "Editar este comentário",
"ep_comments_page.show_comments" : "Mostrar Comentários",
"ep_comments_page.comments_template.suggested_change" : "Alteração Sugerida:",
"ep_comments_page.comments_template.from" : "De:",
"ep_comments_page.comments_template.accept_change.value" : "Aceitar Sugestão",
"ep_comments_page.comments_template.revert_change.value" : "Reverter Sugestão",
"ep_comments_page.comments_template.suggested_change_from" : "Alteração sugerida de:",
"ep_comments_page.comments_template.suggest_change_from" : "Sugerir alteração de:",
"ep_comments_page.comments_template.to" : "Para:",
"ep_comments_page.comments_template.suggested_change_from" : "Alteração sugerida de",
"ep_comments_page.comments_template.suggest_change_from" : "Sugerir alteração de",
"ep_comments_page.comments_template.to" : "Para",
"ep_comments_page.comments_template.include_suggestion" : "Incluir alteração sugerida",
"ep_comments_page.comments_template.comment.value" : "Comentário",
"ep_comments_page.comments_template.cancel.value" : "Cancelar",
"ep_comments_page.comments_template.reply_input_label":"Sua Resposta (clique ENTER para enviar)",
"ep_comments_page.comments_template.reply.value":"Responder",
"ep_comments_page.comments_template.reply.placeholder":"Responder",
"ep_comments_page.time.seconds.past" : "{{count}} segundos atrás",

@@ -48,3 +49,5 @@ "ep_comments_page.time.seconds.future" : "daqui a {{count}} segundos",

"ep_comments_page.time.centuries.past" : "{{count}} séculos atrás",
"ep_comments_page.time.centuries.future" : "daqui a {{count}} séculos"
"ep_comments_page.time.centuries.future" : "daqui a {{count}} séculos",
"ep_comments_page.comments_template.edit_comment.save" : "salvar",
"ep_comments_page.comments_template.edit_comment.cancel" :"cancelar"
}
{
"description": "Adds comments on sidebar and link it to the text. Support for Page View, requires ep_page_view",
"name": "ep_comments_page",
"version": "0.0.35",
"version": "0.1.0",
"author": {

@@ -27,2 +27,5 @@ "name": "Nicolas Lescop",

},
"devDependencies": {
"request" : "*"
},
"engines": {

@@ -29,0 +32,0 @@ "node": "*"

@@ -23,3 +23,3 @@ // Easier access to outter pad

var commentElm = getCommentsContainer().find('#'+ commentId);
commentElm.removeClass('mouseover');
commentElm.removeClass('full-display');

@@ -29,28 +29,46 @@ // hide even the comment title

getPadOuter().find('.comment-modal').hide();
var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]');
inner.contents().find("head .comment-style").remove();
getPadOuter().find('.comment-modal').removeClass('popup-show');
};
var hideOpenedComments = function() {
var openedComments = getCommentsContainer().find('.mouseover');
openedComments.removeClass('mouseover').hide();
getPadOuter().find('.comment-modal').hide();
}
var hideAllComments = function() {
getCommentsContainer().children().hide();
getCommentsContainer().find('.sidebar-comment').removeClass('full-display');
getPadOuter().find('.comment-modal').removeClass('popup-show');
}
var highlightComment = function(commentId, e){
var highlightComment = function(commentId, e, editorComment){
var container = getCommentsContainer();
var commentElm = container.find('#'+ commentId);
var commentsVisible = container.is(":visible");
if(commentsVisible) {
// sidebar view highlight
commentElm.addClass('mouseover');
var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]');
if (container.is(":visible")) {
// hide all other comments
container.find('.sidebar-comment').each(function() {
inner.contents().find("head .comment-style").remove();
$(this).removeClass('full-display')
});
// Then highlight new comment
commentElm.addClass('full-display');
// now if we apply a class such as mouseover to the editor it will go shitty
// so what we need to do is add CSS for the specific ID to the document...
// It's fucked up but that's how we do it..
var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]');
inner.contents().find("head").append("<style class='comment-style'>."+commentId+"{ color: #a7680c !important }</style>");
} else {
var commentElm = container.find('#'+ commentId);
// make a full copy of the html, including listeners
var commentElmCloned = commentElm.clone(true, true);
// before of appending clear the css (like top positionning)
commentElmCloned.attr('style', '');
// fix checkbox, because as we are duplicating the sidebar-comment, we lose unique input names
commentElmCloned.find('.label-suggestion-checkbox').click(function() {
$(this).siblings('input[type="checkbox"]').click();
})
// hovering comment view
getPadOuter().find('.comment-modal-comment').html(commentElm.html());
getPadOuter().find('.comment-modal-comment').html('').append(commentElmCloned);
var padInner = getPadOuter().find('iframe[name="ace_inner"]')
// get modal position

@@ -61,10 +79,19 @@ var containerWidth = getPadOuter().find('#outerdocbody').outerWidth(true);

var targetTop = $(e.target).offset().top;
if (editorComment) {
targetTop += parseInt(padInner.css('padding-top').split('px')[0])
targetTop += parseInt(padOuter.find('#outerdocbody').css('padding-top').split('px')[0])
} else {
// mean we are clicking from a comment Icon
var targetLeft = $(e.target).offset().left - 20;
}
// if positioning modal on target left will make part of the modal to be
// out of screen, we place it closer to the middle of the screen
if (targetLeft + modalWitdh > containerWidth) {
targetLeft = containerWidth - modalWitdh - 2;
targetLeft = containerWidth - modalWitdh - 25;
}
getPadOuter().find('.comment-modal').show().css({
left: targetLeft +"px",
top: targetTop + 25 +"px"
var editorCommentHeight = editorComment ? editorComment.outerHeight(true) : 30;
getPadOuter().find('.comment-modal').addClass('popup-show').css({
left: targetLeft + "px",
top: targetTop + editorCommentHeight +"px"
});

@@ -78,4 +105,3 @@ }

var commentElement = getPadOuter().find('#'+commentId);
var targetTop = baseTop - 5;
commentElement.css("top", targetTop+"px");
commentElement.css("top", baseTop+"px");

@@ -88,3 +114,3 @@ return commentElement;

var commentElement = getPadOuter().find('#'+commentId);
var expectedTop = (baseTop - 5) + "px";
var expectedTop = baseTop + "px";
return commentElement.css("top") === expectedTop;

@@ -104,3 +130,2 @@ }

exports.hideComment = hideComment;
exports.hideOpenedComments = hideOpenedComments;
exports.hideAllComments = hideAllComments;

@@ -107,0 +132,0 @@ exports.highlightComment = highlightComment;

@@ -10,17 +10,2 @@ var $ = require('ep_etherpad-lite/static/js/rjquery').$;

// Indicates if screen has enough space on right margin to display icons
var screenHasSpaceToDisplayIcons;
var screenHasSpaceForIcons = function() {
if (screenHasSpaceToDisplayIcons === undefined) calculateIfScreenHasSpaceForIcons();
return screenHasSpaceToDisplayIcons;
}
var calculateIfScreenHasSpaceForIcons = function() {
var firstElementOnPad = getPadInner().find("#innerdocbody > div").first();
var rightMargin = firstElementOnPad.css("margin-right");
screenHasSpaceToDisplayIcons = rightMargin !== "0px";
}
// Easier access to outer pad

@@ -63,8 +48,7 @@ var padOuter;

var highlightTargetTextOf = function(commentId) {
getPadInner().find("head").append("<style>."+commentId+"{ color:orange }</style>");
getPadInner().find("head").append("<style class='comment-style'>."+commentId+"{ color: #a7680c !important }</style>");
}
var removeHighlightOfTargetTextOf = function(commentId) {
getPadInner().find("head").append("<style>."+commentId+"{ color:black }</style>");
// TODO this could potentially break ep_font_color
var removeHighlightTargetText = function(commentId) {
getPadInner().find("head .comment-style").remove();
}

@@ -78,2 +62,3 @@

getPadOuter().find('#commentIcons').on("mouseover", ".comment-icon", function(e){
removeHighlightTargetText();
var commentId = targetCommentIdOf(e);

@@ -83,3 +68,3 @@ highlightTargetTextOf(commentId);

var commentId = targetCommentIdOf(e);
removeHighlightOfTargetTextOf(commentId);
removeHighlightTargetText();
}).on("click", ".comment-icon.active", function(e){

@@ -93,3 +78,3 @@ toggleActiveCommentIcon($(this));

// one comment box opened at a time
commentBoxes.hideOpenedComments();
commentBoxes.hideAllComments();
var allActiveIcons = getPadOuter().find('#commentIcons').find(".comment-icon.active");

@@ -101,18 +86,6 @@ toggleActiveCommentIcon(allActiveIcons);

var commentId = targetCommentIdOf(e);
commentBoxes.showComment(commentId, e);
commentBoxes.highlightComment(commentId, e);
});
}
// Listen to Page View enabling/disabling, to adjust #commentIcons position
var addListenersToPageView = function() {
$("#options-pageview").on("click", function() {
getPadOuter().find('#outerdocbody').toggleClass("pageViewDisabled");
});
// add class if Page View is disabled already
if(!$('#options-pageview').is(':checked')) {
getPadOuter().find('#outerdocbody').addClass("pageViewDisabled");
}
}
// Listen to clicks on the page to be able to close comment when clicking

@@ -164,7 +137,5 @@ // outside of it

getPadOuter().find("#sidediv").after('<div id="commentIcons"></div>');
adjustIconsForNewScreenSize();
getPadOuter().find("#comments").addClass('with-icons');
addListenersToCommentIcons();
addListenersToCloseOpenedComment();
addListenersToPageView();
}

@@ -178,3 +149,3 @@

var inlineComment = getPadInner().find(".comment."+commentId);
var top = inlineComment.get(0).offsetTop + 5;
var top = inlineComment.get(0).offsetTop;
var iconsAtLine = getOrCreateIconsContainerAt(top);

@@ -189,3 +160,3 @@ var icon = $('#commentIconTemplate').tmpl(comment);

// we're only doing something if icons will be displayed at all
if (!displayIcons() || !screenHasSpaceForIcons()) return;
if (!displayIcons()) return;

@@ -201,6 +172,6 @@ getPadOuter().find('#commentIcons').children().children().each(function(){

// we're only doing something if icons will be displayed at all
if (!displayIcons() || !screenHasSpaceForIcons()) return;
if (!displayIcons()) return;
var icon = getPadOuter().find('#icon-'+commentId);
var targetTop = baseTop+5;
var targetTop = baseTop;
var iconsAtLine = getOrCreateIconsContainerAt(targetTop);

@@ -220,3 +191,3 @@

// we're only doing something if icons will be displayed at all
if (!displayIcons() || !screenHasSpaceForIcons()) return false;
if (!displayIcons()) return false;

@@ -245,3 +216,3 @@ var iconClicked = getPadOuter().find('#commentIcons').find(".comment-icon.active");

if (!displayIcons() || !screenHasSpaceForIcons()) {
if (!displayIcons()) {
// if icons are not being displayed, we always show comments

@@ -257,17 +228,2 @@ shouldShowComment = true;

var adjustIconsForNewScreenSize = function() {
// we're only doing something if icons will be displayed at all
if (!displayIcons()) return;
// now that screen has a different size, we need to force calculation
// of flag used by screenHasSpaceForIcons() before calling the function
calculateIfScreenHasSpaceForIcons();
if (screenHasSpaceForIcons()) {
getPadOuter().find('#commentIcons').show();
} else {
getPadOuter().find('#commentIcons').hide();
}
}
// Indicates if event was on one of the elements that does not close comment (any of the comment icons)

@@ -285,3 +241,2 @@ var shouldNotCloseComment = function(e) {

exports.shouldShow = shouldShow;
exports.adjustIconsForNewScreenSize = adjustIconsForNewScreenSize;
exports.shouldNotCloseComment = shouldNotCloseComment;

@@ -1,24 +0,116 @@

exports.addTextOnClipboard = function(e, ace, padInner){
var commentIdOnSelection;
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var _ = require('ep_etherpad-lite/static/js/underscore');
var shared = require('./shared');
exports.addTextOnClipboard = function(e, ace, padInner, removeSelection, comments, replies){
var commentIdOnFirstPositionSelected;
var hasCommentOnSelection;
ace.callWithAce(function(ace) {
commentIdOnSelection = ace.ace_getCommentIdOnSelection();
commentIdOnFirstPositionSelected = ace.ace_getCommentIdOnFirstPositionSelected();
hasCommentOnSelection = ace.ace_hasCommentOnSelection();
});
// we check if all the selection is in the same comment, if so, we override the copy behavior
if (commentIdOnSelection) {
if(hasCommentOnSelection){
var commentsData;
var range = padInner.contents()[0].getSelection().getRangeAt(0);
var hiddenDiv = createHiddenDiv(range);
var html = getHtml(hiddenDiv);
// when the range selection is fully inside a tag, 'html' will have no HTML tag, so we have to
var rawHtml = createHiddenDiv(range);
var html = rawHtml;
var onlyTextIsSelected = selectionHasOnlyText(rawHtml);
// when the range selection is fully inside a tag, 'rawHtml' will have no HTML tag, so we have to
// build it. Ex: if we have '<span>ab<b>cdef</b>gh</span>" and user selects 'de', the value of
//'html' will be 'de', not '<b>de</b>'
if (selectionHasOnlyText(html, hiddenDiv)) {
html = buildHtmlToCopy(html, range);
e.originalEvent.clipboardData.setData('text/copyCommentId', commentIdOnSelection);
//'rawHtml' will be 'de', not '<b>de</b>'. As it is not possible to have two comments in the same text
// commentIdOnFirstPositionSelected is the commentId in this partial selection
if (onlyTextIsSelected) {
var textSelected = rawHtml[0].textContent;
html = buildHtmlToCopyWhenSelectionHasOnlyText(textSelected, range, commentIdOnFirstPositionSelected);
}
var commentIds = getCommentIds(html);
commentsData = buildCommentsData(html, comments);
var htmlToCopy = replaceCommentIdsWithFakeIds(commentsData, html)
commentsData = JSON.stringify(commentsData);
var replyData = getReplyData(replies, commentIds);
replyData = JSON.stringify(replyData);
e.originalEvent.clipboardData.setData('text/objectReply', replyData);
e.originalEvent.clipboardData.setData('text/objectComment', commentsData);
// here we override the default copy behavior
e.originalEvent.clipboardData.setData('text/html', html);
e.originalEvent.clipboardData.setData('text/html', htmlToCopy);
e.preventDefault();
// if it is a cut event we have to remove the selection
if(removeSelection){
padInner.contents()[0].execCommand("delete");
}
}
};
var getReplyData = function(replies, commentIds){
var replyData = {};
_.each(commentIds, function(commentId){
replyData = _.extend(getRepliesFromCommentId(replies, commentId), replyData);
});
return replyData;
};
var getRepliesFromCommentId = function(replies, commentId){
var repliesFromCommentID = {};
_.each(replies, function(reply, replyId){
if(reply.commentId === commentId){
repliesFromCommentID[replyId] = reply;
}
});
return repliesFromCommentID;
};
var buildCommentIdToFakeIdMap = function(commentsData){
var commentIdToFakeId = {};
_.each(commentsData, function(comment, fakeCommentId){
var commentId = comment.data.originalCommentId;
commentIdToFakeId[commentId] = fakeCommentId;
});
return commentIdToFakeId;
};
var replaceCommentIdsWithFakeIds = function(commentsData, html){
var commentIdToFakeId = buildCommentIdToFakeIdMap(commentsData);
_.each(commentIdToFakeId, function(fakeCommentId, commentId){
$(html).find("." + commentId).removeClass(commentId).addClass(fakeCommentId);
});
var htmlWithFakeCommentIds = getHtml(html);
return htmlWithFakeCommentIds;
};
var buildCommentsData = function(html, comments){
var commentsData = {};
var originalCommentIds = getCommentIds(html);
_.each(originalCommentIds, function(originalCommentId){
var fakeCommentId = generateFakeCommentId();
var comment = comments[originalCommentId];
comment.data.originalCommentId = originalCommentId;
commentsData[fakeCommentId] = comment;
});
return commentsData;
};
var generateFakeCommentId = function(){
var commentId = "fakecomment-" + randomString(16);
return commentId;
};
var getCommentIds = function(html){
var allSpans = $(html).find("span");
var commentIds = [];
_.each(allSpans, function(span){
var cls = $(span).attr('class');
var classCommentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(cls);
var commentId = (classCommentId) ? classCommentId[1] : false;
if(commentId){
commentIds.push(commentId);
}
});
var uniqueCommentIds = _.uniq(commentIds);
return uniqueCommentIds;
};
var createHiddenDiv = function(range){

@@ -35,11 +127,21 @@ var content = range.cloneContents();

var selectionHasOnlyText = function(html, hiddenDiv){
var selectionHasOnlyText = function(rawHtml){
var html = getHtml(rawHtml);
var htmlDecoded = htmlDecode(html);
var text = $(hiddenDiv).text();
var text = $(rawHtml).text();
return htmlDecoded === text;
};
var buildHtmlToCopy = function(html, range) {
var buildHtmlToCopyWhenSelectionHasOnlyText = function(text, range, commentId) {
var htmlWithSpans = buildHtmlWithTwoSpanTags(text, commentId);
var html = buildHtmlWithFormattingTagsOfSelection(htmlWithSpans, range);
var htmlToCopy = $.parseHTML("<div>" + html + "</div>");
return htmlToCopy;
};
var buildHtmlWithFormattingTagsOfSelection = function(html, range) {
var htmlOfParentNode = range.commonAncestorContainer.parentNode;
var tags = getTagsInSelection(htmlOfParentNode);
// this case happens when we got a selection with one or more styling (bold, italic, underline, strikethrough)

@@ -50,7 +152,19 @@ // applied in all selection in the same range. For example, <b><i><u>text</u></i></b>

}
var htmlToCopy = "<span class='comment'>" + html + "</span>";
return htmlToCopy;
};
return html;
}
// FIXME - Allow to copy a comment when user copies only one char
// This is a hack to preserve the comment classes when user pastes a comment. When user pastes a span like this
// <span class='comment c-124'>thing</span>, chrome removes the classes and keeps only the style of the class. With comments
// chrome keeps the background-color. To avoid this we create two spans. The first one, <span class='comment c-124'>thi</span>
// has the text until the last but one character and second one with the last character <span class='comment c-124'>g</span>.
// Etherpad does a good job joining the two spans into one after the paste is triggered.
var buildHtmlWithTwoSpanTags = function(text, commentId) {
var firstSpan = '<span class="comment ' + commentId + '">'+ text.slice(0, -1) + '</span>'; // text until before last char
var secondSpan = '<span class="comment ' + commentId + '">'+ text.slice(-1) + '</span>'; // last char
return firstSpan + secondSpan;
}
var buildOpenTags = function(tags){

@@ -76,3 +190,3 @@ var openTags = "";

var tag;
while($(htmlObject)[0].localName != "span"){
while($(htmlObject)[0].localName !== "span"){
var html = $(htmlObject).prop('outerHTML');

@@ -85,39 +199,55 @@ var stylingTagRegex = /<(b|i|u|s)>/.exec(html);

return tags;
}
exports.addCommentClasses = function(e){
var commentId = e.originalEvent.clipboardData.getData('text/copyCommentId');
var target = e.target;
if (commentId) {
// we need to wait the paste process finishes completely, otherwise we will not have the target to add the necessary classes
setTimeout(function() {
addCommentClassesOnline(target, commentId);
}, 0);
}
};
var addCommentClassesOnline = function (target, commentId) {
var pastingOnEmptyLine = isEmptyLine(target);
var targetElement;
if (pastingOnEmptyLine){
targetElement = $(target).parent();
}else{
targetElement = getTargetOnLineWithContent();
exports.saveCommentsAndReplies = function(e){
var comments = e.originalEvent.clipboardData.getData('text/objectComment');
var replies = e.originalEvent.clipboardData.getData('text/objectReply');
if(comments && replies) {
comments = JSON.parse(comments);
replies = JSON.parse(replies);
saveComments(comments);
saveReplies(replies);
}
targetElement.addClass(commentId).addClass('comment');
};
var saveComments = function(comments){
var commentsToSave = {};
var padId = clientVars.padId;
var getTargetOnLineWithContent = function() {
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]').contents();
var target = padInner.find("span[style='background-color: rgb(255, 250, 205);']");
return target;
var mapOriginalCommentsId = pad.plugins.ep_comments_page.mapOriginalCommentsId;
var mapFakeComments = pad.plugins.ep_comments_page.mapFakeComments;
_.each(comments, function(comment, fakeCommentId){
var commentData = buildCommentData(comment, fakeCommentId);
var newCommentId = shared.generateCommentId();
mapFakeComments[fakeCommentId] = newCommentId;
var originalCommentId = comment.data.originalCommentId;
mapOriginalCommentsId[originalCommentId] = newCommentId;
commentsToSave[newCommentId] = comment;
});
pad.plugins.ep_comments_page.saveCommentWithoutSelection(padId, commentsToSave);
};
// an empty line has only a <br>
var isEmptyLine = function(target) {
return $(target).is("br");
var saveReplies = function(replies){
var repliesToSave = {};
var padId = clientVars.padId;
var mapOriginalCommentsId = pad.plugins.ep_comments_page.mapOriginalCommentsId;
_.each(replies, function(reply, replyId){
var originalCommentId = reply.commentId;
// as the comment copied has got a new commentId, we set this id in the reply as well
reply.commentId = mapOriginalCommentsId[originalCommentId];
repliesToSave[replyId] = reply;
});
pad.plugins.ep_comments_page.saveCommentReplies(padId, repliesToSave);
};
var buildCommentData = function(comment, fakeCommentId){
var commentData = {};
commentData.padId = clientVars.padId;
commentData.comment = comment.data;
commentData.comment.commentId = fakeCommentId;
return commentData;
};
// copied from https://css-tricks.com/snippets/javascript/unescape-html-in-js/

@@ -130,10 +260,84 @@ var htmlDecode = function(input) {

exports.getCommentIdOnSelection = function() {
// here we find the comment id on a position [line, column]. This function is used to get the comment id
// of one line when there is ONLY text selected. E.g In the line with comment, <span class='comment...'>something</span>,
// and user copies the text 'omethin'. The span tags are not copied only the text. So as the comment is
// applied on the selection we get the commentId using the first position selected of the line.
// P.S: It's not possible to have two or more comments when there is only text selected, because for each comment
// created it's generated a <span> and to copy only the text it MUST NOT HAVE any tag on the selection
exports.getCommentIdOnFirstPositionSelected = function() {
var attributeManager = this.documentAttributeManager;
var rep = this.rep;
var selStartAttrib = _.object(attributeManager.getAttributesOnPosition(rep.selStart[0], rep.selStart[1])).comment;
var selEndAttrib = _.object(attributeManager.getAttributesOnPosition(rep.selEnd[0], rep.selEnd[1] - 1)).comment;
return selStartAttrib === selEndAttrib ? selStartAttrib : null;
var commentId = _.object(attributeManager.getAttributesOnPosition(rep.selStart[0], rep.selStart[1])).comment;
return commentId;
};
exports.hasCommentOnSelection = function() {
var hasComment;
var attributeManager = this.documentAttributeManager;
var rep = this.rep;
var firstLineOfSelection = rep.selStart[0];
var firstColumn = rep.selStart[1];
var lastColumn = rep.selEnd[1];
var lastLineOfSelection = rep.selEnd[0];
var selectionOfMultipleLine = hasMultipleLineSelected(firstLineOfSelection, lastLineOfSelection);
if(selectionOfMultipleLine){
hasComment = hasCommentOnMultipleLineSelection(firstLineOfSelection,lastLineOfSelection, rep, attributeManager);
}else{
hasComment = hasCommentOnLine(firstLineOfSelection, firstColumn, lastColumn, attributeManager)
}
return hasComment;
};
var hasCommentOnMultipleLineSelection = function(firstLineOfSelection, lastLineOfSelection, rep, attributeManager){
var foundLineWithComment = false;
for (var line = firstLineOfSelection; line <= lastLineOfSelection && !foundLineWithComment; line++) {
var firstColumn = getFirstColumnOfSelection(line, rep, firstLineOfSelection);
var lastColumn = getLastColumnOfSelection(line, rep, lastLineOfSelection);
var hasComment = hasCommentOnLine(line, firstColumn, lastColumn, attributeManager);
if (hasComment){
foundLineWithComment = true;
}
}
return foundLineWithComment;
}
var getFirstColumnOfSelection = function(line, rep, firstLineOfSelection){
return line !== firstLineOfSelection ? 0 : rep.selStart[1];
};
var getLastColumnOfSelection = function(line, rep, lastLineOfSelection){
var lastColumnOfSelection;
if (line !== lastLineOfSelection) {
lastColumnOfSelection = getLength(line, rep); // length of line
}else{
lastColumnOfSelection = rep.selEnd[1] - 1; //position of last character selected
}
return lastColumnOfSelection;
};
var hasCommentOnLine = function(lineNumber, firstColumn, lastColumn, attributeManager){
var foundCommentOnLine = false;
for (var column = firstColumn; column <= lastColumn && !foundCommentOnLine; column++) {
var commentId = _.object(attributeManager.getAttributesOnPosition(lineNumber, column)).comment;
if (commentId !== undefined){
foundCommentOnLine = true;
}
}
return foundCommentOnLine;
};
var hasMultipleLineSelected = function(firstLineOfSelection, lastLineOfSelection){
return firstLineOfSelection !== lastLineOfSelection;
};
var getLength = function(line, rep) {
var nextLine = line + 1;
var startLineOffset = rep.lines.offsetOfIndex(line);
var endLineOffset = rep.lines.offsetOfIndex(nextLine);
//lineLength without \n
var lineLength = endLineOffset - startLineOffset - 1;
return lineLength;
};

@@ -9,2 +9,3 @@ /* TODO:

var shared = require('./shared');
var $ = require('ep_etherpad-lite/static/js/rjquery').$;

@@ -20,8 +21,10 @@ var _ = require('ep_etherpad-lite/static/js/underscore');

var events = require('ep_comments_page/static/js/copyPasteEvents');
var getCommentIdOnSelection = events.getCommentIdOnSelection;
var getCommentIdOnFirstPositionSelected = events.getCommentIdOnFirstPositionSelected;
var hasCommentOnSelection = events.hasCommentOnSelection;
var browser = require('ep_etherpad-lite/static/js/browser');
var cssFiles = ['ep_comments_page/static/css/comment.css', 'ep_comments_page/static/css/commentIcon.css'];
var UPDATE_COMMENT_LINE_POSITION_EVENT = 'updateCommentLinePosition';
/************************************************************************/

@@ -48,11 +51,7 @@ /* ep_comments Plugin */

this.commentReplies = {};
this.mapFakeComments = [];
this.mapOriginalCommentsId = [];
this.shouldCollectComment = false;
this.init();
this.preCommentMarker = preCommentMark.init(this.ace);
// If we're on a read only pad then hide the ability to attempt to merge a suggestion
if(clientVars.readonly){
this.padInner.append(
"<style>.comment-changeTo-approve," +
".comment-reply-changeTo-approve{display:none;}</style>");
}
}

@@ -85,4 +84,5 @@

self.collectCommentReplies();
self.commentRepliesListen();
}
self.commentRepliesListen();
self.commentListen();
});

@@ -94,30 +94,3 @@

// console.log('pushComment', comment);
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
}, 9000);
}
}, 3000);
}
}, 1000);
}
}, 300);
self.collectCommentsAfterSomeIntervalsOfTime();
});

@@ -129,19 +102,12 @@

self.localizeExistingComments();
newComment.localizeNewCommentForm();
});
// When screen size changes (user changes device orientation, for example),
// we need to make sure all sidebar comments are on the correct place
newComment.waitForResizeToFinishThenCall(200, function() {
self.editorResized();
// Recalculate position when editor is resized
$('#settings input, #skin-variant-full-width').on('change', function(e) {
self.setYofComments();
});
// When Page View is enabled/disabled, we need to recalculate position of comments
$('#options-pageview').on('click', function(e) {
self.editorResized();
this.padInner.contents().on(UPDATE_COMMENT_LINE_POSITION_EVENT, function(e){
self.setYofComments();
});
// When Page Breaks are enabled/disabled, we need to recalculate position of comments
$('#options-pagebreaks').on('click', function(e) {
self.editorResized();
});
$(window).resize(_.debounce( function() { self.setYofComments() }, 100 ) );

@@ -154,99 +120,130 @@ // On click comment icon toolbar

// Import for below listener : we are using this.container.parent() so we include
// events on both comment-modal and sidebar
// Listen for events to delete a comment
// All this does is remove the comment attr on the selection
this.container.on("click", ".comment-delete", function(){
var commentId = $(this).parent().parent()[0].id;
this.container.parent().on("click", ".comment-delete", function(){
var commentId = $(this).closest('.comment-container')[0].id;
self.deleteComment(commentId);
})
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
var selector = "."+commentId;
var ace = self.ace;
ace.callWithAce(function(aceTop){
var repArr = aceTop.ace_getRepFromSelector(selector, padInner);
// rep is an array of reps.. I will need to iterate over each to do something meaningful..
$.each(repArr, function(index, rep){
// I don't think we need this nested call
ace.callWithAce(function (ace){
ace.ace_performSelectionChange(rep[0],rep[1],true);
ace.ace_setAttributeOnSelection('comment', 'comment-deleted');
// Note that this is the correct way of doing it, instead of there being
// a commentId we now flag it as "comment-deleted"
});
});
},'deleteCommentedSelection', true);
// dispatch event
self.socket.emit('deleteComment', {padId: self.padId, commentId: commentId}, function (){});
});
// Listen for include suggested change toggle
this.container.on("change", '.reply-suggestion-checkbox', function(){
if($(this).is(':checked')){
var commentId = $(this).parent().parent().parent()[0].id;
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
// Listen for events to edit a comment
// Here, it adds a form to edit the comment text
this.container.parent().on("click", ".comment-edit", function(){
var $commentBox = $(this).closest('.comment-container');
$commentBox.addClass('editing');
var currentString = padInner.contents().find("."+commentId).html();
$(this).parent().parent().find(".reply-comment-changeFrom-value").html(currentString);
$(this).parent().parent().find('.reply-suggestion').addClass("active");
}else{
$(this).parent().parent().find('.reply-suggestion').removeClass("active");
var textBox = self.findCommentText($commentBox).last();
// if edit form not already there
if (textBox.siblings('.comment-edit-form').length == 0) {
// add a form to edit the field
var data = {};
data.text = textBox.text();
var content = $("#editCommentTemplate").tmpl(data);
// localize the comment/reply edit form
commentL10n.localize(content);
// insert form
textBox.before(content);
}
});
// Create hover modal
$('iframe[name="ace_outer"]').contents().find("body")
.append("<div class='comment-modal'><p class='comment-modal-name'></p><p class='comment-modal-comment'></p></div>");
// DUPLICATE CODE REQUIRED FOR COMMENT REPLIES, see below for slightly different version
this.container.on("click", ".comment-reply-changeTo-approve > input", function(e){
// submit the edition on the text and update the comment text
this.container.parent().on("click", ".comment-edit-submit", function(e){
e.preventDefault();
e.stopPropagation();
var $commentBox = $(this).closest('.comment-container');
var $commentForm = $(this).closest('.comment-edit-form');
var commentId = $commentBox.data('commentid');
var commentText = $commentForm.find('.comment-edit-text').val();
var data = {};
data.commentId = $(this).parent().parent().parent().parent().parent()[0].id;
data.commentId = commentId;
data.padId = clientVars.padId;
data.commentText = commentText;
data.replyId = $(this).parent().parent().parent()[0].id;
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
self.socket.emit('updateCommentText', data, function (err){
if(!err) {
$commentForm.remove();
$commentBox.removeClass('editing');
self.updateCommentBoxText(commentId, commentText);
// Are we reverting a change?
var submitButton = $(this);
var isRevert = submitButton.hasClass("revert");
if(isRevert){
var newString = $(this).parent().parent().parent().contents().find(".comment-changeFrom-value").html();
}else{
var newString = $(this).parent().parent().parent().contents().find(".comment-changeTo-value").html();
}
// although the comment or reply was saved on the data base successfully, it needs
// to update the comment or comment reply variable with the new text saved
self.setCommentOrReplyNewText(commentId, commentText);
}
});
});
// Nuke all that aren't first lines of this comment
padInner.contents().find("."+data.commentId+":not(:first)").html("");
var padCommentContent = padInner.contents().find("."+data.commentId).first();
newString = newString.replace(/(?:\r\n|\r|\n)/g, '<br />');
// hide the edit form and make the comment author and text visible again
this.container.parent().on("click", ".comment-edit-cancel", function(e){
e.preventDefault();
e.stopPropagation();
var $commentBox = $(this).closest('.comment-container');
var textBox = self.findCommentText($commentBox).last();
textBox.siblings('.comment-edit-form').remove();
$commentBox.removeClass('editing');
});
// Write the new pad contents
$(padCommentContent).html(newString);
// Listen for include suggested change toggle
this.container.parent().on("change", '.suggestion-checkbox', function(){
var parentComment = $(this).closest('.comment-container');
var parentSuggest = $(this).closest('.comment-reply');
// We change commentId to replyId in the data object so it's properly processed by the server.. This is hacky
data.commentId = data.replyId;
if($(this).is(':checked')){
var commentId = parentComment.data('commentid');
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
if(isRevert){
// Tell all users this change was reverted
self.socket.emit('revertChange', data, function (){});
self.showChangeAsReverted(data.replyId);
var currentString = padInner.contents().find("."+commentId).html();
parentSuggest.find(".from-value").html(currentString);
parentSuggest.find('.suggestion').show();
}else{
// Tell all users this change was accepted
self.socket.emit('acceptChange', data, function (){});
// Update our own comments container with the accepted change
self.showChangeAsAccepted(data.replyId);
parentSuggest.find('.suggestion').hide();
}
});
// User accepts a change
this.container.on("submit", ".comment-changeTo-form", function(e){
// User accepts or revert a change
this.container.parent().on("submit", ".comment-changeTo-form", function(e){
e.preventDefault();
var data = self.getCommentData();
data.commentId = $(this).parent()[0].id;
var commentEl = $(this).closest('.comment-container');
data.commentId = commentEl.data('commentid');
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
var padInner = padOuter.find('iframe[name="ace_inner"]').contents();
// Are we reverting a change?
var submitButton = $(this).contents().find("input[type='submit']");
var isRevert = submitButton.hasClass("revert");
if(isRevert){
var newString = $(this).parent().contents().find(".comment-changeFrom-value").html();
}else{
var newString = $(this).parent().contents().find(".comment-changeTo-value").html();
}
var isRevert = commentEl.hasClass("change-accepted");
var newString = isRevert ? $(this).find(".from-value").html() : $(this).find(".to-value").html();
// In case of suggested change is inside a reply, the parentId is different from the commentId (=replyId)
var parentId = $(this).closest('.sidebar-comment').data('commentid');
// Nuke all that aren't first lines of this comment
padInner.contents().find("."+data.commentId+":not(:first)").html("");
padInner.find("."+parentId+":not(:first)").html("");
var padCommentContent = padInner.contents().find("."+data.commentId).first();
var padCommentSpan = padInner.find("."+parentId).first();
newString = newString.replace(/(?:\r\n|\r)/g, '<br />');
// Write the new pad contents
$(padCommentContent).html(newString);
padCommentSpan.html(newString);

@@ -260,78 +257,105 @@ if(isRevert){

self.socket.emit('acceptChange', data, function (){});
// Update our own comments container with the accepted change
self.showChangeAsAccepted(data.commentId);
}
// TODO: we need ace editor to commit the change so other people get it
// currently after approving or reverting, you need to do other thing on the pad
// for ace to commit
});
// is this even used? - Yes, it is!
this.container.on("submit", ".comment-reply", function(e){
// When input reply is focused we display more option
this.container.parent().on("focus", ".comment-content", function(e){
$(this).closest('.new-comment').addClass('editing');
});
// When we leave we reset the form option to its minimal (only input)
this.container.parent().on('mouseleave', ".comment-container", function(e) {
$(this).find('.suggestion-checkbox').prop('checked', false);
$(this).find('.new-comment').removeClass('editing');
});
// When a reply get submitted
this.container.parent().on("submit", ".new-comment", function(e){
e.preventDefault();
var data = self.getCommentData();
data.commentId = $(this).parent()[0].id;
data.reply = $(this).find(".comment-reply-input").val();
data.changeTo = $(this).find(".reply-comment-suggest-to").val() || null;
data.changeFrom = $(this).find(".reply-comment-changeFrom-value").text() || null;
data.commentId = $(this).closest('.comment-container').data('commentid');
data.reply = $(this).find(".comment-content").val();
data.changeTo = $(this).find(".to-value").val() || null;
data.changeFrom = $(this).find(".from-value").text() || null;
self.socket.emit('addCommentReply', data, function (){
// Append the reply to the comment
// console.warn("addCommentReplyEmit WE EXPECT REPLY ID", data);
$('iframe[name="ace_outer"]').contents().find('#'+data.commentId + ' > form.comment-reply .comment-reply-input').val("");
self.getCommentReplies(function(replies){
self.commentReplies = replies;
self.collectCommentReplies();
// Once the new reply is displayed, we clear the form
$('iframe[name="ace_outer"]').contents().find('.new-comment').removeClass('editing');
});
});
// On submit we should hide this suggestion no?
if($(this).parent().parent().find(".reply-suggestion-checkbox").is(':checked')){
$(this).parent().parent().find(".reply-suggestion-checkbox:checked").click();
$(this).parent().parent().find(".reply-comment-suggest-to").val("");
//Only uncheck checked boxes. TODO: is a cleanup operation. Should we do it here?
}
$(this).trigger('reset_reply');
});
this.container.parent().on("reset_reply", ".new-comment", function(e){
// Reset the form
$(this).find('.comment-content').val('');
$(this).find(':focus').blur();
$(this).find('.to-value').val('');
$(this).find('.suggestion-checkbox').prop('checked', false);
$(this).removeClass('editing');
});
// When click cancel reply
this.container.parent().on("click", ".btn-cancel-reply", function(e) {
$(this).closest('.new-comment').trigger('reset_reply')
});
// Enable and handle cookies
if (padcookie.getPref("comments") === false) {
self.container.removeClass("active");
self.padOuter.find('#comments, #commentIcons').removeClass("active");
$('#options-comments').attr('checked','unchecked');
$('#options-comments').attr('checked',false);
}else{
} else {
$('#options-comments').attr('checked','checked');
}
$('#options-comments').on('click', function() {
if($('#options-comments').is(':checked')) {
padcookie.setPref("comments", true);
self.container.addClass("active");
} else {
padcookie.setPref("comments", false);
self.container.removeClass("active");
}
$('#options-comments').on('change', function() {
$('#options-comments').is(':checked') ? enableComments() : disableComments();
});
// Check to see if we should show already..
if($('#options-comments').is(':checked')){
self.container.addClass("active");
function enableComments() {
padcookie.setPref("comments", true);
self.padOuter.find('#comments, #commentIcons').addClass("active");
$('body').addClass('comments-active')
$('iframe[name="ace_outer"]').contents().find('body').addClass('comments-active')
}
// Override copy, cut, paste events on Google chrome.
function disableComments() {
padcookie.setPref("comments", false);
self.padOuter.find('#comments, #commentIcons').removeClass("active");
$('body').removeClass('comments-active')
$('iframe[name="ace_outer"]').contents().find('body').removeClass('comments-active')
}
// Check to see if we should show already..
$('#options-comments').trigger('change');
// TODO - Implement to others browser like, Microsoft Edge, Opera, IE
// Override copy, cut, paste events on Google chrome and Mozilla Firefox.
// When an user copies a comment and selects only the span, or part of it, Google chrome
// does not copy the classes only the styles, for example:
// <comment><span>text to be copied</span></comment>
// <comment class='comment'><span>text to be copied</span></comment>
// As the comment classes are not only used for styling we have to add these classes when it pastes the content
// The same does not occur when the user selects more than the span, for example:
// text<comment><span>to be copied</span></comment>
if(browser.chrome){
// text<comment class='comment'><span>to be copied</span></comment>
if(browser.chrome || browser.firefox){
self.padInner.contents().on("copy", function(e) {
events.addTextOnClipboard(e, self.ace, self.padInner);
events.addTextOnClipboard(e, self.ace, self.padInner, false, self.comments, self.commentReplies);
});
self.padInner.contents().on("cut", function(e) {
events.addTextOnClipboard(e, self.ace, self.padInner);
// remove the selected text
self.padInner.contents()[0].execCommand("delete");
events.addTextOnClipboard(e, self.ace, self.padInner, true);
});
self.padInner.contents().on("paste", function(e) {
events.addCommentClasses(e);
events.saveCommentsAndReplies(e);
});

@@ -341,2 +365,46 @@ }

ep_comments.prototype.findCommentText = function($commentBox) {
var isReply = $commentBox.hasClass('sidebar-comment-reply')
if (isReply)
return $commentBox.find(".comment-text");
else
return $commentBox.find('.compact-display-content .comment-text, .full-display-content .comment-title-wrapper .comment-text');
}
// This function is useful to collect new comments on the collaborators
ep_comments.prototype.collectCommentsAfterSomeIntervalsOfTime = function() {
var self = this;
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padOuter = $('iframe[name="ace_outer"]').contents();
this.padOuter = padOuter;
this.padInner = padOuter.find('iframe[name="ace_inner"]');
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
var count_comments=0;
for(var key in self.comments) {count_comments++;}
var padComment = this.padInner.contents().find('.comment');
if( count_comments > padComment.length ) {
window.setTimeout(function() {
self.collectComments();
}, 9000);
}
}, 3000);
}
}, 1000);
}
}, 300);
}
// Insert comments container on element use for linenumbers

@@ -362,3 +430,2 @@ ep_comments.prototype.findContainers = function(){

var commentId = (classCommentId) ? classCommentId[1] : null;
if(!commentId){

@@ -385,12 +452,3 @@ // console.log("returning due to no comment id, probably due to a deleted comment");

self.insertComment(commentId, comment.data, it);
commentElm = container.find('#'+ commentId);
$(this).on('click', function(){
markerTop = $(this).position().top;
commentTop = commentElm.position().top;
containerTop = container.css('top');
container.css('top', containerTop - (commentTop - markerTop));
});
}
// localize comment element

@@ -413,34 +471,26 @@ commentL10n.localize(commentElm);

commentElm.css({ 'top': commentPos });
});
// Should we show "Revert" instead of "Accept"
// Comment Replies are NOT handled here..
if(comments[commentId]){
var showRevert = comments[commentId].data.changeAccepted;
}
// HOVER SIDEBAR COMMENT
var hideCommentTimer;
this.container.on("mouseover", ".sidebar-comment", function(e){
// highlight comment
clearTimeout(hideCommentTimer);
commentBoxes.highlightComment(e.currentTarget.id, e);
if(showRevert){
self.showChangeAsAccepted(commentId);
}
});
// now if we apply a class such as mouseover to the editor it will go shitty
// so what we need to do is add CSS for the specific ID to the document...
// It's fucked up but that's how we do it..
var padInner = this.padInner;
this.container.on("mouseover", ".sidebar-comment", function(e){
var commentId = e.currentTarget.id;
var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]');
inner.contents().find("head").append("<style>."+commentId+"{ color:orange }</style>");
// 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"]');
inner.contents().find("head").append("<style>."+commentId+"{ color:black }</style>");
// TODO this could potentially break ep_font_color
// do not hide directly the comment, because sometime the mouse get out accidently
hideCommentTimer = setTimeout(function() {
commentBoxes.hideComment(e.currentTarget.id);
},1000);
});
// HOVER OR CLICK THE COMMENTED TEXT IN THE EDITOR
// hover event
this.padInner.contents().on("mouseover", ".comment", function(e){
var commentId = self.commentIdOf(e);
commentBoxes.highlightComment(commentId, e);
if (container.is(':visible')) { // not on mobile
clearTimeout(hideCommentTimer);
var commentId = self.commentIdOf(e);
commentBoxes.highlightComment(commentId, e, $(this));
}
});

@@ -451,3 +501,3 @@

var commentId = self.commentIdOf(e);
commentBoxes.highlightComment(commentId, e);
commentBoxes.highlightComment(commentId, e, $(this));
});

@@ -457,6 +507,7 @@

var commentOpenedByClickOnIcon = commentIcons.isCommentOpenedByClickOnIcon();
// only closes comment if it was not opened by a click on the icon
if (!commentOpenedByClickOnIcon) {
self.closeOpenedComment(e);
if (!commentOpenedByClickOnIcon && container.is(':visible')) {
hideCommentTimer = setTimeout(function() {
self.closeOpenedComment(e);
}, 1000);
}

@@ -468,2 +519,3 @@ });

self.setYofComments();
if(callback) callback();
};

@@ -475,9 +527,9 @@

// we need to add listeners to the different iframes of the page
$(document).on("touchstart", function(e){
$(document).on("touchstart click", function(e){
self.closeOpenedCommentIfNotOnSelectedElements(e);
});
this.padOuter.find('html').on("touchstart", function(e){
this.padOuter.find('html').on("touchstart click", function(e){
self.closeOpenedCommentIfNotOnSelectedElements(e);
});
this.padInner.contents().find('html').on("touchstart", function(e){
this.padInner.contents().find('html').on("touchstart click", function(e){
self.closeOpenedCommentIfNotOnSelectedElements(e);

@@ -500,3 +552,2 @@ });

}
// All clear, can close the comment

@@ -512,5 +563,5 @@ this.closeOpenedComment(e);

var padComment = this.padInner.contents().find('.comment');
$.each(this.commentReplies, function(replyId, reply){
var commentId = reply.commentId;
// tell comment icon that this comment has 1+ replies

@@ -520,7 +571,7 @@ commentIcons.commentHasReply(commentId);

var existsAlready = $('iframe[name="ace_outer"]').contents().find('#'+replyId).length;
if(existsAlready){
return;
}
if(existsAlready) return;
reply.replyId = replyId;
reply.text = reply.text || ""
reply.date = prettyDate(reply.timestamp);
reply.formattedDate = new Date(reply.timestamp).toISOString();

@@ -531,8 +582,4 @@

commentL10n.localize(content);
$('iframe[name="ace_outer"]').contents().find('#'+commentId + ' .comment-reply-input-label').before(content);
// Should we show "Revert" instead of "Accept"
// Comment Replies ARE handled here..
if(reply.changeAccepted){
self.showChangeAsAccepted(replyId);
}
var repliesContainer = $('iframe[name="ace_outer"]').contents().find('#'+commentId + ' .comment-replies-container');
repliesContainer.append(content);
});

@@ -552,8 +599,9 @@ };

// Add comments
// Create hover modal
target.prepend("<div class='comment-modal popup'><div class='popup-content comment-modal-comment'></div></div>");
// Add comments side bar container
target.prepend('<div id="comments"></div>');
this.container = this.padOuter.find('#comments');
// Add newComments
newComment.insertContainers(target);
};

@@ -568,2 +616,3 @@

comment.commentId = commentId;
comment.reply = true;
content = $('#commentsTemplate').tmpl(comment);

@@ -593,3 +642,3 @@

var padInner = padOuter.find('iframe[name="ace_inner"]');
var inlineComments = padInner.contents().find(".comment");
var inlineComments = this.getFirstOcurrenceOfCommentIds();
var commentsToBeShown = [];

@@ -603,9 +652,15 @@

$.each(inlineComments, function(){
var y = this.offsetTop;
var commentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(this.className); // classname is the ID of the comment
var commentEle = padOuter.find('#'+commentId[1])
var topOffset = this.offsetTop;
topOffset += parseInt(padInner.css('padding-top').split('px')[0])
// topOffset += ($(this).height() + $(this).outerHeight(true)) / 2 - commentEle.height();
if(commentId) {
// adjust outer comment...
var commentEle = commentBoxes.adjustTopOf(commentId[1], y);
commentBoxes.adjustTopOf(commentId[1], topOffset);
// ... and adjust icons too
commentIcons.adjustTopOf(commentId[1], y);
commentIcons.adjustTopOf(commentId[1], topOffset);

@@ -623,27 +678,22 @@ // mark this comment to be displayed if it was visible before we start adjusting its position

// Make the adjustments after editor is resized (due to a window resize or
// enabling/disabling Page View)
ep_comments.prototype.editorResized = function() {
var self = this;
ep_comments.prototype.getFirstOcurrenceOfCommentIds = function(){
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]').contents();
var commentsId = this.getUniqueCommentsId(padInner);
var firstOcurrenceOfCommentIds = _.map(commentsId, function(commentId){
return padInner.find("." + commentId).first().get(0);
});
return firstOcurrenceOfCommentIds;
}
commentIcons.adjustIconsForNewScreenSize();
// We try increasing timeouts, to make sure user gets the response as fast as we can
setTimeout(function() {
if (!self.allCommentsOnCorrectYPosition()) self.adjustCommentPositions();
setTimeout(function() {
if (!self.allCommentsOnCorrectYPosition()) self.adjustCommentPositions();
setTimeout(function() {
if (!self.allCommentsOnCorrectYPosition()) self.adjustCommentPositions();
}, 1000);
}, 500);
}, 250);
ep_comments.prototype.getUniqueCommentsId = function(padInner){
var inlineComments = padInner.find(".comment");
var commentsId = _.map(inlineComments, function(inlineComment){
var commentId = /(?:^| )(c-[A-Za-z0-9]*)/.exec(inlineComment.className);
// avoid when it has a '.comment' that it has a fakeComment class 'fakecomment-123' yet.
if(commentId) return commentId[1];
});
return _.uniq(commentsId);
}
// Adjusts position on the screen for sidebar comments and comment icons
ep_comments.prototype.adjustCommentPositions = function(){
commentIcons.adjustIconsForNewScreenSize();
this.setYofComments();
}
// Indicates if all comments are on the correct Y position, and don't need to

@@ -692,3 +742,2 @@ // be adjusted

comment.data.formattedDate = new Date(comment.data.timestamp).toISOString();
commentElm.attr('title', comment.data.date);
}

@@ -710,6 +759,25 @@ });

comment.formattedDate = new Date(comment.timestamp).toISOString();
if (comments[commentId] == null) comments[commentId] = {};
comments[commentId].data = comment;
};
// commentReply = ['c-reply-123', commentDataObject]
// commentDataObject = {author:..., name:..., text:..., ...}
ep_comments.prototype.setCommentReply = function(commentReply){
var commentReplies = this.commentReplies;
var replyId = commentReply[0];
commentReplies[replyId] = commentReply[1];
};
// set the text of the comment or comment reply
ep_comments.prototype.setCommentOrReplyNewText = function(commentOrReplyId, text){
if(this.comments[commentOrReplyId]){
this.comments[commentOrReplyId].data.text = text;
}else if(this.commentReplies[commentOrReplyId]){
this.commentReplies[commentOrReplyId].text = text;
}
};
// Get all comments

@@ -753,22 +821,3 @@ ep_comments.prototype.getComments = function (callback){

ep_comments.prototype.deleteComment = function(commentId){
var padOuter = $('iframe[name="ace_outer"]').contents();
var padInner = padOuter.find('iframe[name="ace_inner"]');
var selector = "."+commentId;
var ace = this.ace;
ace.callWithAce(function(aceTop){
var repArr = aceTop.ace_getRepFromSelector(selector, padInner);
// rep is an array of reps.. I will need to iterate over each to do something meaningful..
$.each(repArr, function(index, rep){
// I don't think we need this nested call
ace.callWithAce(function (ace){
ace.ace_performSelectionChange(rep[0],rep[1],true);
ace.ace_setAttributeOnSelection('comment', 'comment-deleted');
// Note that this is the correct way of doing it, instead of there being
// a commentId we now flag it as "comment-deleted"
});
});
},'deleteCommentedSelection', true);
// });
// }, 'getRep');
$('iframe[name="ace_outer"]').contents().find('#' + commentId).remove();
}

@@ -793,2 +842,3 @@

if (noTextSelected) {
$.gritter.add({text: html10n.translations["ep_comments_page.add_comment.hint"] || "Please first select the text to comment"})
return;

@@ -800,7 +850,6 @@ }

// Write the text to the changeFrom form
var padOuter = $('iframe[name="ace_outer"]').contents();
padOuter.find(".comment-suggest-from").val(selectedText);
$('#newComment').find(".from-value").text(selectedText);
// Display form
newComment.showNewCommentForm();
newComment.showNewCommentPopup();

@@ -816,11 +865,3 @@ // Check if the first element selected is visible in the viewport

// Adjust focus on the form
padOuter.find('.comment-content').focus();
// fix for iOS: when opening #newComment, we need to force focus on padOuter
// contentWindow, otherwise keyboard will be displayed but text input made by
// the user won't be added to textarea
var outerIframe = $('iframe[name="ace_outer"]').get(0);
if (outerIframe && outerIframe.contentWindow) {
outerIframe.contentWindow.focus();
}
$('#newComment').find('.comment-content').focus();
}

@@ -879,3 +920,3 @@

ep_comments.prototype.checkNoTextSelected = function(rep) {
var noTextSelected = (rep.selStart[0] == rep.selEnd[0] && rep.selStart[1] == rep.selEnd[1]);
var noTextSelected = ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]));

@@ -891,3 +932,3 @@ return noTextSelected;

// If a new comment box doesn't already exist, create one
newComment.insertNewCommentFormIfDontExist(data, function(comment, index) {
newComment.insertNewCommentPopupIfDontExist(data, function(comment, index) {
if(comment.changeTo){

@@ -987,3 +1028,2 @@ data.comment.changeFrom = comment.changeFrom;

var self = this;
self.socket.emit('addComment', data, function (commentId, comment){

@@ -993,3 +1033,3 @@ comment.commentId = commentId;

self.ace.callWithAce(function (ace){
// console.log('addComment :: ', commentId);
// console.log('addComment :: ', rep, comment);
ace.ace_performSelectionChange(rep.selStart, rep.selEnd, true);

@@ -1004,2 +1044,95 @@ ace.ace_setAttributeOnSelection('comment', commentId);

// commentData = {c-newCommentId123: data:{author:..., date:..., ...}, c-newCommentId124: data:{...}}
ep_comments.prototype.saveCommentWithoutSelection = function (padId, commentData) {
var self = this;
var data = self.buildComments(commentData);
self.socket.emit('bulkAddComment', padId, data, function (comments){
self.setComments(comments);
self.shouldCollectComment = true
});
}
ep_comments.prototype.buildComments = function(commentsData){
var self = this;
var comments = _.map(commentsData, function(commentData, commentId){
return self.buildComment(commentId, commentData.data);
});
return comments;
}
// commentData = {c-newCommentId123: data:{author:..., date:..., ...}, ...
ep_comments.prototype.buildComment = function(commentId, commentData){
var data = {};
data.padId = this.padId;
data.commentId = commentId;
data.text = commentData.text;
data.changeTo = commentData.changeTo
data.changeFrom = commentData.changeFrom;
data.name = commentData.name;
data.timestamp = parseInt(commentData.timestamp);
return data;
}
ep_comments.prototype.getMapfakeComments = function(){
return this.mapFakeComments;
}
// commentReplyData = {c-reply-123:{commentReplyData1}, c-reply-234:{commentReplyData1}, ...}
ep_comments.prototype.saveCommentReplies = function(padId, commentReplyData){
var self = this;
var data = self.buildCommentReplies(commentReplyData);
self.socket.emit('bulkAddCommentReplies', padId, data, function (replies){
_.each(replies,function(reply){
self.setCommentReply(reply);
});
self.shouldCollectComment = true; // force collect the comment replies saved
});
}
ep_comments.prototype.buildCommentReplies = function(repliesData){
var self = this;
var replies = _.map(repliesData, function(replyData){
return self.buildCommentReply(replyData);
});
return replies;
}
// take a replyData and add more fields necessary. E.g. 'padId'
ep_comments.prototype.buildCommentReply = function(replyData){
var data = {};
data.padId = this.padId;
data.commentId = replyData.commentId;
data.text = replyData.text;
data.changeTo = replyData.changeTo
data.changeFrom = replyData.changeFrom;
data.replyId = replyData.replyId;
data.name = replyData.name;
data.timestamp = parseInt(replyData.timestamp);
return data;
}
// Listen for comment
ep_comments.prototype.commentListen = function(){
var self = this;
var socket = this.socket;
socket.on('pushAddCommentInBulk', function (){
self.getComments(function (allComments){
if (!$.isEmptyObject(allComments)){
// we get the comments in this format {c-123:{author:...}, c-124:{author:...}}
// but it's expected to be {c-123: {data: {author:...}}, c-124:{data:{author:...}}}
// in this.comments
var commentsProcessed = {};
_.map(allComments, function (comment, commentId) {
commentsProcessed[commentId] = {}
commentsProcessed[commentId].data = comment;
});
self.comments = commentsProcessed;
self.collectCommentsAfterSomeIntervalsOfTime(); // here we collect on the collaborators
}
});
});
};
// Listen for comment replies

@@ -1018,3 +1151,2 @@ ep_comments.prototype.commentRepliesListen = function(){

self.collectCommentReplies();
self.commentRepliesListen();
}

@@ -1026,2 +1158,8 @@ });

ep_comments.prototype.updateCommentBoxText = function (commentId, commentText) {
var $comment = this.container.parent().find("[data-commentid='" + commentId + "']");
var textBox = this.findCommentText($comment);
textBox.text(commentText)
}
ep_comments.prototype.showChangeAsAccepted = function(commentId){

@@ -1031,7 +1169,14 @@ var self = this;

// Get the comment
var comment = self.container.find("#"+commentId);
var button = comment.find("input[type='submit']").first(); // we need to get the first button otherwise the replies suggestions will be affected too
button.attr("data-l10n-id", "ep_comments_page.comments_template.revert_change.value");
button.addClass("revert");
commentL10n.localize(button);
var comment = this.container.parent().find("[data-commentid='" + commentId + "']");
// Revert other comment that have already been accepted
comment.closest('.sidebar-comment')
.find('.comment-container.change-accepted').addBack('.change-accepted')
.each(function() {
$(this).removeClass('change-accepted');
var data = {commentId: $(this).attr('data-commentid'), padId: self.padId}
self.socket.emit('revertChange', data, function (){});
})
// this comment get accepted
comment.addClass('change-accepted');
}

@@ -1041,9 +1186,5 @@

var self = this;
// Get the comment
var comment = self.container.find("#"+commentId);
var button = comment.find("input[type='submit']").first(); // we need to get the first button otherwise the replies suggestions will be affected too
button.attr("data-l10n-id", "ep_comments_page.comments_template.accept_change.value");
button.removeClass("revert");
commentL10n.localize(button);
var comment = self.container.parent().find("[data-commentid='" + commentId + "']");
comment.removeClass('change-accepted');
}

@@ -1056,2 +1197,10 @@

socket.on('textCommentUpdated', function (commentId, commentText) {
self.updateCommentBoxText(commentId, commentText);
})
socket.on('commentDeleted', function(commentId){
self.deleteComment(commentId);
});
socket.on('changeAccepted', function(commentId){

@@ -1072,9 +1221,2 @@ self.showChangeAsAccepted(commentId);

// On collaborator delete a comment in the current pad
else if (eventType == 'remove'){
socket.on('pushRemoveComment', function (commentId){
callback(commentId);
});
}
// On reply

@@ -1099,2 +1241,11 @@ else if (eventType == "addCommentReply"){

pad.plugins.ep_comments_page = Comments;
if (!$('#editorcontainerbox').hasClass('flex-layout')) {
$.gritter.add({
title: "Error",
text: "Ep_comments_page: Please upgrade to etherpad 1.8.3 for this plugin to work correctly",
sticky: true,
class_name: "error"
})
}
},

@@ -1113,8 +1264,12 @@

// padOuter.find('#sidediv').removeClass("sidedivhidden"); // TEMPORARY to do removing authorship colors can add sidedivhidden class to sidesiv!
if(!context.callstack.docTextChanged) return;
// only adjust comments if plugin was already initialized,
// otherwise there's nothing to adjust anyway
if (pad.plugins && pad.plugins.ep_comments_page) {
pad.plugins.ep_comments_page.setYofComments();
if(eventType == "setup" || eventType == "setBaseText" || eventType == "importText") return;
if(context.callstack.docTextChanged) pad.plugins.ep_comments_page.setYofComments();
var commentWasPasted = pad.plugins.ep_comments_page.shouldCollectComment;
var domClean = context.callstack.domClean;
// we have to wait the DOM update from a fakeComment 'fakecomment-123' to a comment class 'c-123'
if(commentWasPasted && domClean){
pad.plugins.ep_comments_page.collectComments(function(){
pad.plugins.ep_comments_page.collectCommentReplies();
pad.plugins.ep_comments_page.shouldCollectComment = false;
});
}

@@ -1239,4 +1394,5 @@ },

editorInfo.ace_getRepFromSelector = _(getRepFromSelector).bind(context);
editorInfo.ace_getCommentIdOnSelection = _(getCommentIdOnSelection).bind(context);
editorInfo.ace_getCommentIdOnFirstPositionSelected = _(getCommentIdOnFirstPositionSelected).bind(context);
editorInfo.ace_hasCommentOnSelection = _(hasCommentOnSelection).bind(context);
}
var $ = require('ep_etherpad-lite/static/js/rjquery').$;
var commentL10n = require('ep_comments_page/static/js/commentL10n');
// Easier access to outer pad
var padOuter;
var getPadOuter = function() {
padOuter = padOuter || $('iframe[name="ace_outer"]').contents();
return padOuter;
}
// Easier access to new comment container
var newCommentContainer;
var getNewCommentContainer = function() {
newCommentContainer = newCommentContainer || getPadOuter().find('#newComments');
return newCommentContainer;
}
// Insert a comment node
var createNewCommentForm = function(comment) {
var container = getNewCommentContainer();
comment.commentId = "";
var content = $('#newCommentTemplate').tmpl(comment);
content.prependTo(container);
return content;
};
// Create a comment object with data filled on the given form
var buildCommentFrom = function(form) {
var text = form.find('.comment-content').val();
var changeFrom = form.find('.comment-suggest-from').val();
var changeTo = form.find('.comment-suggest-to').val() || null;
var changeFrom = form.find('.from-value').text();
var changeTo = form.find('.to-value').val() || null;
var comment = {};

@@ -47,14 +22,18 @@

var cancelNewComment = function(){
hideNewCommentForm();
hideNewCommentPopup();
}
// Callback for new comment Submit
var submitNewComment = function(form, callback) {
var submitNewComment = function(callback) {
var index = 0;
var text = form.find('.comment-content').val();
var commentTextIsNotEmpty = text.length !== 0;
var form = $('#newComment');
var comment = buildCommentFrom(form);
if (commentTextIsNotEmpty) {
hideNewCommentForm();
if (comment.text.length > 0 || comment.changeTo && comment.changeTo.length > 0) {
form.find('.comment-content, .to-value').removeClass('error');
hideNewCommentPopup();
callback(comment, index);
} else {
if (comment.text.length == 0) form.find('.comment-content').addClass('error');
if (comment.changeTo && comment.changeTo.length == 0) form.find('.to-value').addClass('error');
}

@@ -64,103 +43,50 @@ return false;

var fixFlyingToobarOnIOS = function() {
if (browser.ios) {
var shouldPlaceMenuRightOnBottom = $(".toolbar ul.menu_right").css('bottom') !== "auto";
getNewCommentContainer().find('input, textarea')
.on("focus", function() {
fixToolbarPosition();
if (shouldPlaceMenuRightOnBottom) placeMenuRightOnBottom();
})
.on("blur", function() {
revertFixToToolbarPosition();
if (shouldPlaceMenuRightOnBottom) revertPlacingMenuRightOnBottom();
});
// When user changes orientation, we need to re-position menu_right
if (shouldPlaceMenuRightOnBottom) {
waitForResizeToFinishThenCall(500, function() {
var needToUpdateTop = $(".toolbar ul.menu_right").css("top") !== "";
if (needToUpdateTop) placeMenuRightOnBottom();
});
}
}
}
var fixToolbarPosition = function() {
$(".toolbar ul.menu_left, .toolbar ul.menu_right").css("position", "absolute");
}
var revertFixToToolbarPosition = function() {
$(".toolbar ul.menu_left, .toolbar ul.menu_right").css("position", "");
}
var placeMenuRightOnBottom = function() {
$(".toolbar ul.menu_right").css("top", $(document).outerHeight());
}
var revertPlacingMenuRightOnBottom = function() {
$(".toolbar ul.menu_right").css("top", "");
}
/* ***** Public methods: ***** */
var localizeNewCommentForm = function() {
var newCommentForm = getNewCommentContainer().find('#newComment');
if (newCommentForm.length !== 0) commentL10n.localize(newCommentForm);
var localizenewCommentPopup = function() {
var newCommentPopup = $('#newComment');
if (newCommentPopup.length !== 0) commentL10n.localize(newCommentPopup);
};
// Create container to hold new comment form
var insertContainers = function(target) {
target.prepend('<div id="newComments"></div>');
// Listen for include suggested change toggle
getNewCommentContainer().on("change", '#suggestion-checkbox', function() {
if($(this).is(':checked')) {
getPadOuter().find('.suggestion').show();
} else {
getPadOuter().find('.suggestion').hide();
}
});
}
// Insert new Comment Form
var insertNewCommentFormIfDontExist = function(comment, callback) {
var newCommentForm = getNewCommentContainer().find('#newComment');
var formDoesNotExist = newCommentForm.length === 0;
if (formDoesNotExist) {
newCommentForm = createNewCommentForm(comment);
localizeNewCommentForm();
var insertNewCommentPopupIfDontExist = function(comment, callback) {
$('#newComment').remove();
var newCommentPopup = $('#newComment');
// Listen to cancel
newCommentForm.find('#comment-reset').on('click', function() {
cancelNewComment();
});
comment.commentId = "";
var newCommentPopup = $('#newCommentTemplate').tmpl(comment);
newCommentPopup.appendTo($('#editorcontainerbox'));
// Hack to avoid "flying" toolbars on iOS
fixFlyingToobarOnIOS();
localizenewCommentPopup();
} else {
// Reset form to make sure it is all clear
newCommentForm.get(0).reset();
// Listen for include suggested change toggle
$('#newComment').find('.suggestion-checkbox').change(function() {
$('#newComment').find('.suggestion').toggle($(this).is(':checked'));
});
// Detach current "submit" handler to be able to call the updated callback
newCommentForm.off("submit");
}
// Listen to comment confirmation (needs to be outside of if/else to be able to update the callback)
newCommentForm.submit(function() {
var form = $(this);
return submitNewComment(form, callback);
// Cancel btn
newCommentPopup.find('#comment-reset').on('click', function() {
cancelNewComment();
});
// Create btn
$('#newComment').on("submit", function(e) {
e.preventDefault();
return submitNewComment(callback);
});
return newCommentForm;
return newCommentPopup;
};
var showNewCommentForm = function() {
getNewCommentContainer().addClass("active");
// we need to set a timeout otherwise the animation to show #newComment won't be visible
window.setTimeout(function() {
getPadOuter().find('.suggestion').hide(); // Hides suggestion in case of a cancel
getNewCommentContainer().find('#newComment').removeClass("hidden").addClass("visible");
}, 0);
var showNewCommentPopup = function() {
// position below comment icon
$('#newComment').css('left', $('.toolbar .addComment').offset().left)
// Reset form to make sure it is all clear
$('#newComment').find('.suggestion-checkbox').prop('checked', false).trigger('change');
$('#newComment').find('textarea').val("");
$('#newComment').find('.comment-content, .to-value').removeClass('error');
// Show popup
$('#newComment').addClass("popup-show");
// mark selected text, so it is clear to user which text range the comment is being applied to

@@ -170,13 +96,8 @@ pad.plugins.ep_comments_page.preCommentMarker.markSelectedText();

var hideNewCommentForm = function() {
getNewCommentContainer().find('#newComment').removeClass("visible").addClass("hidden");
var hideNewCommentPopup = function() {
$('#newComment').removeClass("popup-show");
// force focus to be lost, so virtual keyboard is hidden on mobile devices
getNewCommentContainer().find(':focus').blur();
$('#newComment').find(':focus').blur();
// we need to give some time for the animation of #newComment to finish
window.setTimeout(function() {
getNewCommentContainer().removeClass("active");
}, 500);
// unmark selected text, as now there is no text being commented

@@ -186,19 +107,5 @@ pad.plugins.ep_comments_page.preCommentMarker.unmarkSelectedText();

// Some browsers trigger resize several times while resizing the window, so
// we need to make sure resize is done to avoid calling the callback multiple
// times.
// Based on: https://css-tricks.com/snippets/jquery/done-resizing-event/
var waitForResizeToFinishThenCall = function(timeout, callback){
var resizeTimer;
$(window).on("resize", function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(callback, timeout);
});
}
exports.localizeNewCommentForm = localizeNewCommentForm;
exports.insertNewCommentFormIfDontExist = insertNewCommentFormIfDontExist;
exports.showNewCommentForm = showNewCommentForm;
exports.hideNewCommentForm = hideNewCommentForm;
exports.insertContainers = insertContainers;
exports.waitForResizeToFinishThenCall = waitForResizeToFinishThenCall;
exports.localizenewCommentPopup = localizenewCommentPopup;
exports.insertNewCommentPopupIfDontExist = insertNewCommentPopupIfDontExist;
exports.showNewCommentPopup = showNewCommentPopup;
exports.hideNewCommentPopup = hideNewCommentPopup;

@@ -0,8 +1,28 @@

var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var collectContentPre = function(hook, context){
var comment = /(?:^| )(c-[A-Za-z0-9]*)/.exec(context.cls);
var fakeComment = /(?:^| )(fakecomment-[A-Za-z0-9]*)/.exec(context.cls);
if(comment && comment[1]){
context.cc.doAttrib(context.state, "comment::" + comment[1]);
}
// a fake comment is a comment copied from this or another pad. To avoid conflicts
// with existing comments, a fake commentId is used, so then we generate a new one
// when the comment is saved
if(fakeComment){
var mapFakeComments = pad.plugins.ep_comments_page.getMapfakeComments();
var fakeCommentId = fakeComment[1];
var commentId = mapFakeComments[fakeCommentId];
context.cc.doAttrib(context.state, "comment::" + commentId);
}
};
exports.collectContentPre = collectContentPre;
exports.generateCommentId = function(){
var commentId = "c-" + randomString(16);
return commentId;
}

@@ -22,3 +22,6 @@ var supertest = require('ep_etherpad-lite/node_modules/supertest'),

beforeEach(function(done){
padID = createPad(done);
createPad(function(err, newPadID) {
padID = newPadID;
done(err);
});
});

@@ -49,4 +52,4 @@

// add a comment to a pad
createComment(pad, {}, function(err, comment) {
createCommentReply(pad, comment, {}, function(err, replyId){
createComment(padID, {}, function(err, comment) {
createCommentReply(padID, comment, {}, function(err, replyId){
api.get(listCommentRepliesEndPointFor(padID, apiKey))

@@ -63,3 +66,3 @@ .expect(function(res){

it('returns comment replies data', function(done){
createComment(pad, {}, function(err, comment){
createComment(padID, {}, function(err, comment){
var text = "text";

@@ -78,3 +81,3 @@ var changeTo = "changeTo";

};
createCommentReply(pad, comment, data, function(err, replyId){
createCommentReply(padID, comment, data, function(err, replyId){
api.get(listCommentRepliesEndPointFor(padID, apiKey))

@@ -96,5 +99,5 @@ .expect(function(res){

// create comment
createComment(pad, {}, function(err, comment){
createComment(padID, {}, function(err, comment){
// create reply
createCommentReply(pad, comment, {}, function(err, commentId){
createCommentReply(padID, comment, {}, function(err, commentId){
// get r-w data

@@ -105,3 +108,3 @@ api.get(listCommentRepliesEndPointFor(padID, apiKey))

// get read-only pad id
readOnlyId(pad, function(err, roPadId) {
readOnlyId(padID, function(err, roPadId) {
// get r-o data

@@ -127,5 +130,8 @@ api.get(listCommentRepliesEndPointFor(roPadId, apiKey))

// creates a new pad...
padID = createPad(function(err, pad) {
createPad(function(err, newPadID) {
if (err) throw err;
padID = newPadID;
// ... and a comment before each test run
createComment(pad, {}, function(err, comment) {
createComment(padID, {}, function(err, comment) {
commentID = comment;

@@ -218,5 +224,8 @@ done(err);

// creates a new pad...
padID = createPad(function(err, pad) {
createPad(function(err, newPadID) {
if (err) throw err;
padID = newPadID;
// ... and a comment before each test run, then...
createComment(pad, {},function(err, comment) {
createComment(padID, {},function(err, comment) {
commentID = comment;

@@ -226,3 +235,3 @@

var socket = io.connect(appUrl + "/comment");
var req = { padId: pad };
var req = { padId: padID };
// needs to get comments to be able to join the pad room, where the messages will be broadcast to:

@@ -263,2 +272,4 @@ socket.emit('getComments', req, function (res){

createPad(function(err, otherPadId) {
if (err) throw err;
// ... and another comment...

@@ -265,0 +276,0 @@ createComment(otherPadId, {},function(err, otherCommentId) {

@@ -20,3 +20,6 @@ var supertest = require('ep_etherpad-lite/node_modules/supertest'),

beforeEach(function(done){
padID = createPad(done);
createPad(function(err, newPadID) {
padID = newPadID;
done(err);
});
});

@@ -47,5 +50,5 @@

// creates first comment...
createComment(pad, {},function(err, comment) {
createComment(padID, {},function(err, comment) {
// ... creates second comment...
createComment(pad, {},function(err, comment) {
createComment(padID, {},function(err, comment) {
// ... and finally checks if comments are returned

@@ -76,3 +79,3 @@ api.get(listCommentsEndPointFor(padID, apiKey))

};
createComment(pad, data, function(err, commentId){
createComment(padID, data, function(err, commentId){
api.get(listCommentsEndPointFor(padID, apiKey))

@@ -92,7 +95,7 @@ .expect(function(res){

it('returns same data for read-write and read-only pad ids', function(done){
createComment(pad, {}, function(err, commentId){
createComment(padID, {}, function(err, commentId){
api.get(listCommentsEndPointFor(padID, apiKey))
.end(function(err, res) {
var rwData = JSON.stringify(res.body.data);
readOnlyId(pad, function(err, roPadId) {
readOnlyId(padID, function(err, roPadId) {
api.get(listCommentsEndPointFor(roPadId, apiKey))

@@ -115,3 +118,6 @@ .expect(function(res){

beforeEach(function(done){
padID = createPad(done);
createPad(function(err, newPadID) {
padID = newPadID;
done(err);
});
});

@@ -202,6 +208,9 @@

//create a new pad before each test run...
padID = createPad(function(err, pad) {
createPad(function(err, newPadID) {
if (err) throw err;
padID = newPadID;
// ... and listens to the broadcast message:
var socket = io.connect(appUrl + "/comment");
var req = { padId: pad };
var req = { padId: padID };
// needs to get comments to be able to join the pad room, where the messages will be broadcast to:

@@ -220,3 +229,3 @@ socket.emit('getComments', req, function (res){

// create first comment...
createComment(padID,{}, function(err, commentId) {
createComment(padID, {}, function(err, commentId) {
if(err) throw err;

@@ -226,3 +235,3 @@ if(!commentId) throw new Error("Comment should had been created");

// ... create second comment...
createComment(padID,{}, function(err, commentId) {
createComment(padID, {}, function(err, commentId) {
if(err) throw err;

@@ -243,2 +252,4 @@ if(!commentId) throw new Error("Comment should had been created");

createPad(function(err, otherPadId) {
if(err) throw err;
// ... and add comment to it:

@@ -245,0 +256,0 @@ createComment(otherPadId, {}, function(err, commentId) {

@@ -1,2 +0,2 @@

describe("Comment Localization", function(){
describe("ep_comments_page - Comment Localization", function(){
//create a new pad with comment before each test run

@@ -25,3 +25,3 @@ beforeEach(function(cb){

//get the title of the comment
var $changeToLabel = outer$(".comment-changeTo-label").first();
var $changeToLabel = outer$(".to-label").first();
expect($changeToLabel.text()).to.be("Suggested Change:");

@@ -40,3 +40,3 @@

//get the 'Suggested Change' label
var $changeToLabel = outer$("#" + commentId + " .comment-changeTo-label").first();
var $changeToLabel = outer$("#" + commentId + " .to-label").first();
expect($changeToLabel.text()).to.be("Alteração Sugerida:");

@@ -64,3 +64,3 @@

//get the 'Include suggested change' label
var $changeToLabel = outer$('#newComment label[for=suggestion-checkbox]').first();
var $changeToLabel = outer$('#newComment label.label-suggestion-checkbox').first();
expect($changeToLabel.text()).to.be("Incluir alteração sugerida");

@@ -95,5 +95,5 @@

$commentField.val("My comment");
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
var $suggestionField = outer$("textarea.comment-suggest-to");
var $suggestionField = outer$("textarea.to-value");
$suggestionField.val("Change to this suggestion");

@@ -100,0 +100,0 @@ var $submittButton = outer$("input[type=submit]");

@@ -1,2 +0,2 @@

describe("Comment settings", function() {
describe("ep_comments_page - Comment settings", function() {
describe("when user unchecks 'Show Comments'", function() {

@@ -12,2 +12,3 @@ // create a new pad and check "Show Comments" checkbox

it("sidebar comments should not be visible when opening a new pad", function(done) {
this.timeout(60000);
// force to create a new pad, so validation would be on brand new pads

@@ -22,2 +23,3 @@ helper.newPad(function() {

it("sidebar comments should not be visible when adding a new comment to a new pad", function(done) {
this.timeout(60000);
// force to create a new pad, so validation would be on brand new pads

@@ -85,5 +87,5 @@ helper.newPad(function() {

$commentField.val("My comment");
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
var $suggestionField = outer$("textarea.comment-suggest-to");
var $suggestionField = outer$("textarea.to-value");
$suggestionField.val("Change to this suggestion");

@@ -109,2 +111,2 @@ var $submittButton = outer$("input[type=submit]");

}
});
});

@@ -1,2 +0,2 @@

describe("Comment Delete", function(){
describe("ep_comments_page - Comment Delete", function(){
//create a new pad with comment before each test run

@@ -46,5 +46,5 @@ beforeEach(function(cb){

$commentField.val("My comment");
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
var $suggestionField = outer$("textarea.comment-suggest-to");
var $suggestionField = outer$("textarea.to-value");
$suggestionField.val("Change to this suggestion");

@@ -51,0 +51,0 @@ var $submittButton = outer$("input[type=submit]");

@@ -1,2 +0,2 @@

describe("Comment icons", function() {
describe("ep_comments_page - Comment icons", function() {
//create a new pad with comment before each test run

@@ -249,5 +249,5 @@ beforeEach(function(cb){

// we don't need comment suggestion to be filled for these tests, but here's how to do it:
// var $hasSuggestion = outer$("#suggestion-checkbox");
// var $hasSuggestion = outer$(".suggestion-checkbox");
// $hasSuggestion.click();
// var $suggestionField = outer$("textarea.comment-suggest-to");
// var $suggestionField = outer$("textarea.to-value");
// $suggestionField.val("Change to this suggestion");

@@ -254,0 +254,0 @@ var $submittButton = outer$("input[type=submit]");

@@ -1,2 +0,2 @@

describe("Comment Reply", function(){
describe("ep_comments_page - Comment Reply", function(){
//create a new pad with comment before each test run

@@ -41,5 +41,5 @@ beforeEach(function(cb){

var $replyForm = outer$("form.comment-reply");
var $replyField = $replyForm.find(".comment-reply-input");
var $replyField = $replyForm.find(".comment-content");
var $replyWithSuggestionCheckbox = $replyForm.find(".reply-suggestion-checkbox");
var $replySuggestionTextarea = $replyForm.find(".reply-comment-suggest-to");
var $replySuggestionTextarea = $replyForm.find(".reply-to-value");
expect($replyField.text()).to.be("");

@@ -111,5 +111,5 @@ expect($replyWithSuggestionCheckbox.is(":checked")).to.be(false);

$commentField.val("My comment");
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
var $suggestionField = outer$("textarea.comment-suggest-to");
var $suggestionField = outer$("textarea.to-value");
$suggestionField.val("Change to this suggestion");

@@ -139,3 +139,3 @@ var $submittButton = outer$("input[type=submit]");

// fill reply field
var $replyField = outer$(".comment-reply-input");
var $replyField = outer$(".comment-content");
$replyField.val("My reply");

@@ -150,3 +150,3 @@

// fill suggestion field
var $suggestionField = outer$("textarea.reply-comment-suggest-to");
var $suggestionField = outer$("textarea.reply-to-value");
$suggestionField.val("My suggestion");

@@ -153,0 +153,0 @@ }

@@ -1,2 +0,2 @@

describe("Comment Suggestion", function(){
describe("ep_comments_page - Comment Suggestion", function(){
//create a new pad before each test run

@@ -18,3 +18,3 @@ beforeEach(function(cb){

var $suggestionFrom = outer$(".comment-suggest-from");
var $suggestionFrom = outer$(".from-value");
expect($suggestionFrom.val()).to.be("A\n text with\n line attributes");

@@ -41,3 +41,3 @@ done();

var $suggestionFrom = outer$(".comment-suggest-from");
var $suggestionFrom = outer$(".from-value");
expect($suggestionFrom.val()).to.be('New target for comment');

@@ -69,5 +69,5 @@ done();

// check suggestion box
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
}

@@ -1,2 +0,2 @@

describe("Pre-comment text mark", function() {
describe("ep_comments_page - Pre-comment text mark", function() {
var padId;

@@ -134,5 +134,5 @@

$commentField.val("My comment");
var $hasSuggestion = outer$("#suggestion-checkbox");
var $hasSuggestion = outer$(".suggestion-checkbox");
$hasSuggestion.click();
var $suggestionField = outer$("textarea.comment-suggest-to");
var $suggestionField = outer$("textarea.to-value");
$suggestionField.val("Change to this suggestion");

@@ -139,0 +139,0 @@ var $submittButton = outer$("input[type=submit]");

var prettyDate;
describe("Time Formatting", function() {
_.each({'en': 'English', 'de': 'a language not localized yet'}, function(description, lang) {
describe("ep_comments_page - Time Formatting", function() {
_.each({'en': 'English', 'af': 'a language not localized yet'}, function(description, lang) {
describe("in " + description, function(){

@@ -390,3 +390,3 @@ before(function(cb) {

'pt-br' : 'Negrito (Ctrl-B)',
'de' : 'Fett (Strg-B)'
'af' : 'Vet (Ctrl-B)'
};

@@ -393,0 +393,0 @@ var chrome$ = helper.padChrome$;

@@ -45,3 +45,3 @@ var appUrl = 'http://localhost:9001';

var createPad = function(done) {
pad = randomString(5);
var pad = randomString(5);

@@ -54,4 +54,2 @@ api.get('/api/'+apiVersion+'/createPad?apikey='+apiKey+"&padID="+pad)

done(null, pad);
return pad;
}

@@ -58,0 +56,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc