diff --git a/public/res/constants.js b/public/res/constants.js
index f4e816e4..a5922c62 100644
--- a/public/res/constants.js
+++ b/public/res/constants.js
@@ -1,7 +1,7 @@
define([], function() {
var constants = {};
constants.VERSION = "3.1.9";
-
+
constants.MAIN_URL = "https://stackedit.io/";
constants.GOOGLE_ANALYTICS_ACCOUNT_ID = "UA-39556145-1";
constants.GOOGLE_API_KEY = "AIzaSyAeCU8CGcSkn0z9js6iocHuPBX4f_mMWkw";
@@ -14,7 +14,7 @@ define([], function() {
constants.DEFAULT_FILE_TITLE = "Title";
constants.DEFAULT_FOLDER_NAME = "New folder";
constants.GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
- constants.EDITOR_DEFAULT_PADDING = 30;
+ constants.EDITOR_DEFAULT_PADDING = 35;
constants.CHECK_ONLINE_PERIOD = 120000;
constants.AJAX_TIMEOUT = 30000;
constants.ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
@@ -28,7 +28,7 @@ define([], function() {
constants.PICASA_PROXY_URL = "https://stackedit-picasa-proxy.herokuapp.com/";
constants.SSH_PROXY_URL = "https://stackedit-ssh-proxy.herokuapp.com/";
constants.HTMLTOPDF_URL = "https://stackedit-htmltopdf.herokuapp.com/";
-
+
// Site dependent
constants.BASE_URL = "http://localhost/";
constants.GOOGLE_CLIENT_ID = '241271498917-lev37kef013q85avc91am1gccg5g8lrb.apps.googleusercontent.com';
@@ -37,7 +37,7 @@ define([], function() {
constants.TUMBLR_PROXY_URL = "https://stackedit-tumblr-proxy-local.herokuapp.com/";
constants.WORDPRESS_CLIENT_ID = '23361';
constants.WORDPRESS_PROXY_URL = "https://stackedit-io-wordpress-proxy.herokuapp.com/";
-
+
if(location.hostname.indexOf("stackedit.io") === 0) {
constants.BASE_URL = constants.MAIN_URL;
constants.GOOGLE_CLIENT_ID = '241271498917-t4t7d07qis7oc0ahaskbif3ft6tk63cd.apps.googleusercontent.com';
@@ -61,7 +61,7 @@ define([], function() {
constants.GATEKEEPER_URL = "https://stackedit-gatekeeper-insomnia.herokuapp.com/";
constants.TUMBLR_PROXY_URL = "https://stackedit-tumblr-proxy-beta.herokuapp.com/";
}
-
+
constants.THEME_LIST = {
"default": "Default",
"gray": "Gray",
@@ -69,6 +69,6 @@ define([], function() {
"night": "Night",
"school": "School",
};
-
+
return constants;
});
diff --git a/public/res/core.js b/public/res/core.js
index 1d91a174..749b1e0c 100644
--- a/public/res/core.js
+++ b/public/res/core.js
@@ -397,7 +397,7 @@ define([
if(pagedownEditor !== undefined) {
// If the editor is already created
- $editorElt.val(initDocumentContent);
+ editor.contentElt.textContent = initDocumentContent;
pagedownEditor.undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
$editorElt.focus();
return;
@@ -455,7 +455,7 @@ define([
eventMgr.onPagedownConfigure(pagedownEditor);
pagedownEditor.hooks.chain("onPreviewRefresh", eventMgr.onAsyncPreview);
pagedownEditor.run();
- $editorElt.val(initDocumentContent);
+ editor.contentElt.textContent = initDocumentContent;
pagedownEditor.undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
$editorElt.focus();
@@ -799,7 +799,7 @@ define([
});
$(".action-import-docs-settings-confirm").click(function() {
storage.clear();
- var allowedKeys = /^file\.|^folder\.|^publish\.|^settings$|^sync\.|^google\.|^themeV3$|^version$/;
+ var allowedKeys = /^file\.|^folder\.|^publish\.|^settings$|^sync\.|^google\.|^author\.|^themeV3$|^version$/;
_.each(newstorage, function(value, key) {
if(allowedKeys.test(key)) {
storage[key] = value;
diff --git a/public/res/editor.js b/public/res/editor.js
index 3603c492..0524d9a0 100644
--- a/public/res/editor.js
+++ b/public/res/editor.js
@@ -61,23 +61,66 @@ define([
fileDesc = selectedFileDesc;
});
- function saveEditorState() {
- if(!inputElt.focused) {
- return;
+ function saveSelectionState() {
+ var selection = window.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;
+ }
+ }
+
+ element = container = container.parentNode;
+ } while (element && element != inputElt);
+ selectionStart = offset;
+ selectionEnd = offset + (range + '').length;
+ }
}
- selectionStart = inputElt.selectionStart;
- selectionEnd = inputElt.selectionEnd;
- scrollTop = inputElt.scrollTop;
if(fileChanged === false) {
fileDesc.editorStart = selectionStart;
fileDesc.editorEnd = selectionEnd;
- fileDesc.editorScrollTop = scrollTop;
}
}
var previousTextContent;
+ function getContentChange(textContent) {
+ // Find the first modified char
+ var startIndex = 0;
+ var startIndexMax = Math.min(previousTextContent.length, textContent.length);
+ while (startIndex < startIndexMax) {
+ if (previousTextContent.charCodeAt(startIndex) !== textContent.charCodeAt(startIndex)) {
+ break;
+ }
+ startIndex++;
+ }
+ // Find the last modified char
+ var endIndex = 1;
+ var endIndexMax = Math.min(previousTextContent.length - startIndex, textContent.length - startIndex);
+ while (endIndex <= endIndexMax) {
+ if (previousTextContent.charCodeAt(previousTextContent.length - endIndex) !== textContent.charCodeAt(textContent.length - endIndex)) {
+ break;
+ }
+ endIndex++;
+ }
+
+ var replacement = textContent.substring(startIndex, textContent.length - endIndex + 1);
+ endIndex = previousTextContent.length - endIndex + 1;
+ return {
+ startIndex: startIndex,
+ endIndex: endIndex,
+ replacement: replacement
+ };
+ }
+
function checkContentChange() {
- saveEditorState();
+ saveSelectionState();
var currentTextContent = inputElt.textContent;
if(fileChanged === false) {
if(currentTextContent == previousTextContent) {
@@ -86,6 +129,37 @@ define([
if(!/\n$/.test(currentTextContent)) {
currentTextContent += '\n';
}
+ var change = getContentChange(currentTextContent);
+ var endOffset = change.startIndex + change.replacement.length - change.endIndex;
+
+ // Move comments according to change
+ var updateDiscussionList = false;
+ _.each(fileDesc.discussionList, function(discussion) {
+ if(discussion.isRemoved === true) {
+ return;
+ }
+ // selectionEnd
+ if(discussion.selectionEnd >= change.endIndex) {
+ discussion.selectionEnd += endOffset;
+ updateDiscussionList = true;
+ }
+ else if(discussion.selectionEnd > change.startIndex) {
+ discussion.selectionEnd = change.startIndex;
+ updateDiscussionList = true;
+ }
+ // selectionStart
+ if(discussion.selectionStart >= change.endIndex) {
+ discussion.selectionStart += endOffset;
+ updateDiscussionList = true;
+ }
+ else if(discussion.selectionStart > change.startIndex) {
+ discussion.selectionStart = change.startIndex;
+ updateDiscussionList = true;
+ }
+ });
+ if(updateDiscussionList === true) {
+ fileDesc.discussionList = fileDesc.discussionList; // Write discussionList in localStorage
+ }
fileDesc.content = currentTextContent;
eventMgr.onContentChanged(fileDesc, currentTextContent);
}
@@ -199,7 +273,7 @@ define([
var cursorY = 0;
var isBackwardSelection = false;
function updateCursorCoordinates() {
- saveEditorState();
+ saveSelectionState();
$inputElt.toggleClass('has-selection', selectionStart !== selectionEnd);
var element;
@@ -234,24 +308,25 @@ define([
eventMgr.onCursorCoordinates(coordinates.x, coordinates.y);
}
- function adjustCursorPosition() {
- inputElt && setTimeout(function() {
- updateCursorCoordinates();
+ var adjustCursorPosition = _.debounce(function() {
+ if(inputElt === undefined) {
+ return;
+ }
+ updateCursorCoordinates();
- var adjust = inputElt.offsetHeight / 2;
- if(adjust > 130) {
- adjust = 130;
- }
- var cursorMinY = inputElt.scrollTop + adjust;
- var cursorMaxY = inputElt.scrollTop + inputElt.offsetHeight - adjust;
- if(cursorY < cursorMinY) {
- inputElt.scrollTop += cursorY - cursorMinY;
- }
- else if(cursorY > cursorMaxY) {
- inputElt.scrollTop += cursorY - cursorMaxY;
- }
- }, 0);
- }
+ var adjust = inputElt.offsetHeight / 2;
+ if(adjust > 130) {
+ adjust = 130;
+ }
+ var cursorMinY = inputElt.scrollTop + adjust;
+ var cursorMaxY = inputElt.scrollTop + inputElt.offsetHeight - adjust;
+ if(cursorY < cursorMinY) {
+ inputElt.scrollTop += cursorY - cursorMinY;
+ }
+ else if(cursorY > cursorMaxY) {
+ inputElt.scrollTop += cursorY - cursorMaxY;
+ }
+ }, 0);
eventMgr.addListener('onLayoutResize', adjustCursorPosition);
editor.init = function(elt1, elt2) {
@@ -279,7 +354,12 @@ define([
characterData: true
});
- $(inputElt).scroll(saveEditorState);
+ $(inputElt).scroll(function() {
+ scrollTop = inputElt.scrollTop;
+ if(fileChanged === false) {
+ fileDesc.editorScrollTop = scrollTop;
+ }
+ });
$(previewElt).scroll(function() {
if(fileChanged === false) {
fileDesc.previewScrollTop = previewElt.scrollTop;
@@ -303,64 +383,16 @@ define([
return this.textContent;
},
set: function (value) {
- var currentValue = this.textContent;
-
- // Find the first modified char
- var startIndex = 0;
- var startIndexMax = Math.min(currentValue.length, value.length);
- while (startIndex < startIndexMax) {
- if (currentValue.charCodeAt(startIndex) !== value.charCodeAt(startIndex)) {
- break;
- }
- startIndex++;
- }
- // Find the last modified char
- var endIndex = 1;
- var endIndexMax = Math.min(currentValue.length - startIndex, value.length - startIndex);
- while (endIndex <= endIndexMax) {
- if (currentValue.charCodeAt(currentValue.length - endIndex) !== value.charCodeAt(value.length - endIndex)) {
- break;
- }
- endIndex++;
- }
-
- var replacementText = value.substring(startIndex, value.length - endIndex + 1);
- endIndex = currentValue.length - endIndex + 1;
-
- var range = inputElt.createRange(startIndex, endIndex);
+ var contentChange = getContentChange(value);
+ var range = inputElt.createRange(contentChange.startIndex, contentChange.endIndex);
range.deleteContents();
- range.insertNode(document.createTextNode(replacementText));
+ range.insertNode(document.createTextNode(contentChange.replacement));
}
});
Object.defineProperty(inputElt, 'selectionStart', {
get: function () {
- var selection = window.getSelection();
-
- if (selection.rangeCount) {
- var range = selection.getRangeAt(0),
- element = range.startContainer,
- container = element,
- offset = range.startOffset;
-
- if (!(this.compareDocumentPosition(element) & 0x10)) {
- return selectionStart;
- }
-
- do {
- while (element = element.previousSibling) {
- if (element.textContent) {
- offset += element.textContent.length;
- }
- }
-
- element = container = container.parentNode;
- } while (element && element != this);
-
- return offset;
- } else {
- return selectionStart;
- }
+ return selectionStart;
},
set: function (value) {
inputElt.setSelectionStartEnd(value, selectionEnd);
@@ -372,13 +404,7 @@ define([
Object.defineProperty(inputElt, 'selectionEnd', {
get: function () {
- var selection = window.getSelection();
-
- if (selection.rangeCount) {
- return this.selectionStart + (selection.getRangeAt(0) + '').length;
- } else {
- return selectionEnd;
- }
+ return selectionEnd;
},
set: function (value) {
inputElt.setSelectionStartEnd(selectionStart, value);
@@ -388,34 +414,24 @@ define([
configurable: true
});
- inputElt.setSelectionStartEnd = function (ss, se) {
- selectionStart = ss;
- selectionEnd = se;
- var range = inputElt.createRange(ss, se);
-
+ inputElt.setSelectionStartEnd = function (start, end) {
+ selectionStart = start;
+ selectionEnd = end;
+ var range = inputElt.createRange(start, end);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
};
- inputElt.getSelectionStartEnd = function () {
- return {
- selectionStart: selectionStart,
- selectionEnd: selectionEnd
- };
- };
-
- inputElt.createRange = function(ss, se) {
-
- var range = document.createRange(),
- offset = _.isObject(ss) ? ss : findOffset(ss);
+ inputElt.createRange = function(start, end) {
+ var range = document.createRange();
+ var offset = _.isObject(start) ? start : findOffset(start);
range.setStart(offset.element, offset.offset);
- if (se && se != ss) {
- offset = _.isObject(se) ? se : findOffset(se);
+ if (end && end != start) {
+ offset = _.isObject(end) ? end : findOffset(end);
}
-
range.setEnd(offset.element, offset.offset);
return range;
};
@@ -435,7 +451,7 @@ define([
) {
return;
}
- saveEditorState();
+ saveSelectionState();
adjustCursorPosition();
var cmdOrCtrl = evt.metaKey || evt.ctrlKey;
diff --git a/public/res/eventMgr.js b/public/res/eventMgr.js
index 245e30eb..c7c80d7c 100644
--- a/public/res/eventMgr.js
+++ b/public/res/eventMgr.js
@@ -210,6 +210,11 @@ define([
addEventHook("onSectionsCreated");
addEventHook("onCursorCoordinates");
+ // Operations on comments
+ addEventHook("onDiscussionCreated");
+ addEventHook("onDiscussionRemoved");
+ addEventHook("onCommentAdded");
+
// Refresh twitter buttons
addEventHook("onTweet");
diff --git a/public/res/extensions/comments.js b/public/res/extensions/comments.js
index be1ce56e..08d7e070 100644
--- a/public/res/extensions/comments.js
+++ b/public/res/extensions/comments.js
@@ -2,18 +2,31 @@ define([
"jquery",
"underscore",
"utils",
+ "storage",
"crel",
"rangy",
"classes/Extension",
"text!html/commentsPopoverContent.html",
"bootstrap"
-], function($, _, utils, crel, rangy, Extension, commentsPopoverContentHTML) {
+], function($, _, utils, storage, crel, rangy, Extension, commentsPopoverContentHTML) {
var comments = new Extension("comments", 'Comments');
+ var commentTmpl = [
+ '
',
+ ].join('');
+
+ var eventMgr;
+ comments.onEventMgrCreated = function(eventMgrParam) {
+ eventMgr = eventMgrParam;
+ };
+
var offsetMap = {};
function setCommentEltCoordinates(commentElt, y) {
- var lineIndex = Math.round(y/10);
+ var lineIndex = Math.round(y / 10);
var top = (y - 8) + 'px';
var right = ((offsetMap[lineIndex] || 0) * 25 + 10) + 'px';
commentElt.style.top = top;
@@ -33,32 +46,28 @@ define([
setCommentEltCoordinates(newCommentElt, cursorY);
};
- var fileDesc;
- comments.onFileSelected = function(selectedFileDesc) {
- fileDesc = selectedFileDesc;
- refreshDiscussions();
- };
-
- var openedPopover;
- comments.onLayoutResize = function() {
- openedPopover && openedPopover.popover('toggle').popover('destroy');
- refreshDiscussions();
- };
-
var refreshId;
+ var currentFileDesc;
function refreshDiscussions() {
+ if(currentFileDesc === undefined) {
+ return;
+ }
+ var author = storage['author.name'];
clearTimeout(refreshId);
commentEltList.forEach(function(commentElt) {
marginElt.removeChild(commentElt);
});
commentEltList = [];
offsetMap = {};
- var discussionList = _.map(fileDesc.discussionList, _.identity);
+ var discussionList = _.map(currentFileDesc.discussionList, _.identity);
function refreshOne() {
- if(discussionList.length === 0) {
- return;
- }
- var discussion = discussionList.pop();
+ var discussion;
+ do {
+ if(discussionList.length === 0) {
+ return;
+ }
+ discussion = discussionList.pop();
+ } while(discussion.isRemoved);
var commentElt = crel('a', {
class: 'icon-comment'
});
@@ -69,63 +78,115 @@ define([
marginElt.appendChild(commentElt);
commentEltList.push(commentElt);
- // Replace newCommentElt
+ // Move newCommentElt
setCommentEltCoordinates(newCommentElt, cursorY);
- refreshId = setTimeout(refreshOne, 0);
+ // Apply class later for fade effect
+ commentElt.offsetWidth; // Refresh
+ var isReplied = _.last(discussion.commentList).author != author;
+ commentElt.className += isReplied ? ' replied' : ' added';
+ refreshId = setTimeout(refreshOne, 50);
}
refreshId = setTimeout(refreshOne, 50);
}
+ var debouncedRefreshDiscussions = _.debounce(refreshDiscussions, 2000);
+
+ comments.onFileOpen = function(fileDesc) {
+ currentFileDesc = fileDesc;
+ refreshDiscussions();
+ };
+
+ comments.onContentChanged = function(fileDesc, content) {
+ currentFileDesc === fileDesc && debouncedRefreshDiscussions();
+ };
+
+ var currentContext;
+ function closeCurrentPopover() {
+ currentContext && currentContext.$commentElt.popover('toggle').popover('destroy');
+ }
+ comments.onLayoutResize = function() {
+ closeCurrentPopover();
+ refreshDiscussions();
+ };
+
+ comments.onDiscussionCreated = function() {
+ refreshDiscussions();
+ };
+
+ comments.onDiscussionRemoved = function() {
+ refreshDiscussions();
+ };
+
+ comments.onCommentAdded = function() {
+ refreshDiscussions();
+ };
+
+ function getDiscussionComments() {
+ return currentContext.discussion.commentList.map(function(comment) {
+ return _.template(commentTmpl, {
+ author: comment.author || 'Anonymous',
+ content: comment.content
+ });
+ }).join('');
+ }
comments.onReady = function() {
var cssApplier = rangy.createCssClassApplier("comment-highlight", {
normalize: false
});
- var selectionRange;
- var rangyRange;
- var currentDiscussion;
+ var previousContent = '';
inputElt = document.getElementById('wmd-input');
marginElt = document.querySelector('#wmd-input > .editor-margin');
marginElt.appendChild(newCommentElt);
$(document.body).append(crel('div', {
class: 'comments-popover'
- })).popover({
+ })).on('click', function(evt) {
+ // Close on click outside the popover
+ if(currentContext && currentContext.$commentElt[0] !== evt.target) {
+ closeCurrentPopover();
+ }
+ }).popover({
placement: 'auto top',
container: '.comments-popover',
html: true,
title: function() {
- if(!currentDiscussion) {
- return '...';
+ if(!currentContext) {
+ return true;
}
- var titleLength = currentDiscussion.selectionEnd - currentDiscussion.selectionStart;
- var title = inputElt.textContent.substr(currentDiscussion.selectionStart, titleLength > 20 ? 20 : titleLength);
+ var titleLength = currentContext.discussion.selectionEnd - currentContext.discussion.selectionStart;
+ var title = inputElt.textContent.substr(currentContext.discussion.selectionStart, titleLength > 20 ? 20 : titleLength);
if(titleLength > 20) {
title += '...';
}
- return title.replace(/&/g, '&').replace(/' + title;
},
content: function() {
var content = _.template(commentsPopoverContentHTML, {
+ commentList: getDiscussionComments()
});
return content;
},
selector: '#wmd-input > .editor-margin > .icon-comment'
}).on('show.bs.popover', '#wmd-input > .editor-margin', function(evt) {
- $(evt.target).addClass('active');
+ closeCurrentPopover();
+ var context = {
+ $commentElt: $(evt.target).addClass('active')
+ };
+ currentContext = context;
inputElt.scrollTop += parseInt(evt.target.style.top) - inputElt.scrollTop - inputElt.offsetHeight * 3 / 4;
// If it's an existing discussion
if(evt.target.discussion) {
- currentDiscussion = evt.target.discussion;
- selectionRange = inputElt.createRange(currentDiscussion.selectionStart, currentDiscussion.selectionEnd);
+ context.discussion = evt.target.discussion;
+ context.selectionRange = inputElt.createRange(context.discussion.selectionStart, context.discussion.selectionEnd);
return;
}
// Get selected text
- var inputSelection = inputElt.getSelectionStartEnd();
- var selectionStart = inputSelection.selectionStart;
- var selectionEnd = inputSelection.selectionEnd;
+ var selectionStart = inputElt.selectionStart;
+ var selectionEnd = inputElt.selectionEnd;
if(selectionStart === selectionEnd) {
var after = inputElt.textContent.substring(selectionStart);
var match = /\S+/.exec(after);
@@ -139,26 +200,30 @@ define([
selectionEnd += match.index + match[0].length;
}
}
- selectionRange = inputElt.createRange(selectionStart, selectionEnd);
- currentDiscussion = {
+ context.selectionRange = inputElt.createRange(selectionStart, selectionEnd);
+ context.discussion = {
selectionStart: selectionStart,
selectionEnd: selectionEnd,
commentList: []
};
}).on('shown.bs.popover', '#wmd-input > .editor-margin', function(evt) {
-
// Move the popover in the margin
- var popoverElt = document.querySelector('.comments-popover .popover:last-child');
- var left = -10;
- if(popoverElt.offsetWidth < marginElt.offsetWidth) {
- left = marginElt.offsetWidth - popoverElt.offsetWidth - 10;
+ var context = currentContext;
+ context.popoverElt = document.querySelector('.comments-popover .popover:last-child');
+ var left = -5;
+ if(context.popoverElt.offsetWidth < marginElt.offsetWidth - 5) {
+ left = marginElt.offsetWidth - 10 - context.popoverElt.offsetWidth;
}
- popoverElt.style.left = left + 'px';
- popoverElt.querySelector('.arrow').style.left = (marginElt.offsetWidth - parseInt(evt.target.style.right) - evt.target.offsetWidth / 2 - left) + 'px';
+ context.popoverElt.style.left = left + 'px';
+ context.popoverElt.querySelector('.arrow').style.left = (marginElt.offsetWidth - parseInt(evt.target.style.right) - evt.target.offsetWidth / 2 - left) + 'px';
- var $textarea = $(popoverElt.querySelector('.input-comment-content'));
- var $addButton = $(popoverElt.querySelector('.action-add-comment'));
- $textarea.keydown(function(evt) {
+ // Scroll to the bottom of the discussion
+ context.popoverElt.querySelector('.popover-content').scrollTop = 9999999;
+
+ context.$authorInputElt = $(context.popoverElt.querySelector('.input-comment-author')).val(storage['author.name']);
+ context.$contentInputElt = $(context.popoverElt.querySelector('.input-comment-content'));
+ var $addButton = $(context.popoverElt.querySelector('.action-add-comment'));
+ context.$contentInputElt.keydown(function(evt) {
// Enter key
switch(evt.which) {
case 13:
@@ -167,65 +232,106 @@ define([
return;
case 27:
evt.preventDefault();
- openedPopover && openedPopover.popover('toggle').popover('destroy');
+ closeCurrentPopover();
inputElt.focus();
return;
}
});
$addButton.click(function(evt) {
- var author = utils.getInputTextValue(popoverElt.querySelector('.input-comment-author'));
- var content = utils.getInputTextValue($textarea, evt);
+ var author = utils.getInputTextValue(context.$authorInputElt);
+ var content = utils.getInputTextValue(context.$contentInputElt, evt);
if(evt.isPropagationStopped()) {
return;
}
- var discussionList = fileDesc.discussionList || {};
- if(!currentDiscussion.discussionIndex) {
+ context.$contentInputElt.val('');
+ closeCurrentPopover();
+
+ var discussionList = currentFileDesc.discussionList || {};
+ var isNew = false;
+ if(!context.discussion.discussionIndex) {
+ isNew = true;
// Create discussion index
var discussionIndex;
do {
discussionIndex = utils.randomString();
} while(_.has(discussionList, discussionIndex));
- currentDiscussion.discussionIndex = discussionIndex;
- discussionList[discussionIndex] = currentDiscussion;
+ context.discussion.discussionIndex = discussionIndex;
+ discussionList[discussionIndex] = context.discussion;
}
- currentDiscussion.commentList.push({
+ context.discussion.commentList.push({
author: author,
content: content
});
- fileDesc.discussionList = discussionList;
- openedPopover.popover('toggle').popover('destroy');
- refreshDiscussions();
+ currentFileDesc.discussionList = discussionList; // Write discussionList in localStorage
+ isNew ?
+ eventMgr.onDiscussionCreated(currentFileDesc, context.discussion) :
+ eventMgr.onCommentAdded(currentFileDesc, context.discussion);
inputElt.focus();
});
+ var $removeButton = $(context.popoverElt.querySelector('.action-remove-discussion'));
+ if(evt.target.discussion) {
+ // If it's an existing discussion
+ var $removeCancelButton = $(context.popoverElt.querySelector('.action-remove-discussion-cancel'));
+ var $removeConfirmButton = $(context.popoverElt.querySelector('.action-remove-discussion-confirm'));
+ $removeButton.click(function() {
+ $(context.popoverElt.querySelector('.new-comment-block')).addClass('hide');
+ $(context.popoverElt.querySelector('.remove-discussion-confirm')).removeClass('hide');
+ context.popoverElt.querySelector('.popover-content').scrollTop = 9999999;
+ });
+ $removeCancelButton.click(function() {
+ $(context.popoverElt.querySelector('.new-comment-block')).removeClass('hide');
+ $(context.popoverElt.querySelector('.remove-discussion-confirm')).addClass('hide');
+ context.popoverElt.querySelector('.popover-content').scrollTop = 9999999;
+ context.$contentInputElt.focus();
+ });
+ $removeConfirmButton.click(function() {
+ closeCurrentPopover();
+ context.discussion.isRemoved = true;
+ delete context.discussion.selectionStart;
+ delete context.discussion.selectionEnd;
+ delete context.discussion.commentList;
+ currentFileDesc.discussionList = currentFileDesc.discussionList; // Write discussionList in localStorage
+ eventMgr.onDiscussionRemoved(currentFileDesc, context.discussion);
+ inputElt.focus();
+ });
+ }
+ else {
+ // Otherwise hide the remove button
+ $removeButton.hide();
+ }
+
// Prevent from closing on click inside the popover
- $(popoverElt).on('click', function(evt) {
+ $(context.popoverElt).on('click', function(evt) {
evt.stopPropagation();
});
- setTimeout(function() {
- openedPopover = $(evt.target);
- // Highlight selected text
- rangyRange = rangy.createRange();
- rangyRange.setStart(selectionRange.startContainer, selectionRange.startOffset);
- rangyRange.setEnd(selectionRange.endContainer, selectionRange.endOffset);
- cssApplier.applyToRange(rangyRange);
+ // Highlight selected text
+ context.rangyRange = rangy.createRange();
+ context.rangyRange.setStart(context.selectionRange.startContainer, context.selectionRange.startOffset);
+ context.rangyRange.setEnd(context.selectionRange.endContainer, context.selectionRange.endOffset);
+ setTimeout(function() { // Need to delay this because it's not refreshed properly
+ if(currentContext === context) {
+ cssApplier.applyToRange(context.rangyRange);
+ }
+ }, 50);
- // Focus on textarea
- $textarea.focus();
- }, 10);
+ // Focus on textarea
+ context.$contentInputElt.focus().val(previousContent);
}).on('hide.bs.popover', '#wmd-input > .editor-margin', function(evt) {
- $(evt.target).removeClass('active');
+ if(!currentContext) {
+ return;
+ }
+ currentContext.$commentElt.removeClass('active');
+
+ // Save content and author for later
+ previousContent = currentContext.$contentInputElt.val();
+ storage['author.name'] = currentContext.$authorInputElt.val();
// Remove highlight
- rangyRange && cssApplier.undoToRange(rangyRange);
- openedPopover = undefined;
- rangyRange = undefined;
- currentDiscussion = undefined;
- }).on('click', function() {
- // Close on click outside the popover
- openedPopover && openedPopover.popover('toggle').popover('destroy');
+ cssApplier.undoToRange(currentContext.rangyRange);
+ currentContext = undefined;
});
};
diff --git a/public/res/html/commentsPopoverContent.html b/public/res/html/commentsPopoverContent.html
index 1e56b649..bec86cd9 100644
--- a/public/res/html/commentsPopoverContent.html
+++ b/public/res/html/commentsPopoverContent.html
@@ -1,5 +1,5 @@
-
-
+
+
+
+
+
Remove this discussion, really?
+
+
+
+
+
diff --git a/public/res/providers/gdriveProviderBuilder.js b/public/res/providers/gdriveProviderBuilder.js
index 7388c3b0..d40537c7 100644
--- a/public/res/providers/gdriveProviderBuilder.js
+++ b/public/res/providers/gdriveProviderBuilder.js
@@ -451,6 +451,18 @@ define([
};
function mergeDiscussion(localDiscussion, realtimeDiscussion, takeServer) {
+ if(localDiscussion.isRemoved === true) {
+ realtimeDiscussion.set('isRemoved');
+ realtimeDiscussion.delete('selectionStart');
+ realtimeDiscussion.delete('selectionEnd');
+ return realtimeDiscussion.delete('commentList');
+ }
+ if(realtimeDiscussion.get('isRemoved') === true) {
+ localDiscussion.isRemoved = true;
+ delete localDiscussion.selectionStart;
+ delete localDiscussion.selectionEnd;
+ return delete localDiscussion.commentList;
+ }
if(takeServer) {
localDiscussion.selectionStart = realtimeDiscussion.get('selectionStart');
localDiscussion.selectionEnd = realtimeDiscussion.get('selectionEnd');
diff --git a/public/res/styles/main.less b/public/res/styles/main.less
index 72cce427..b1d1bf6e 100644
--- a/public/res/styles/main.less
+++ b/public/res/styles/main.less
@@ -127,7 +127,7 @@
@popover-arrow-outer-color: @secondary-border-color;
@popover-title-bg: @transparent;
@alert-border-radius: 0;
-@label-warning-bg: #d90;
+@label-warning-bg: #da0;
@label-danger-bg: #d00;
@@ -140,7 +140,7 @@ body {
}
#preview-contents {
- padding: 30px;
+ padding: 35px;
margin: 0 auto 200px;
text-align: justify;
}
@@ -362,7 +362,7 @@ a {
.form-control.error {
border-color: @error-border;
- .box-shadow(~"@{form-control-inset-shadow}, 0 0 8px rgba(255, 134, 97, 0.6)");
+ .box-shadow(~"@{form-control-inset-shadow}, 0 0 8px rgba(255, 0, 0, 0.6)");
}
.help-block {
@@ -773,7 +773,7 @@ a {
position: absolute;
z-index: 1;
margin-top: 6px;
- right: 30px;
+ right: 35px;
.ui-layout-resizer-south-closed & {
display: none !important;
}
@@ -1060,22 +1060,28 @@ a {
top: 0;
.icon-comment {
&.new {
- color: fade(@tertiary-color, 12%);
- &:hover {
+ color: fade(@tertiary-color, 10%);
+ &:hover, &.active, &.active:hover {
color: fade(@tertiary-color, 35%) !important;
}
}
&.replied {
- color: fade(@label-danger-bg, 30%);
+ color: fade(@label-danger-bg, 35%);
&:hover, &.active, &.active:hover {
color: fade(@label-danger-bg, 45%) !important;
}
}
+ &.added {
+ color: fade(@label-warning-bg, 40%);
+ &:hover, &.active, &.active:hover {
+ color: fade(@label-warning-bg, 60%) !important;
+ }
+ }
+ .transition(~"color ease-in-out .25s");
position: absolute;
- color: fade(@label-warning-bg, 40%);
+ color: fade(#fff, 0%);
cursor: pointer;
- &:hover, &.active, &.active:hover {
- color: fade(@label-warning-bg, 60%) !important;
+ &:hover, &.active {
text-decoration: none;
}
}
@@ -1399,7 +1405,7 @@ input[type="file"] {
*********************/
.popover {
- max-width: 240px;
+ max-width: 230px;
padding: 10px 20px 0;
//.box-shadow(0 5px 30px rgba(0,0,0,.175));
.popover-title {
@@ -1408,6 +1414,10 @@ input[type="file"] {
padding: 5px 0 15px;
border-bottom: 1px solid @hr-border;
line-height: @headings-line-height;
+ .action-remove-discussion {
+ font-size: 16px;
+ line-height: 22px;
+ }
}
.icon-lock {
font-size: 38px;
@@ -1417,10 +1427,20 @@ input[type="file"] {
display: none;
}
.popover-content {
- padding-bottom: 0;
+ padding: 10px 20px 0;
+ overflow: auto;
+ max-height: 230px;
+ margin: 0 -20px;
.btn {
padding: 6px 11px;
}
+ .comment-block {
+ margin-bottom: 5px;
+ }
+ .comment-author {
+ padding-left: 12px;
+ font-weight: bold;
+ }
.input-comment-author {
border: none;
background: none;