From f0e60a26379ae4fb89e584d3e1adb988a7441e0f Mon Sep 17 00:00:00 2001 From: benweet Date: Sun, 6 Apr 2014 01:59:32 +0100 Subject: [PATCH] Stability fixes --- public/res/classes/Provider.js | 87 ++--- public/res/core.js | 7 +- public/res/editor.js | 339 ++++++++++---------- public/res/extensions/comments.js | 106 ++++-- public/res/html/bodyIndex.html | 4 +- public/res/html/commentsPopoverContent.html | 32 +- public/res/styles/main.less | 66 ++-- 7 files changed, 342 insertions(+), 299 deletions(-) diff --git a/public/res/classes/Provider.js b/public/res/classes/Provider.js index 4cab7c34..14f12681 100644 --- a/public/res/classes/Provider.js +++ b/public/res/classes/Provider.js @@ -27,12 +27,9 @@ define([ ) { throw 'invalid'; } - if(discussion.type == 'conflict') { - return; - } - discussion.commentList.forEach(function(comment) { + discussion.commentList && discussion.commentList.forEach(function(comment) { if( - (!_.isString(comment.author)) || + (!(!comment.author || _.isString(comment.author))) || (!_.isString(comment.content)) ) { throw 'invalid'; @@ -46,7 +43,7 @@ define([ }; Provider.prototype.serializeContent = function(content, discussionList) { - if(discussionList.length > 2) { // It's a serialized JSON + if(discussionList.length > 2) { // Serialized JSON return content + ''; } return content; @@ -86,24 +83,29 @@ define([ var addDiff = [1, '']; var distance = 20; function pushDiff() { - var separator = '///'; - if(removeDiff[1]) { - removeDiff[1] = '///' + removeDiff[1] + separator; - separator = ''; - result.push(removeDiff); + if(!removeDiff[1] && !addDiff[1]) { + return; } - if(addDiff[1]) { - addDiff[1] = separator + addDiff[1] + '///'; + if(!removeDiff[1] || !addDiff[1]) { + result.push([0, removeDiff[1] + addDiff[1]]); + } + else { + removeDiff[1] = '///' + removeDiff[1] + '///'; + addDiff[1] += '///'; + result.push(removeDiff); result.push(addDiff); } removeDiff = [-1, '']; addDiff = [1, '']; } - diffs.forEach(function(diff) { + diffs.forEach(function(diff, index) { + function firstOrLast() { + return index === 0 || index === diffs.length - 1; + } var diffType = diff[0]; var diffText = diff[1]; if(diffType === 0) { - if(diffText.length > distance) { + if(firstOrLast() || diffText.length > distance) { if(removeDiff[1] || addDiff[1]) { var match = /\s/.exec(diffText); if(match) { @@ -121,7 +123,7 @@ define([ } var suffix = diffText.substring(suffixOffset); diffText = diffText.substring(0, suffixOffset); - if(diffText.length > distance) { + if(firstOrLast() || diffText.length > distance) { pushDiff(); result.push([0, diffText]); } @@ -154,51 +156,6 @@ define([ return result; } - function moveComments(oldTextContent, newTextContent, discussionList) { - if(!discussionList.length) { - return; - } - var changes = diffMatchPatch.diff_main(oldTextContent, newTextContent); - var changed = false; - var startOffset = 0; - changes.forEach(function(change) { - var changeType = change[0]; - var changeText = change[1]; - if(changeType === 0) { - startOffset += changeText.length; - return; - } - var endOffset = startOffset; - var diffOffset = changeText.length; - if(changeType === -1) { - endOffset += diffOffset; - diffOffset = -diffOffset; - } - discussionList.forEach(function(discussion) { - // selectionEnd - if(discussion.selectionEnd >= endOffset) { - discussion.selectionEnd += diffOffset; - discussion.discussionIndex && (changed = true); - } - else if(discussion.selectionEnd > startOffset) { - discussion.selectionEnd = startOffset; - discussion.discussionIndex && (changed = true); - } - // selectionStart - if(discussion.selectionStart >= endOffset) { - discussion.selectionStart += diffOffset; - discussion.discussionIndex && (changed = true); - } - else if(discussion.selectionStart > startOffset) { - discussion.selectionStart = startOffset; - discussion.discussionIndex && (changed = true); - } - }); - startOffset = endOffset; - }); - return changed; - } - var localContent = fileDesc.content; var localTitle = fileDesc.title; var localDiscussionListJSON = fileDesc.discussionListJSON; @@ -314,7 +271,7 @@ define([ } } - // Adjust local discussions offset + // Adjust local discussions offsets var editorSelection; if(contentChanged) { var localDiscussionArray = []; @@ -330,13 +287,13 @@ define([ if(adjustLocalDiscussionList) { localDiscussionArray = localDiscussionArray.concat(_.values(localDiscussionList)); } - discussionListChanged |= moveComments(localContent, newContent, localDiscussionArray); + discussionListChanged |= editor.adjustCommentOffsets(localContent, newContent, localDiscussionArray); } - // Adjust remote discussions offset + // Adjust remote discussions offsets if(adjustRemoteDiscussionList) { var remoteDiscussionArray = _.values(remoteDiscussionList); - moveComments(remoteContent, newContent, remoteDiscussionArray); + editor.adjustCommentOffsets(remoteContent, newContent, remoteDiscussionArray); } // Patch remote discussionList with local modifications diff --git a/public/res/core.js b/public/res/core.js index fbc3e84e..26085394 100644 --- a/public/res/core.js +++ b/public/res/core.js @@ -359,7 +359,7 @@ define([ var $titleContainer; var marginWidth = 18 * 2 + 25 + 25; var titleWidth = 18 + 348; - var leftButtonsWidth = 18 * 4 + 80 + 160 + 160 + 80; + var leftButtonsWidth = 18 * 4 + 80 + 160 + 200 + 80; var rightButtonsWidth = 18 + 80; var buttonsDropdownWidth = 40; function adjustWindow() { @@ -394,7 +394,8 @@ define([ if(pagedownEditor !== undefined) { // If the editor is already created - return editor.undoMgr.init(); + editor.undoMgr.init(); + return pagedownEditor.uiManager.setUndoRedoButtonStates(); } // Create the converter and the editor @@ -450,10 +451,12 @@ define([ $("#wmd-code-button").append($('')).appendTo($btnGroupElt); $("#wmd-image-button").append($('')).appendTo($btnGroupElt); $btnGroupElt = $('.wmd-button-group3'); + var $openDiscussionElt = $btnGroupElt.find('.button-open-discussion'); $("#wmd-olist-button").append($('')).appendTo($btnGroupElt); $("#wmd-ulist-button").append($('')).appendTo($btnGroupElt); $("#wmd-heading-button").append($('')).appendTo($btnGroupElt); $("#wmd-hr-button").append($('')).appendTo($btnGroupElt); + $openDiscussionElt.appendTo($btnGroupElt); $btnGroupElt = $('.wmd-button-group4'); $("#wmd-undo-button").append($('')).appendTo($btnGroupElt); $("#wmd-redo-button").append($('')).appendTo($btnGroupElt); diff --git a/public/res/editor.js b/public/res/editor.js index 51e1e93e..0c946242 100644 --- a/public/res/editor.js +++ b/public/res/editor.js @@ -1,4 +1,5 @@ /* jshint -W084, -W099 */ +// Credit to http://dabblet.com/ define([ 'jquery', 'underscore', @@ -8,15 +9,10 @@ define([ 'diff_match_patch_uncompressed', 'jsondiffpatch', 'crel', + 'rangy', 'MutationObservers', 'libs/prism-markdown' -], function ($, _, settings, eventMgr, Prism, diff_match_patch, jsondiffpatch, crel) { - - function strSplice(str, i, remove, add) { - remove = +remove || 0; - add = add || ''; - return str.slice(0, i) + add + str.slice(i + remove); - } +], function ($, _, settings, eventMgr, Prism, diff_match_patch, jsondiffpatch, crel, rangy) { var editor = {}; var scrollTop = 0; @@ -110,6 +106,7 @@ define([ }); function SelectionMgr() { + var self = this; this.selectionStart = 0; this.selectionEnd = 0; this.cursorY = 0; @@ -147,21 +144,26 @@ define([ if(end === undefined) { end = this.selectionEnd; } + var maxOffset = inputElt.textContent.length - 1; + if(start > maxOffset) { + start = maxOffset; + range = undefined; + skipSelectionUpdate = false; + } + if(end > maxOffset) { + end = maxOffset; + range = undefined; + skipSelectionUpdate = false; + } + this.selectionStart = start; + this.selectionEnd = end; var min = Math.min(start, end); var max = Math.max(start, end); range = range || this.createRange(min, max); - if(start < end || !skipSelectionUpdate) { - this.selectionStart = min; - this.selectionEnd = max; - } - else { - this.selectionStart = max; - this.selectionEnd = min; - } if(!skipSelectionUpdate) { - var selection = window.getSelection(); + var selection = rangy.getSelection(); selection.removeAllRanges(); - selection.addRange(range); + selection.addRange(range, start > end); } fileDesc.editorStart = this.selectionStart; fileDesc.editorEnd = this.selectionEnd; @@ -174,47 +176,42 @@ define([ } return range; }; - this.saveSelectionState = function(skipSelectionUpdate) { - if(fileChanged === false) { - var selection = window.getSelection(); - if(!skipSelectionUpdate && selection.rangeCount > 0) { - var range = selection.getRangeAt(0); - var element = range.startContainer; + this.saveSelectionState = (function() { + function save() { + if(fileChanged === false) { + var selection = rangy.getSelection(); + if(selection.rangeCount > 0) { + var range = selection.getRangeAt(0); + var element = range.startContainer; - if ((inputElt.compareDocumentPosition(element) & 0x10)) { - var container = element; - var offset = range.startOffset; - do { - while (element = element.previousSibling) { - if (element.textContent) { - offset += element.textContent.length; + if ((contentElt.compareDocumentPosition(element) & 0x10)) { + var container = element; + var offset = range.startOffset; + do { + while (element = element.previousSibling) { + if (element.textContent) { + offset += element.textContent.length; + } } + element = container = container.parentNode; + } while (element && element != inputElt); + + if(selection.isBackwards()) { + self.setSelectionStartEnd(offset + (range + '').length, offset, range, true); + } + else { + self.setSelectionStartEnd(offset, offset + (range + '').length, range, true); } - - element = container = container.parentNode; - } while (element && element != inputElt); - - // Determine if it's a backward selection - var isBackwardSelection = false; - if (!selection.isCollapsed) { - var tmpRange = document.createRange(); - tmpRange.setStart(selection.anchorNode, selection.anchorOffset); - tmpRange.setEnd(selection.focusNode, selection.focusOffset); - isBackwardSelection = tmpRange.collapsed; - tmpRange.detach(); - } - - if(isBackwardSelection) { - this.setSelectionStartEnd(offset + (range + '').length, offset, range, true); - } - else { - this.setSelectionStartEnd(offset, offset + (range + '').length, range, true); } } } + undoMgr.saveSelectionState(); } - undoMgr.saveSelectionState(); - }; + var debouncedSave = _.debounce(save, 0); + return function(debounced) { + debounced ? debouncedSave() : save(); + }; + })(); this.getCoordinates = function(inputOffset, container, offset) { if(!container) { offset = this.findOffset(inputOffset); @@ -236,7 +233,7 @@ define([ container: container, offset: offset }; - if(selectedChar === undefined || selectedChar == '\n') { + if(inputOffset > 0 && (selectedChar === undefined || selectedChar == '\n')) { if(startOffset.offset === 0) { startOffset = inputOffset - 1; } @@ -265,31 +262,41 @@ define([ } var selectionMgr = new SelectionMgr(); editor.selectionMgr = selectionMgr; + $(document).on('selectionchange', function() { + selectionMgr.saveSelectionState(true); + }); - var adjustCursorPosition = _.debounce(function() { - if(inputElt === undefined) { - return; - } - selectionMgr.saveSelectionState(); - - var adjust = inputElt.offsetHeight / 2; - if(adjust > 130) { - adjust = 130; - } - var cursorMinY = inputElt.scrollTop + adjust; - var cursorMaxY = inputElt.scrollTop + inputElt.offsetHeight - adjust; - if(selectionMgr.cursorY < cursorMinY) { - inputElt.scrollTop += selectionMgr.cursorY - cursorMinY; - } - else if(selectionMgr.cursorY > cursorMaxY) { - inputElt.scrollTop += selectionMgr.cursorY - cursorMaxY; - } - }, 0); + var adjustCursorPosition = (function() { + var adjust = _.debounce(function() { + var adjust = inputElt.offsetHeight / 2; + if(adjust > 130) { + adjust = 130; + } + var cursorMinY = inputElt.scrollTop + adjust; + var cursorMaxY = inputElt.scrollTop + inputElt.offsetHeight - adjust; + if(selectionMgr.cursorY < cursorMinY) { + inputElt.scrollTop += selectionMgr.cursorY - cursorMinY; + } + else if(selectionMgr.cursorY > cursorMaxY) { + inputElt.scrollTop += selectionMgr.cursorY - cursorMaxY; + } + }, 0); + return function() { + if(inputElt === undefined) { + return; + } + selectionMgr.saveSelectionState(true); + adjust(); + }; + })(); eventMgr.addListener('onLayoutResize', adjustCursorPosition); var textContent; function setValue(value) { var startOffset = diffMatchPatch.diff_commonPrefix(textContent, value); + if(startOffset === textContent.length) { + startOffset--; + } var endOffset = Math.min( diffMatchPatch.diff_commonSuffix(textContent, value), textContent.length - startOffset, @@ -310,7 +317,7 @@ define([ function getValue() { return textContent; } - editor.setValueNoWatch = getValue; + editor.getValue = getValue; function UndoMgr() { var undoStack = []; @@ -459,48 +466,10 @@ define([ currentTextContent += '\n'; } undoMgr.currentMode = undoMgr.currentMode || 'typing'; - var changes = diffMatchPatch.diff_main(textContent, currentTextContent); - textContent = currentTextContent; - // Move comments according to changes - var updateDiscussionList = false; - var startOffset = 0; var discussionList = _.values(fileDesc.discussionList); fileDesc.newDiscussion && discussionList.push(fileDesc.newDiscussion); - changes.forEach(function(change) { - var changeType = change[0]; - var changeText = change[1]; - if(changeType === 0) { - startOffset += changeText.length; - return; - } - var endOffset = startOffset; - var diffOffset = changeText.length; - if(changeType === -1) { - endOffset += diffOffset; - diffOffset = -diffOffset; - } - discussionList.forEach(function(discussion) { - // selectionEnd - if(discussion.selectionEnd >= endOffset) { - discussion.selectionEnd += diffOffset; - updateDiscussionList = true; - } - else if(discussion.selectionEnd > startOffset) { - discussion.selectionEnd = startOffset; - updateDiscussionList = true; - } - // selectionStart - if(discussion.selectionStart >= endOffset) { - discussion.selectionStart += diffOffset; - updateDiscussionList = true; - } - else if(discussion.selectionStart > startOffset) { - discussion.selectionStart = startOffset; - updateDiscussionList = true; - } - }); - startOffset = endOffset; - }); + var updateDiscussionList = adjustCommentOffsets(textContent, currentTextContent, discussionList); + textContent = currentTextContent; if(updateDiscussionList === true) { fileDesc.discussionList = fileDesc.discussionList; // Write discussionList in localStorage } @@ -525,12 +494,57 @@ define([ } } + function adjustCommentOffsets(oldTextContent, newTextContent, discussionList) { + if(!discussionList.length) { + return; + } + var changes = diffMatchPatch.diff_main(oldTextContent, newTextContent); + var changed = false; + var startOffset = 0; + changes.forEach(function(change) { + var changeType = change[0]; + var changeText = change[1]; + if(changeType === 0) { + startOffset += changeText.length; + return; + } + var endOffset = startOffset; + var diffOffset = changeText.length; + if(changeType === -1) { + endOffset += diffOffset; + diffOffset = -diffOffset; + } + discussionList.forEach(function(discussion) { + // selectionEnd + if(discussion.selectionEnd > endOffset) { + discussion.selectionEnd += diffOffset; + discussion.discussionIndex && (changed = true); + } + else if(discussion.selectionEnd > startOffset) { + discussion.selectionEnd = startOffset; + discussion.discussionIndex && (changed = true); + } + // selectionStart + if(discussion.selectionStart >= endOffset) { + discussion.selectionStart += diffOffset; + discussion.discussionIndex && (changed = true); + } + else if(discussion.selectionStart > startOffset) { + discussion.selectionStart = startOffset; + discussion.discussionIndex && (changed = true); + } + }); + if(changeType === 1) { + startOffset += changeText.length; + } + }); + return changed; + } + editor.adjustCommentOffsets = adjustCommentOffsets; + editor.init = function(elt1, elt2) { inputElt = elt1; $inputElt = $(inputElt); - editor.inputElt = inputElt; - editor.$inputElt = $inputElt; - previewElt = elt2; contentElt = crel('div', { @@ -538,10 +552,8 @@ define([ contenteditable: true }); inputElt.appendChild(contentElt); - editor.contentElt = contentElt; $contentElt = $(contentElt); editor.$contentElt = $contentElt; - marginElt = crel('div', { class: 'editor-margin' }); @@ -568,12 +580,6 @@ define([ selectionMgr.setSelectionStartEnd(); inputElt.scrollTop = scrollTop; }; - $contentElt.focus(function() { - inputElt.focused = true; - }); - $contentElt.blur(function() { - inputElt.focused = false; - }); Object.defineProperty(inputElt, 'value', { get: function () { @@ -584,7 +590,7 @@ define([ Object.defineProperty(inputElt, 'selectionStart', { get: function () { - return selectionMgr.selectionStart; + return Math.min(selectionMgr.selectionStart, selectionMgr.selectionEnd); }, set: function (value) { selectionMgr.setSelectionStartEnd(value); @@ -596,7 +602,7 @@ define([ Object.defineProperty(inputElt, 'selectionEnd', { get: function () { - return selectionMgr.selectionEnd; + return Math.max(selectionMgr.selectionStart, selectionMgr.selectionEnd); }, set: function (value) { selectionMgr.setSelectionStartEnd(undefined, value); @@ -642,9 +648,7 @@ define([ } }) .on('mouseup', function() { - setTimeout(function() { - selectionMgr.saveSelectionState(); - }, 0); + selectionMgr.saveSelectionState(true); }) .on('paste', function () { undoMgr.currentMode = 'paste'; @@ -658,52 +662,52 @@ define([ var action = function (action, options) { options = options || {}; - var text = inputElt.value, - ss = options.start || selectionMgr.selectionStart, - se = options.end || selectionMgr.selectionEnd, - state = { - ss: ss, - se: se, - before: text.slice(0, ss), - after: text.slice(se), - selection: text.slice(ss, se) - }; + var textContent = getValue(); + var selectionStart = options.start || selectionMgr.selectionStart; + var selectionEnd = options.end || selectionMgr.selectionEnd; + var state = { + selectionStart: selectionStart, + selectionEnd: selectionEnd, + before: textContent.slice(0, selectionStart), + after: textContent.slice(selectionEnd), + selection: textContent.slice(selectionStart, selectionEnd) + }; actions[action](state, options); - inputElt.value = state.before + state.selection + state.after; - selectionMgr.setSelectionStartEnd(state.ss, state.se); + setValue(state.before + state.selection + state.after); + selectionMgr.setSelectionStartEnd(state.selectionStart, state.selectionEnd); $inputElt.trigger('input'); }; var actions = { indent: function (state, options) { + function strSplice(str, i, remove, add) { + remove = +remove || 0; + add = add || ''; + return str.slice(0, i) + add + str.slice(i + remove); + } var lf = state.before.lastIndexOf('\n') + 1; - if (options.inverse) { if (/\s/.test(state.before.charAt(lf))) { state.before = strSplice(state.before, lf, 1); - state.ss--; - state.se--; + state.selectionStart--; + state.selectionEnd--; } - state.selection = state.selection.replace(/^[ \t]/gm, ''); } else if (state.selection) { state.before = strSplice(state.before, lf, 0, '\t'); state.selection = state.selection.replace(/\r?\n(?=[\s\S])/g, '\n\t'); - - state.ss++; - state.se++; + state.selectionStart++; + state.selectionEnd++; } else { state.before += '\t'; - - state.ss++; - state.se++; - + state.selectionStart++; + state.selectionEnd++; return; } - state.se = state.ss + state.selection.length; + state.selectionEnd = state.selectionStart + state.selection.length; }, newline: function (state) { @@ -711,8 +715,8 @@ define([ if(clearNewline) { state.before = state.before.substring(0, lf); state.selection = ''; - state.ss = lf; - state.se = lf; + state.selectionStart = lf; + state.selectionEnd = lf; clearNewline = false; return; } @@ -732,8 +736,8 @@ define([ state.before += '\n' + indent; state.selection = ''; - state.ss += indent.length + 1; - state.se = state.ss; + state.selectionStart += indent.length + 1; + state.selectionEnd = state.selectionStart; }, }; }; @@ -843,17 +847,18 @@ define([ }); } - var entityMap = { - "&": "&", - "<": "<", - "\u00a0": ' ', - }; - - function escape(str) { - return str.replace(/[&<\u00a0]/g, function(s) { - return entityMap[s]; - }); - } + var escape = (function() { + var entityMap = { + "&": "&", + "<": "<", + "\u00a0": ' ', + }; + return function(str) { + return str.replace(/[&<\u00a0]/g, function(s) { + return entityMap[s]; + }); + }; + })(); function highlight(section) { var text = escape(section.text); diff --git a/public/res/extensions/comments.js b/public/res/extensions/comments.js index 719b15cd..85d015d8 100644 --- a/public/res/extensions/comments.js +++ b/public/res/extensions/comments.js @@ -10,7 +10,7 @@ define([ "bootstrap" ], function($, _, utils, storage, crel, rangy, Extension, commentsPopoverContentHTML) { - var comments = new Extension("comments", 'Comments'); + var comments = new Extension("comments", 'Comments', false, true); var commentTmpl = [ '
', @@ -40,14 +40,15 @@ define([ var offsetMap = {}; function setCommentEltCoordinates(commentElt, y) { var lineIndex = Math.round(y / 10); - var yOffset = -10; + var yOffset = -9; if(commentElt.className.indexOf(' icon-split') !== -1) { yOffset = -12; } - var top = (y + yOffset) + 'px'; - var right = ((offsetMap[lineIndex] || 0) * 25 + 10) + 'px'; - commentElt.style.top = top; - commentElt.style.right = right; + var top = y + yOffset; + var right = (offsetMap[lineIndex] || 0) * 25 + 10; + commentElt.orderedIndex = lineIndex * 10000 + right; + commentElt.style.top = top + 'px'; + commentElt.style.right = right + 'px'; return lineIndex; } @@ -87,17 +88,27 @@ define([ } popoverElt.style.left = left + 'px'; popoverElt.querySelector('.arrow').style.left = (marginElt.offsetWidth - parseInt(commentElt.style.right) - commentElt.offsetWidth / 2 - left) + 'px'; + var $contentInputElt = currentContext.$contentInputElt; + var popoverTopOffset = document.body.offsetHeight - $contentInputElt.offset().top - $contentInputElt.outerHeight(); + if(popoverTopOffset < 0) { + popoverElt.style.top = (parseInt(popoverElt.style.top) + popoverTopOffset) + 'px'; + } } var cssApplier; var currentFileDesc; var refreshTimeoutId; var commentEltMap = {}; + var sortedCommentEltList = []; + var someReplies = false; + var $openDiscussionElt; + var $openDiscussionIconElt; var refreshDiscussions = _.debounce(function() { if(currentFileDesc === undefined) { return; } - + someReplies = false; + sortedCommentEltList = []; var author = storage['author.name']; offsetMap = {}; var discussionList = _.values(currentFileDesc.discussionList); @@ -116,24 +127,32 @@ define([ inputElt.scrollTop += parseInt(newCommentElt.style.top) - inputElt.scrollTop - inputElt.offsetHeight * 3 / 4; movePopover(newCommentElt); } + sortedCommentEltList = _.sortBy(commentEltMap, function(commentElt) { + return commentElt.orderedIndex; + }); + $openDiscussionElt.toggleClass('some', sortedCommentEltList.length !== 0); + $openDiscussionElt.toggleClass('replied', someReplies); + $openDiscussionIconElt.toggleClass('icon-chat', sortedCommentEltList.length !== 0); return; } var discussion = discussionList.pop(); - var commentElt = crel('a', { - class: 'discussion' - }); - var isReplied = !discussion.commentList || _.last(discussion.commentList).author != author; - commentElt.className += ' icon-quote-left' + (isReplied ? ' replied' : ' added'); - if(discussion.type == 'conflict') { - commentElt.className += ' icon-split'; + var commentElt = commentEltMap[discussion.discussionIndex]; + if(!commentElt) { + commentElt = crel('a'); } + var className = 'discussion'; + var isReplied = !discussion.commentList || _.last(discussion.commentList).author != author; + isReplied && (someReplies = true); + className += ' icon-quote-left' + (isReplied ? ' replied' : ' added'); + if(discussion.type == 'conflict') { + className += ' icon-split'; + } + commentElt.className = className; commentElt.discussionIndex = discussion.discussionIndex; var coordinates = editor.selectionMgr.getCoordinates(discussion.selectionEnd); var lineIndex = setCommentEltCoordinates(commentElt, coordinates.y); offsetMap[lineIndex] = (offsetMap[lineIndex] || 0) + 1; - var oldCommentElt = commentEltMap[discussion.discussionIndex]; - oldCommentElt && marginElt.removeChild(oldCommentElt); marginElt.appendChild(commentElt); commentEltMap[discussion.discussionIndex] = commentElt; @@ -225,7 +244,7 @@ define([ if(discussion.type == 'conflict') { result.unshift(_.template(commentTmpl, { author: 'StackEdit', - content: 'Multiple users have made conflicting modifications that you have to review.', + content: 'Multiple users have made conflicting modifications.', reply: true })); } @@ -276,6 +295,9 @@ define([ }).on('show.bs.popover', '#wmd-input > .editor-margin', function(evt) { closeCurrentPopover(); var context = new Context(evt.target, currentFileDesc); + if(evt.target !== newCommentElt) { + $newCommentElt.addClass('hide'); + } currentContext = context; inputElt.scrollTop += parseInt(evt.target.style.top) - inputElt.scrollTop - inputElt.offsetHeight * 3 / 4; @@ -295,15 +317,15 @@ define([ var match = /\S+/.exec(after); if(match) { selectionStart += match.index; - if(match.index === 0) { - while(selectionStart && /\S/.test(textContent[selectionStart - 1])) { - selectionStart--; - } + selectionEnd = selectionStart + match[0].length; + } + if(!match || match.index === 0) { + while(selectionStart && /\S/.test(textContent[selectionStart - 1])) { + selectionStart--; } - selectionEnd += match.index + match[0].length; } } - context.selectionRange = editor.selectionMgr.createRange(selectionStart, selectionEnd); + context.selectionRange = editor.selectionMgr.setSelectionStartEnd(selectionStart, selectionEnd, undefined, true); currentFileDesc.newDiscussion = { selectionStart: selectionStart, selectionEnd: selectionEnd, @@ -311,14 +333,14 @@ define([ }; }).on('shown.bs.popover', '#wmd-input > .editor-margin', function(evt) { var context = currentContext; - movePopover(context.commentElt); var popoverElt = context.getPopoverElt(); - - // Scroll to the bottom of the discussion - popoverElt.querySelector('.popover-content').scrollTop = 9999999; - context.$authorInputElt = $(popoverElt.querySelector('.input-comment-author')).val(storage['author.name']); context.$contentInputElt = $(popoverElt.querySelector('.input-comment-content')); + movePopover(context.commentElt); + + // Scroll to the bottom of the discussion + popoverElt.querySelector('.scrollport').scrollTop = 9999999; + var $addButton = $(popoverElt.querySelector('.action-add-comment')); $().add(context.$contentInputElt).add(context.$authorInputElt).keydown(function(evt) { // Enter key @@ -377,12 +399,12 @@ define([ $removeButton.click(function() { $(popoverElt.querySelector('.new-comment-block')).addClass('hide'); $(popoverElt.querySelector('.remove-discussion-confirm')).removeClass('hide'); - popoverElt.querySelector('.popover-content').scrollTop = 9999999; + popoverElt.querySelector('.scrollport').scrollTop = 9999999; }); $removeCancelButton.click(function() { $(popoverElt.querySelector('.new-comment-block')).removeClass('hide'); $(popoverElt.querySelector('.remove-discussion-confirm')).addClass('hide'); - popoverElt.querySelector('.popover-content').scrollTop = 9999999; + popoverElt.querySelector('.scrollport').scrollTop = 9999999; context.$contentInputElt.focus(); }); $removeConfirmButton.click(function() { @@ -421,6 +443,7 @@ define([ return; } currentContext.$commentElt.removeClass('active'); + $newCommentElt.removeClass('hide'); // Save content and author for later previousContent = currentContext.$contentInputElt.val(); @@ -431,6 +454,29 @@ define([ currentContext = undefined; delete currentFileDesc.newDiscussion; }); + + var $newCommentElt = $(newCommentElt); + $openDiscussionElt = $('.button-open-discussion').click(function(evt) { + var $commentElt = $newCommentElt; + if(sortedCommentEltList.length) { + if(!currentContext) { + $commentElt = $(_.first(sortedCommentEltList)); + } + else { + var curentIndex = -1; + sortedCommentEltList.some(function(elt, index) { + if(elt === currentContext.commentElt) { + curentIndex = index; + return true; + } + }); + $commentElt = $(sortedCommentEltList[(curentIndex + 1) % sortedCommentEltList.length]); + } + } + $commentElt.click(); + evt.stopPropagation(); + }); + $openDiscussionIconElt = $openDiscussionElt.find('i'); }; return comments; diff --git a/public/res/html/bodyIndex.html b/public/res/html/bodyIndex.html index 763398f1..fab6db48 100644 --- a/public/res/html/bodyIndex.html +++ b/public/res/html/bodyIndex.html @@ -19,7 +19,9 @@