diff --git a/commentManager.js b/commentManager.js index 00e180ff..505db063 100644 --- a/commentManager.js +++ b/commentManager.js @@ -27,6 +27,23 @@ 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) diff --git a/index.js b/index.js index 2642aa41..dcb2b8f0 100644 --- a/index.js +++ b/index.js @@ -72,6 +72,15 @@ exports.socketio = function (hook_name, args, cb){ }); }); + 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) { // Broadcast to all other users that this change was accepted. // Note that commentId here can either be the commentId or replyId.. diff --git a/locales/de.json b/locales/de.json index 6bd85884..06a445b8 100644 --- a/locales/de.json +++ b/locales/de.json @@ -2,19 +2,21 @@ "ep_comments_page.comment" : "Kommentar", "ep_comments_page.comments" : "Kommentare", "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.placeholder":"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", "ep_comments_page.time.seconds.future" : "{{count}} Sekunden von jetzt an", "ep_comments_page.time.one_minute.past" : "vor 1 Minute", diff --git a/locales/en.json b/locales/en.json index 419780c8..4ce1875b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -2,20 +2,22 @@ "ep_comments_page.comment" : "Comment", "ep_comments_page.comments" : "Comments", "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.placeholder":"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", "ep_comments_page.time.seconds.future" : "{{count}} seconds from now", "ep_comments_page.time.one_minute.past" : "1 minute ago", diff --git a/locales/fr.json b/locales/fr.json index f285101f..3fd83f49 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -2,19 +2,21 @@ "ep_comments_page.comment" : "Annotation", "ep_comments_page.comments" : "Annotations", "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.placeholder":"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", "ep_comments_page.time.seconds.future" : "dans {{count}} secondes", "ep_comments_page.time.one_minute.past" : "il y a 1 minute", diff --git a/locales/pl.json b/locales/pl.json index d006e8fe..73dc5eb2 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -2,19 +2,21 @@ "ep_comments_page.comment" : "Komentarz", "ep_comments_page.comments" : "Komentarze", "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.placeholder":"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", "ep_comments_page.time.seconds.future" : "{{count}} sekund od teraz", "ep_comments_page.time.one_minute.past" : "Minutę temu", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 06c924b4..94d8f818 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -2,20 +2,20 @@ "ep_comments_page.comment" : "Comentário", "ep_comments_page.comments" : "Comentários", "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.placeholder":"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", "ep_comments_page.time.seconds.future" : "daqui a {{count}} segundos", "ep_comments_page.time.one_minute.past" : "1 minuto atrás", diff --git a/static/css/comment.css b/static/css/comment.css index 48bd6a5b..28ebb4cc 100644 --- a/static/css/comment.css +++ b/static/css/comment.css @@ -1,362 +1,211 @@ -note{ - display:block; +/* Text commented inside editor */ +#innerdocbody .ace-line .comment { + background-color: #fff382; + color: #222; } - -.comment { - /*WARNING - if you change this value, you need to change it too on copyPasteEvents.js, otherwise you'll break part of the copy/paste behavior"*/ - background: #FFFACD !important; - border-radius: 0px; -} - - -.pre-selected-comment { - background: #FFE168 !important; -} - -time{ - display:none; +#innerdocbody .ace-line .comment[data-open="true"]{ + color: orange !important; } -.comment-modal .comment-text, -.sidebar-comment:hover .comment-text, -.sidebar-comment.mouseover .comment-text{ - height:auto; - white-space:normal; - text-overflow:auto; -} - -.sidebar-comment{ - line-height:24px; -} -.sidebar-comment:hover, -.sidebar-comment.mouseover{ - height:auto; - white-space:normal; - text-overflow:auto; -} - -.sidebar-comment:hover .comment-reply-button, -.sidebar-comment.mouseover .comment-reply-button { - display:block; -} - -.comment-modal{ - position:absolute; - min-width:20px; - max-width:50%; - min-height:10px; - background-color:#fff; - z-index:99999; - top:0; - left:0; - border:solid #ccc 1px; - display:none; - padding:10px 10px 0px 10px; - font-size:12px; - line-height:24px; -} - -.comment-changeTo-approve{ - margin:0px 0px 10px 0px; -} - -.reply-suggestion.active { - display: block; +/* Comment right side container */ +#comments { + width: 250px; + order: 3; + position: relative; } - -/* hide comment elements when displayed on modal, not on sidebar */ -.comment-modal .reply-comment-suggest, -.comment-modal .comment-reply-input-label, -.comment-modal .reply-suggestion, -.comment-modal input, -.comment-modal .comment-changeTo-approve{ - display:none; +#comments:not(.active) { + display: none; } - -.sidebar-comment-reply{ - padding-top:5px; - padding-left:10px; - margin-bottom: 5px; - position: relative; +@media (max-width: 900px) { + #commentIcons, #comments { + display: none !important; + } } -.sidebar-comment-reply:nth-child(even){ - background-color:#f1f1f1; +.sidebar-comment { + position: absolute; + width: 100%; + margin-top: 3px; } -.comment-reply{ - margin-bottom:5px; +/* WITH ICONS */ +#comments.with-icons { + display: none; } -.comment-reply-input{ - margin-top:10px; - width:187px; +/* NEW COMMENT FORM (included both in popup and reply) */ +.new-comment .comment-content { + width: 100%; } - -#comments { - display:none; - width:210px; - bottom:0; - position:absolute; - top:10px; - right:0px; - font-family: Helvetica, Arial, sans-serif; - z-index:1; +.comment-reply .new-comment:not(.editing) .form-more { + display: none; } - -#newComments { - display:none; - width:210px; - bottom:0; - position:absolute; - top:10px; - right:0px; - font-family: Helvetica, Arial, sans-serif; - z-index:1; +input.error, textarea.error { + border-color: red; } -#newComments.active { - display:block; - z-index: 1000; +/* COMMENT GENERAL STYLE */ +.comment-author-name { + font-weight: bold; } - -.comment-reply-button{ - float:right; - display:none; +.comment-created-at { + font-size: .8em; + opacity: .7; + margin-left: 5px; } - -.comment-reply-button:hover{ - cursor:pointer; - color:#000; +.comment-actions-wrapper { + float: right; } -.comment-reply, .suggestion{ - font-weight:bold; - color:#555 -} -.reply-suggestion .reply-comment-suggest-from, .suggestion .comment-suggest-from, .comment-changeTo-value, .comment-changeFrom-value{ - font-weight: normal; - color: black; - padding-left: 0.5em; - border:none; - border-left: 2px solid #DDD; - max-height: 5em; - width: 178px; - overflow-y: auto; - white-space: normal; - line-height: 1.3em; +/* COMMENT COMPACTED (Visible on right side) */ +.sidebar-comment:not(.full-display) .full-display-content { + display: none; } - -.sidebar-comment { +.compact-display-content { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - right:0px; - margin-left: 10px; - padding: 0px 5px 5px 5px; - background: white; - width:90%; - height:20px; - border-top-left-radius: 2px; - border-bottom-left-radius: 2px; - -moz-box-shadow: 0 0 2px #888; - -webkit-box-shadow: 0 0 2px #888; - box-shadow: 0 0 2px #888; - position:absolute; - font-size:12px; + padding: 0 10px; + background-color: #eeeeed; } -.sidebar-comment input[type=submit]{ - padding:2px; +/* COMMENT FULL (when mouse hover) */ +.sidebar-comment.full-display { + z-index: 2; } - -.sidebar-comment:hover, -.sidebar-comment.mouseover{ - z-index:999; +.sidebar-comment.full-display .full-display-content { + display: block; + margin-top: -10px; } - -.sidebar-comment.mouseover { - - z-index:99999; - margin: 0px 0 0 14px; - white-space:normal; - height:auto; - -webkit-transition: z-index 100ms ease-in, margin 100ms ease-in, background 100ms ease-in; - -moz-transition: z-index 100ms ease-in, margin 100ms ease-in, background 100ms ease-in; - -ms-transition: z-index 100ms ease-in, margin 100ms ease-in, background 100ms ease-in; - -o-transition: z-index 100ms ease-in, margin 100ms ease-in, background 100ms ease-in; - transition: z-index 100ms ease-in, margin 100ms ease-in, background 100ms ease-in; +.sidebar-comment.full-display .compact-display-content { + display: none; } - -.comment-author-name { - color: #555; - font-weight:bold; - font-size: 1em; - display:inline; +.full-display-content { + background-color: white; + border-radius: 5px; + overflow: hidden; + box-shadow: 0 2px 4px #ddd; + z-index: 99; } - -.comment-changeTo-label, .comment-changeFrom-label{ - color: #555; - font-weight:bold; - font-size: 1em; - display:inline; +.full-display-content .comment-title-wrapper, +.full-display-content .comment-reply { + padding: 10px; } - -.comment-text { - font-size: 1em; - color: #333; - word-wrap: break-word; +.full-display-content .comment-title-wrapper .comment-text { + display: block; + margin-top: 10px; white-space: normal; - display: inline; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; } - -.sidebar-comment textarea { - border: 2px solid #DDD; - background: #fff; - width: 170px; - height: 40px; - padding: 5px; - font-size:1em; +.full-display-content .comment-title-wrapper .comment-text.default-text { + display: none; } -.comment-content:focus { - border: 2px solid #ccc; +/* SUGGESTION */ +.suggestion, .reply-suggestion { + display: none; } - -.comment-buttons input { - color: #666; - border: 2px solid #DDD; - background: #EEE; - width: 59px; - height: 30px; - font-size:12px; +.suggestion-display { + margin-top: 5px; + white-space: normal; } - -.comment-buttons input[type="submit"] { - margin-right: 5px; - width:120px; +.suggestion-display .from-label, +.suggestion-display .to-label { + margin-right: 2px; } - -.comment-buttons input:hover { - color: #333; - cursor: pointer; +.suggestion-display .from-value, +.suggestion-display .to-value { + opacity: .8; + font-style: italic; } - -#newComment.visible{ - width:250px; - right:0px; - -webkit-transition: width 500ms; - -moz-transition: width 500ms; - -ms-transition: width 500ms; - -o-transition: width 500ms; - transition: width 500ms; - height:auto; - min-height:100px; +.suggestion-display .from-value:after, .suggestion-display .from-value:before, +.suggestion-display .to-value:after, .suggestion-display .to-value:before { + content: '"'; } - -#newComment.hidden{ - width:0px; - right:-50px; - -webkit-transition: right 500ms, width 500ms; - -moz-transition: right 500ms, width 500ms; - -ms-transition: right 500ms, width 500ms; - -o-transition: right 500ms, width 500ms; - transition: right 500ms, width 500ms; +.suggestion-create .from-label, +.suggestion-create .to-label { + display: block; + font-weight: bold; + margin: 5px 0; } - -#newComment{ - position:fixed; - display:block; - width:0px; - z-index:1000; - font-size:12px; +.approve-suggestion-btn, .revert-suggestion-btn { + display: block; + margin-bottom: 10px; } - -#outerdocbody{ - width:1050px; +.suggestion-create textarea.to-value { + width: 100%; } +.comment-container .revert-suggestion-btn { display: none; } +.comment-container.change-accepted .revert-suggestion-btn { display: block; } +.comment-container.change-accepted .approve-suggestion-btn { display: none; } -/* if Page View is disabled, we use the full width. (This is only changed - by commentIcons.js) */ -#outerdocbody.pageViewDisabled{ - width:100%; +.comment-container.change-accepted .comment-replies-container .revert-suggestion-btn { display: none; } +.comment-container.change-accepted .comment-replies-container .approve-suggestion-btn { display: block; } +.comment-container.change-accepted .comment-replies-container .comment-container.change-accepted .revert-suggestion-btn { display: block; } +.comment-container.change-accepted .comment-replies-container .comment-container.change-accepted .approve-suggestion-btn { display: none; } +/* REPLIES */ +.comment-reply { + background-color: #f9f9f9; + border-top: 1px solid #eee; } - -/* dont display comments at < 955px wide */ -@media (max-width: 955px) { - #outerdocbody{ - width:100%; - padding-right:0px; - } +/* One previous reply */ +.sidebar-comment-reply { + margin-bottom: 10px; } -/* #innerdocbody is inside of another iframe, so needs a new media query */ -@media (max-width: 947px) { /* 955 - 8 (8 is #innerdocbody left position) */ - #innerdocbody{ - margin-right:0px; - } +.sidebar-comment-reply .comment-text { + display: inline; + margin-top: 5px; + white-space: normal; } - -/* display comments at > 955px wide */ -@media (min-width: 955px) { - #comments.active{ - display:block; - } +.sidebar-comment-reply .comment-edit { + display: none; + margin-left: 5px; + font-size: 1em; + opacity: .7; + transition: opacity .2s; } -/* #innerdocbody is inside of another iframe, so needs a new media query */ -@media (min-width: 947px) { /* 955 - 8 (8 is #innerdocbody left position) */ - #innerdocbody.comments:not(.innerPV){ - margin-right:200px; - } +.sidebar-comment-reply:hover:not(.editing) .comment-edit { + display: inline; } - -.suggestion, .reply-suggestion{ - display:none; +.sidebar-comment-reply:hover:not(.editing) .comment-edit:hover { + opacity: 1; } - -.sidebar-comment-reply input{ - margin-bottom:10px; -/* margin-left:40px; */ +.reply-comment-suggest, .comment-suggest { + margin-top: 10px; } -.comment-options-button{ - float: right; +/* EDITING COMMENT */ +.comment-edit-text { + width: 100%; } -.comment-options-selected{ - background-color: #d8d8d8; +.comment-edit-form + .comment-text { + display: none !important; } -.comment-options-button--icon{ - background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzLjk2OSIgaGVpZ2h0PSIyMC4xNTYiIHZpZXdCb3g9IjAgMCAzLjk2OSAyMC4xNTYiPjxzd2l0Y2g%2BPGcgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiM2RTcwNzIiPjxwYXRoIGQ9Ik0zLjk1NyAyLjk2NWExLjk2NSAxLjk2NSAwIDEgMS0zLjkzIDAgMS45NjUgMS45NjUgMCAwIDEgMy45MyAwTTMuOTU3IDkuOTY1YTEuOTY1IDEuOTY1IDAgMSAxLTMuOTMgMCAxLjk2NSAxLjk2NSAwIDAgMSAzLjkzIDBNMy45NTcgMTYuOTY1YTEuOTY1IDEuOTY1IDAgMSAxLTMuOTMgMCAxLjk2NSAxLjk2NSAwIDAgMSAzLjkzIDAiLz48L2c%2BPC9zd2l0Y2g%2BPC9zdmc%2B); - background-repeat: no-repeat; - cursor: pointer; - padding: 2px; - margin: 6px; - background-size: contain; - background-position: 50% 50%; +/* MODAL FOR MOBILES */ +.comment-modal { + bottom: auto !important; + right: auto !important; } - -.comment-options{ - position: absolute; - background-color: #dcdcdc; - margin-left: 108px; - top: 24px; - right: 5px; - z-index: 999; +.comment-modal-comment { + padding: 0; } - -.comment-delete, .comment-edit { - display: block; - font-weight: bold; - cursor: pointer; - color: #555; - margin-top: 1px; - padding: 0 18px; +.comment-modal-comment .sidebar-comment { + position: relative; + top: 0; } - -.comment-edit:hover, .comment-delete:hover{ - background-color: #b3b3b3; +.comment-modal-comment .compact-display-content { + display: none; +} +.comment-modal-comment .full-display-content { + display: block !important; + margin: 0; } +.comment-modal-comment .comment-content { + margin-top: 0 !important; +} + +/* OTHER */ +.hidden { + display: none; +} \ No newline at end of file diff --git a/static/css/commentIcon.css b/static/css/commentIcon.css index 58fe1e39..a7265e44 100644 --- a/static/css/commentIcon.css +++ b/static/css/commentIcon.css @@ -1,29 +1,17 @@ #commentIcons { display: block; z-index: 1; - margin-left: 760px; - padding-left: 25px; + margin-left: 15px; + width: 50px; + position: relative; } - -/* when line numbers are not visible, we need to move icons to the left */ -#sidediv.sidedivhidden ~ #commentIcons { - padding-left: 0px; -} - -/* when page view is disabled, we need to move icons to the left */ -#outerdocbody.pageViewDisabled #commentIcons { - margin-left: calc(100% - 90px); -} - -/* this is the point where #comments will be visible (check media queries on comment.css) */ -@media (min-width: 955px) { - #outerdocbody.pageViewDisabled #commentIcons { - margin-left: calc(100% - 290px); - } +#commentIcons:not(.active) { + display: none; } .comment-icon-line { position: absolute; + margin-top: 2px; } .comment-icon { background-repeat: no-repeat; @@ -32,10 +20,11 @@ vertical-align: middle; width: 16px; margin-right: 5px; + cursor: pointer; } .comment-icon:before { font-family: "fontawesome-etherpad"; - content: "\e838"; + content: "\E850"; color:#666; font-size:14px; padding-top:2px; @@ -43,9 +32,9 @@ } .comment-icon.with-reply:before { - content: "\e828"; + content: "\E82D"; } .comment-icon.active:before { color:orange; -} +} \ No newline at end of file diff --git a/static/js/commentBoxes.js b/static/js/commentBoxes.js index 2d4fda36..ea82bdcc 100644 --- a/static/js/commentBoxes.js +++ b/static/js/commentBoxes.js @@ -15,69 +15,82 @@ var showComment = function(commentId, e) { var commentElm = getCommentsContainer().find('#'+ commentId); commentElm.show(); - highlightComment(commentId, e, false); + highlightComment(commentId, e); }; var hideComment = function(commentId, hideCommentTitle) { var commentElm = getCommentsContainer().find('#'+ commentId); - commentElm.removeClass('mouseover'); + commentElm.removeClass('full-display'); // hide even the comment title if (hideCommentTitle) commentElm.hide(); - getPadOuter().find('.comment-modal').hide(); -}; - -var hideOpenedComments = function() { - var openedComments = getCommentsContainer().find('.mouseover'); - openedComments.removeClass('mouseover').hide(); + var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]'); + inner.contents().find("head .comment-style").remove(); - getPadOuter().find('.comment-modal').hide(); -} + getPadOuter().find('.comment-modal').removeClass('popup-show'); +}; 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, hideEditAndRemoveCommentWindow){ +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'); - } else { - // make a full copy of the html, including listeners - var commentElm = container.find('#'+ commentId).parent().clone(true, true); + var inner = $('iframe[name="ace_outer"]').contents().find('iframe[name="ace_inner"]'); - // clean styles - commentElm.children().removeAttr("style"); + 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') + }); - // only show the comment of the text selected - commentElm.find('note').not('#' + commentId).hide(); + // 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(""); + } else { + // make a full copy of the html, including listeners + var commentElmCloned = commentElm.clone(true, true); - // before of appending it, we remove the classes that only makes sense on the side-bar - commentElm.children().attr('class', ''); + // 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()); - - // if hideEditAndRemoveCommentWindow is true, it hides the comment edit/remove window - getPadOuter().find('.comment-options-wrapper').toggleClass('hidden', hideEditAndRemoveCommentWindow); - + getPadOuter().find('.comment-modal-comment').html('').append(commentElmCloned); + var padInner = getPadOuter().find('iframe[name="ace_inner"]') // get modal position var containerWidth = getPadOuter().find('#outerdocbody').outerWidth(true); var modalWitdh = getPadOuter().find('.comment-modal').outerWidth(true); var targetLeft = e.clientX; 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" }); } } @@ -86,8 +99,7 @@ var highlightComment = function(commentId, e, hideEditAndRemoveCommentWindow){ // height of the pad text associated to the comment, and return the affected element var adjustTopOf = function(commentId, baseTop) { var commentElement = getPadOuter().find('#'+commentId); - var targetTop = baseTop - 5; - commentElement.css("top", targetTop+"px"); + commentElement.css("top", baseTop+"px"); return commentElement; } @@ -95,7 +107,7 @@ var adjustTopOf = function(commentId, baseTop) { // Indicates if comment is on the expected position (baseTop-5) var isOnTop = function(commentId, baseTop) { var commentElement = getPadOuter().find('#'+commentId); - var expectedTop = (baseTop - 5) + "px"; + var expectedTop = baseTop + "px"; return commentElement.css("top") === expectedTop; } @@ -110,7 +122,6 @@ var shouldNotCloseComment = function(e) { exports.showComment = showComment; exports.hideComment = hideComment; -exports.hideOpenedComments = hideOpenedComments; exports.hideAllComments = hideAllComments; exports.highlightComment = highlightComment; exports.adjustTopOf = adjustTopOf; diff --git a/static/js/commentIcons.js b/static/js/commentIcons.js index c89c30de..184e9772 100644 --- a/static/js/commentIcons.js +++ b/static/js/commentIcons.js @@ -7,31 +7,6 @@ var displayIcons = function() { return clientVars.displayCommentAsIcon } -// 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 availableSpaceOnTheRightOfPadLines = getSpaceAvailableOnTheRightSide($firstElementOnPad); - - screenHasSpaceToDisplayIcons = availableSpaceOnTheRightOfPadLines !== 0; -} - -// The space available can be anything like padding, margin or border -var getSpaceAvailableOnTheRightSide = function($element) { - var rightPadding = parseInt($element.css("padding-right"), 10); - var rightBorder = parseInt($element.css("border-right-width"), 10); - var rightMargin = parseInt($element.css("margin-right"), 10); - - var rightEdgeSpace = rightPadding + rightBorder + rightMargin; - return rightEdgeSpace; -} - // Easier access to outer pad var padOuter; var getPadOuter = function() { @@ -69,12 +44,11 @@ var targetCommentIdOf = function(e) { } var highlightTargetTextOf = function(commentId) { - getPadInner().find("head").append(""); + getPadInner().find("head").append(""); } -var removeHighlightOfTargetTextOf = function(commentId) { - getPadInner().find("head").append(""); - // TODO this could potentially break ep_font_color +var removeHighlightTargetText = function(commentId) { + getPadInner().find("head .comment-style").remove(); } var toggleActiveCommentIcon = function(target) { @@ -83,11 +57,12 @@ var toggleActiveCommentIcon = function(target) { var addListenersToCommentIcons = function() { getPadOuter().find('#commentIcons').on("mouseover", ".comment-icon", function(e){ + removeHighlightTargetText(); var commentId = targetCommentIdOf(e); highlightTargetTextOf(commentId); }).on("mouseout", ".comment-icon", function(e){ var commentId = targetCommentIdOf(e); - removeHighlightOfTargetTextOf(commentId); + removeHighlightTargetText(); }).on("click", ".comment-icon.active", function(e){ toggleActiveCommentIcon($(this)); @@ -96,27 +71,15 @@ var addListenersToCommentIcons = function() { }).on("click", ".comment-icon.inactive", function(e){ // deactivate/hide other comment boxes that are opened, so we have only // one comment box opened at a time - commentBoxes.hideOpenedComments(); + commentBoxes.hideAllComments(); var allActiveIcons = getPadOuter().find('#commentIcons').find(".comment-icon.active"); toggleActiveCommentIcon(allActiveIcons); // activate/show only target comment toggleActiveCommentIcon($(this)); var commentId = targetCommentIdOf(e); - commentBoxes.showComment(commentId, e, true); - }); -} - -// Listen to Page View enabling/disabling, to adjust #commentIcons position -var addListenersToPageView = function() { - $("#options-pageview").on("click", function() { - getPadOuter().find('#outerdocbody').toggleClass("pageViewDisabled"); + commentBoxes.highlightComment(commentId, e); }); - - // 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 @@ -165,11 +128,9 @@ var insertContainer = function() { if (!displayIcons()) return; getPadOuter().find("#sidediv").after('
'); - - adjustIconsForNewScreenSize(); + getPadOuter().find("#comments").addClass('with-icons'); addListenersToCommentIcons(); addListenersToCloseOpenedComment(); - addListenersToPageView(); } // Create a new comment icon @@ -178,7 +139,7 @@ var addIcon = function(commentId, comment){ if (!displayIcons()) return; var inlineComment = getPadInner().find(".comment."+commentId); - var top = inlineComment.get(0).offsetTop + 5; + var top = inlineComment.get(0).offsetTop; var iconsAtLine = getOrCreateIconsContainerAt(top); var icon = $('#commentIconTemplate').tmpl(comment); @@ -188,7 +149,7 @@ var addIcon = function(commentId, comment){ // Hide comment icons from container var hideIcons = function() { // we're only doing something if icons will be displayed at all - if (!displayIcons() || !screenHasSpaceForIcons()) return; + if (!displayIcons()) return; getPadOuter().find('#commentIcons').children().children().each(function(){ $(this).hide(); @@ -199,10 +160,10 @@ var hideIcons = function() { // height of the pad text associated to the comment, and return the affected icon var adjustTopOf = function(commentId, baseTop) { // 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); // move icon from one line to the other @@ -217,7 +178,7 @@ var adjustTopOf = function(commentId, baseTop) { // comment icon. var isCommentOpenedByClickOnIcon = function() { // we're only doing something if icons will be displayed at all - if (!displayIcons() || !screenHasSpaceForIcons()) return false; + if (!displayIcons()) return false; var iconClicked = getPadOuter().find('#commentIcons').find(".comment-icon.active"); var commentOpenedByClickOnIcon = iconClicked.length !== 0; @@ -241,7 +202,7 @@ var commentHasReply = function(commentId) { var shouldShow = function(sidebarComent) { var shouldShowComment = false; - if (!displayIcons() || !screenHasSpaceForIcons()) { + if (!displayIcons()) { // if icons are not being displayed, we always show comments shouldShowComment = true; } else if (sidebarComent.hasClass("mouseover")) { @@ -252,21 +213,6 @@ var shouldShow = function(sidebarComent) { return shouldShowComment; } -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) var shouldNotCloseComment = function(e) { return $(e.target).closest('.comment-icon').length !== 0; @@ -279,5 +225,4 @@ exports.adjustTopOf = adjustTopOf; exports.isCommentOpenedByClickOnIcon = isCommentOpenedByClickOnIcon; exports.commentHasReply = commentHasReply; exports.shouldShow = shouldShow; -exports.adjustIconsForNewScreenSize = adjustIconsForNewScreenSize; exports.shouldNotCloseComment = shouldNotCloseComment; diff --git a/static/js/index.js b/static/js/index.js index 4f4f53f5..0270eb50 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -51,13 +51,6 @@ function ep_comments(context){ 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( - ""); - } } // Init Etherpad plugin comment pads @@ -101,28 +94,16 @@ ep_comments.prototype.init = function(){ // all templates are localized html10n.bind('localized', function() { 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(); - }); - - // When Page View is enabled/disabled, we need to recalculate position of comments - $('#options-pageview').on('click', function(e) { - self.editorResized(); - }); - // When Page Breaks are enabled/disabled, we need to recalculate position of comments - $('#options-pagebreaks').on('click', function(e) { - self.editorResized(); + // Recalculate position when editor is resized + $('#settings input, #skin-variant-full-width').on('change', function(e) { + self.setYofComments(); }); - - // Allow recalculating the comments position by event this.padInner.contents().on(UPDATE_COMMENT_LINE_POSITION_EVENT, function(e){ - self.editorResized(); + self.setYofComments(); }); + $(window).resize(_.debounce( function() { self.setYofComments() }, 100 ) ); // On click comment icon toolbar $('.addComment').on('click', function(e){ @@ -130,38 +111,64 @@ ep_comments.prototype.init = function(){ self.displayNewCommentForm(); }); + // 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.parent().on("click", ".comment-delete", function(){ - var commentId = $(this).closest('note')[0].id; + 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 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('note'); - - // hide the option window when it show the edit form - var $commentOptions = $commentBox.children('.comment-options'); // edit, delete actions - $commentOptions.addClass('hidden'); - - // hide the comment author name and the comment text - $commentBox.children('.comment-author-name, .comment-text').addClass('hidden'); - self.addCommentEditFormIfDontExist($commentBox); - - // place original text on the edit form - var originalText = $commentBox.children('.comment-text').text(); - $commentBox.find('.comment-edit-text').text(originalText); + var $commentBox = $(this).closest('.comment-container'); + $commentBox.addClass('editing'); + + 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); + } }); // 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('note'); + var $commentBox = $(this).closest('.comment-container'); + var $commentForm = $(this).closest('.comment-edit-form'); var commentId = $commentBox.data('commentid'); - var commentText = $commentBox.find('.comment-edit-text')[0].value; + var commentText = $commentForm.find('.comment-edit-text').val(); var data = {}; data.commentId = commentId; data.padId = clientVars.padId; @@ -169,8 +176,8 @@ ep_comments.prototype.init = function(){ self.socket.emit('updateCommentText', data, function (err){ if(!err) { - $commentBox.children('.comment-edit-form').remove(); - $commentBox.children('.comment-author-name, .comment-text').removeClass('hidden'); + $commentForm.remove(); + $commentBox.removeClass('editing'); self.updateCommentBoxText(commentId, commentText); // although the comment or reply was saved on the data base successfully, it needs @@ -184,101 +191,54 @@ ep_comments.prototype.init = function(){ this.container.parent().on("click", ".comment-edit-cancel", function(e){ e.preventDefault(); e.stopPropagation(); - var $commentBox = $(this).closest('note'); - $commentBox.children('.comment-edit-form').remove(); - $commentBox.children('.comment-author-name, .comment-text').removeClass('hidden'); + var $commentBox = $(this).closest('.comment-container'); + var textBox = self.findCommentText($commentBox).last(); + textBox.siblings('.comment-edit-form').remove(); + $commentBox.removeClass('editing'); }); // Listen for include suggested change toggle - this.container.on("change", '.reply-suggestion-checkbox', function(){ + this.container.parent().on("change", '.suggestion-checkbox', function(){ + var parentComment = $(this).closest('.comment-container'); + var parentSuggest = $(this).closest('.comment-reply'); + if($(this).is(':checked')){ - var commentId = $(this).parent().parent().parent().data('commentid'); + var commentId = parentComment.data('commentid'); var padOuter = $('iframe[name="ace_outer"]').contents(); var padInner = padOuter.find('iframe[name="ace_inner"]'); 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"); - } - }); - - - // Create hover modal - $('iframe[name="ace_outer"]').contents().find("body") - .append("