Selaa lähdekoodia

10.4.9 release

Gaudenz Alder 6 vuotta sitten
vanhempi
commit
02d4a74e82
75 muutettua tiedostoa jossa 2802 lisäystä ja 1004 poistoa
  1. 4 0
      ChangeLog
  2. 1 1
      VERSION
  3. 2 0
      etc/build/build.xml
  4. 1 1
      src/main/webapp/cache.manifest
  5. BIN
      src/main/webapp/images/check.png
  6. 762 733
      src/main/webapp/js/app.min.js
  7. 57 0
      src/main/webapp/js/diagramly/App.js
  8. 5 0
      src/main/webapp/js/diagramly/Devel.js
  9. 690 2
      src/main/webapp/js/diagramly/Dialogs.js
  10. 51 0
      src/main/webapp/js/diagramly/DrawioComment.js
  11. 33 1
      src/main/webapp/js/diagramly/DrawioFile.js
  12. 1 1
      src/main/webapp/js/diagramly/DriveClient.js
  13. 70 0
      src/main/webapp/js/diagramly/DriveComment.js
  14. 71 0
      src/main/webapp/js/diagramly/DriveFile.js
  15. 5 0
      src/main/webapp/js/diagramly/Editor.js
  16. 7 4
      src/main/webapp/js/diagramly/EditorUi.js
  17. 58 1
      src/main/webapp/js/diagramly/Menus.js
  18. 10 1
      src/main/webapp/js/diagramly/Minimal.js
  19. 260 259
      src/main/webapp/js/viewer.min.js
  20. 10 0
      src/main/webapp/resources/dia.txt
  21. 10 0
      src/main/webapp/resources/dia_am.txt
  22. 10 0
      src/main/webapp/resources/dia_ar.txt
  23. 10 0
      src/main/webapp/resources/dia_bg.txt
  24. 10 0
      src/main/webapp/resources/dia_bn.txt
  25. 10 0
      src/main/webapp/resources/dia_bs.txt
  26. 10 0
      src/main/webapp/resources/dia_ca.txt
  27. 10 0
      src/main/webapp/resources/dia_cs.txt
  28. 10 0
      src/main/webapp/resources/dia_da.txt
  29. 10 0
      src/main/webapp/resources/dia_de.txt
  30. 10 0
      src/main/webapp/resources/dia_el.txt
  31. 10 0
      src/main/webapp/resources/dia_eo.txt
  32. 10 0
      src/main/webapp/resources/dia_es.txt
  33. 10 0
      src/main/webapp/resources/dia_et.txt
  34. 10 0
      src/main/webapp/resources/dia_fa.txt
  35. 10 0
      src/main/webapp/resources/dia_fi.txt
  36. 10 0
      src/main/webapp/resources/dia_fil.txt
  37. 10 0
      src/main/webapp/resources/dia_fr.txt
  38. 10 0
      src/main/webapp/resources/dia_gu.txt
  39. 10 0
      src/main/webapp/resources/dia_he.txt
  40. 10 0
      src/main/webapp/resources/dia_hi.txt
  41. 10 0
      src/main/webapp/resources/dia_hr.txt
  42. 10 0
      src/main/webapp/resources/dia_hu.txt
  43. 10 0
      src/main/webapp/resources/dia_i18n.txt
  44. 10 0
      src/main/webapp/resources/dia_id.txt
  45. 10 0
      src/main/webapp/resources/dia_it.txt
  46. 10 0
      src/main/webapp/resources/dia_ja.txt
  47. 10 0
      src/main/webapp/resources/dia_kn.txt
  48. 10 0
      src/main/webapp/resources/dia_ko.txt
  49. 10 0
      src/main/webapp/resources/dia_lt.txt
  50. 10 0
      src/main/webapp/resources/dia_lv.txt
  51. 10 0
      src/main/webapp/resources/dia_ml.txt
  52. 10 0
      src/main/webapp/resources/dia_mr.txt
  53. 10 0
      src/main/webapp/resources/dia_ms.txt
  54. 10 0
      src/main/webapp/resources/dia_nl.txt
  55. 10 0
      src/main/webapp/resources/dia_no.txt
  56. 10 0
      src/main/webapp/resources/dia_pl.txt
  57. 10 0
      src/main/webapp/resources/dia_pt-br.txt
  58. 10 0
      src/main/webapp/resources/dia_pt.txt
  59. 10 0
      src/main/webapp/resources/dia_ro.txt
  60. 10 0
      src/main/webapp/resources/dia_ru.txt
  61. 10 0
      src/main/webapp/resources/dia_sk.txt
  62. 10 0
      src/main/webapp/resources/dia_sl.txt
  63. 10 0
      src/main/webapp/resources/dia_sr.txt
  64. 10 0
      src/main/webapp/resources/dia_sv.txt
  65. 10 0
      src/main/webapp/resources/dia_sw.txt
  66. 10 0
      src/main/webapp/resources/dia_ta.txt
  67. 10 0
      src/main/webapp/resources/dia_te.txt
  68. 10 0
      src/main/webapp/resources/dia_th.txt
  69. 10 0
      src/main/webapp/resources/dia_tr.txt
  70. 10 0
      src/main/webapp/resources/dia_uk.txt
  71. 10 0
      src/main/webapp/resources/dia_vi.txt
  72. 10 0
      src/main/webapp/resources/dia_zh-tw.txt
  73. 10 0
      src/main/webapp/resources/dia_zh.txt
  74. 6 0
      src/main/webapp/styles/dark.css
  75. 168 0
      src/main/webapp/styles/grapheditor.css

+ 4 - 0
ChangeLog

@@ -1,3 +1,7 @@
+17-MAR-2019: 10.4.9
+
+- Adds comments for Google Drive (beta)
+
 15-MAR-2019: 10.4.8
 
 - Adds autosaveDelay and defaultEdgeLength to drawio-config

+ 1 - 1
VERSION

@@ -1 +1 @@
-10.4.8
+10.4.9

+ 2 - 0
etc/build/build.xml

@@ -353,6 +353,8 @@
 				<file name="TrelloFile.js" />
 				<file name="TrelloLibrary.js" />
 				<file name="TrelloClient.js" />
+				<file name="DrawioComment.js" />
+				<file name="DriveComment.js" />
 			</sources>
 			
 			<sources dir="${war.dir}/js/diagramly">

+ 1 - 1
src/main/webapp/cache.manifest

@@ -1,7 +1,7 @@
 CACHE MANIFEST
 
 # THIS FILE WAS GENERATED. DO NOT MODIFY!
-# 03/15/2019 07:50 PM
+# 03/17/2019 11:30 AM
 
 app.html
 index.html?offline=1

BIN
src/main/webapp/images/check.png


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 762 - 733
src/main/webapp/js/app.min.js


+ 57 - 0
src/main/webapp/js/diagramly/App.js

@@ -4356,6 +4356,38 @@ App.prototype.updateButtonContainer = function()
 	{
 		var file = this.getCurrentFile();
 		
+		// Comments
+		if (file != null && file.constructor == DriveFile)
+		{
+			if (this.commentButton == null)
+			{
+				this.commentButton = document.createElement('a');
+				this.commentButton.setAttribute('title', mxResources.get('comments'));
+				this.commentButton.className = 'geToolbarButton';
+				this.commentButton.style.cssText = 'display:inline-block;position:relative;box-sizing:border-box;' +
+					'margin-right:4px;float:left;cursor:pointer;width:24px;height:24px;background-size:24px 24px;' +
+					'background-position:center center;background-repeat:no-repeat;background-image:' +
+					'url(' + Editor.commentImage + ');';
+				
+				mxEvent.addListener(this.commentButton, 'click', mxUtils.bind(this, function()
+				{
+					this.actions.get('comments').funct();
+				}));
+				
+				this.buttonContainer.appendChild(this.commentButton);
+				
+				if (uiTheme == 'dark')
+				{
+					this.commentButton.style.filter = 'invert(100%)';
+				}
+			}
+		}
+		else if (this.commentButton != null)
+		{
+			this.commentButton.parentNode.removeChild(this.commentButton);
+			this.commentButton = null;
+		}
+		
 		// Share
 		if (file != null && file.constructor == DriveFile)
 		{
@@ -5628,6 +5660,31 @@ App.prototype.updateUserElement = function()
 	}
 };
 
+//TODO Use this function to get the currently logged in user
+App.prototype.getCurrentUser = function()
+{
+	var user = null;
+	
+	if (this.drive != null && this.drive.getUser() != null)
+	{
+		user = this.drive.getUser();
+	}
+	else if (this.oneDrive != null && this.oneDrive.getUser() != null)
+	{
+		user = this.oneDrive.getUser();
+	}
+	else if (this.dropbox != null && this.dropbox.getUser() != null)
+	{
+		user = this.dropbox.getUser();
+	}
+	else if (this.gitHub != null && this.gitHub.getUser() != null)
+	{
+		user = this.gitHub.getUser();
+	}
+	//TODO Trello no user issue
+	
+	return user;
+}
 /**
  * Override depends on mxSettings which is not defined in the minified viewer.
  */

+ 5 - 0
src/main/webapp/js/diagramly/Devel.js

@@ -89,6 +89,11 @@ mxscript(drawDevUrl + 'js/diagramly/DiffSync.js');
 mxscript(drawDevUrl + 'js/diagramly/Settings.js');
 mxscript(drawDevUrl + 'js/diagramly/DrawioFileSync.js');
 
+
+//Comments
+mxscript(drawDevUrl + 'js/diagramly/DrawioComment.js');
+mxscript(drawDevUrl + 'js/diagramly/DriveComment.js');
+
 // Excluded in base.min.js
 mxscript(drawDevUrl + 'js/diagramly/DrawioClient.js');
 mxscript(drawDevUrl + 'js/diagramly/DrawioUser.js');

+ 690 - 2
src/main/webapp/js/diagramly/Dialogs.js

@@ -5050,7 +5050,7 @@ var FeedbackDialog = function(editorUi)
 	textarea.style.height = '140px';
 	textarea.style.marginTop = '6px';
 	
-	textarea.setAttribute('placeholder', mxResources.get('commentsNotes'));
+	textarea.setAttribute('placeholder', mxResources.get('comments'));
 	
 	div.appendChild(textarea);
 
@@ -9559,4 +9559,692 @@ var BtnDialog = function(editorUi, peer, btnLbl, fn)
 	div.appendChild(button);
 	
 	this.container = div;
-};
+};
+
+/**
+ * 
+ */
+var CommentsWindow = function(editorUi, x, y, w, h)
+{
+	var file = editorUi.getCurrentFile();
+	var readOnly = !file.desc.canComment;
+	var canReplyToReplies = !readOnly;
+	var file = editorUi.getCurrentFile();
+	
+	var div = document.createElement('div');
+	div.className = 'geCommentsWin';
+	div.style.background = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor;
+
+	var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px';
+	
+	var listDiv = document.createElement('div');
+	listDiv.className = 'geCommentsList';
+	listDiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor;
+	listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px';
+	div.appendChild(listDiv);
+	
+	var noComments = document.createElement('span');
+	noComments.style.cssText = 'display:none;padding-top:10px;text-align:center;';
+	mxUtils.write(noComments, mxResources.get('noCommentsFound'));
+	
+	var selectionComment = null;
+	
+	var ldiv = document.createElement('div');
+	
+	ldiv.className = 'geToolbarContainer geCommentsToolbar';
+	ldiv.style.height = tbarHeight;
+	ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px';
+	ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor;
+	
+	if (mxClient.IS_QUIRKS)
+	{
+		ldiv.style.filter = 'none';
+	}
+	
+	var link = document.createElement('a');
+	link.className = 'geButton';
+	
+	if (mxClient.IS_QUIRKS)
+	{
+		link.style.filter = 'none';
+	}
+	
+	function editComment(comment, cdiv, saveCallback, deleteOnCancel)
+	{
+		var commentTxt = cdiv.querySelector('.geCommentTxt');
+		var actionsDiv = cdiv.querySelector('.geCommentActionsList');
+		
+		var textArea = document.createElement('textarea');
+		textArea.className = 'geCommentEditTxtArea';
+		textArea.style.minHeight = commentTxt.offsetHeight + 'px';
+		textArea.value = commentTxt.innerHTML;
+		cdiv.insertBefore(textArea, commentTxt);
+		
+		var btnDiv = document.createElement('div');
+		btnDiv.className = 'geCommentEditBtns';
+		
+		function reset()
+		{
+			cdiv.removeChild(textArea);
+			cdiv.removeChild(btnDiv);
+			actionsDiv.style.display = 'block';
+			commentTxt.style.display = 'block';	
+		};
+		
+		var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
+		{
+			if (deleteOnCancel)
+			{
+				cdiv.parentNode.removeChild(cdiv);
+			}
+			else
+			{
+				reset();
+			}
+		});
+		
+		cancelBtn.className = 'geCommentEditBtn';
+		btnDiv.appendChild(cancelBtn);
+		
+		var saveBtn = mxUtils.button(mxResources.get('save'), function()
+		{
+			commentTxt.innerHTML = '';
+			comment.content = textArea.value;
+			mxUtils.write(commentTxt, comment.content);
+			reset();
+			saveCallback(comment);
+		});
+		
+		// Updates modified state and handles placeholder text
+		mxEvent.addListener(textArea, 'keydown', mxUtils.bind(this, function(evt)
+		{
+			if (!mxEvent.isConsumed(evt))
+			{
+				if ((mxEvent.isControlDown(evt) || (mxClient.IS_MAC &&
+					mxEvent.isMetaDown(evt))) && evt.keyCode == 13 /* Ctrl+Enter */)
+				{
+					saveBtn.click();
+					mxEvent.consume(evt);
+				}
+				else if (evt.keyCode == 27 /* Escape */)
+				{
+					cancelBtn.click();
+					mxEvent.consume(evt);
+				}
+			}
+		}));
+		
+		// Focused to include in viewport before focusin textbox
+		saveBtn.focus();
+		saveBtn.className = 'geCommentEditBtn gePrimaryBtn';
+		btnDiv.appendChild(saveBtn);
+
+		cdiv.insertBefore(btnDiv, commentTxt);
+		actionsDiv.style.display = 'none';
+		commentTxt.style.display = 'none';
+		textArea.focus();
+	};
+	
+	function writeCommentDate(comment, dateDiv)
+	{
+		dateDiv.innerHTML = '';
+		var str = editorUi.timeSince(new Date(comment.modifiedDate));
+		
+		if (str == null)
+		{
+			str = mxResources.get('lessThanAMinute');
+		}
+		
+		mxUtils.write(dateDiv, mxResources.get('timeAgo', [str], '{1} ago'));	
+	};
+	
+	function showBusy(commentDiv)
+	{
+		var busyImg = document.createElement('img');
+		busyImg.className = 'geCommentBusyImg';
+		busyImg.src= '/images/spin.gif';
+		commentDiv.appendChild(busyImg);
+		commentDiv.busyImg = busyImg;
+	};
+	
+	function showError(commentDiv)
+	{
+		commentDiv.style.border = '1px solid red';
+		commentDiv.removeChild(commentDiv.busyImg);
+	};
+	
+	function showDone(commentDiv)
+	{
+		commentDiv.style.border = '';
+		commentDiv.removeChild(commentDiv.busyImg);
+	};
+
+	function addComment(comment, parentArr, parent, level, showResolved)
+	{
+		//Skip resolved comments if showResolved is not set
+		if (!showResolved && comment.isResolved)
+		{
+			return;
+		}
+
+		noComments.style.display = 'none';
+		
+		var cdiv = document.createElement('div');
+		cdiv.className = 'geCommentContainer';
+		cdiv.setAttribute('data-commentId', comment.id);
+		cdiv.style.marginLeft = (level * 20 + 5) + 'px';
+
+		if (comment.isResolved && uiTheme != 'dark')
+		{
+			cdiv.style.backgroundColor = 'ghostWhite';
+		}
+		
+		var headerDiv = document.createElement('div');
+		headerDiv.className = 'geCommentHeader';
+		
+		var userImg = document.createElement('img');
+		userImg.className = 'geCommentUserImg';
+		userImg.src = comment.user.pictureUrl || Editor.userImage;
+		headerDiv.appendChild(userImg);
+		
+		var headerTxt = document.createElement('div');
+		headerTxt.className = 'geCommentHeaderTxt';
+		headerDiv.appendChild(headerTxt);
+		
+		var usernameDiv = document.createElement('div');
+		usernameDiv.className = 'geCommentUsername';
+		mxUtils.write(usernameDiv, comment.user.displayName || '');
+		headerTxt.appendChild(usernameDiv);
+		
+		var dateDiv = document.createElement('div');
+		dateDiv.className = 'geCommentDate';
+		dateDiv.setAttribute('data-commentId', comment.id);
+		writeCommentDate(comment, dateDiv);
+		headerTxt.appendChild(dateDiv);
+		cdiv.appendChild(headerDiv);
+		
+		var commentTxtDiv = document.createElement('div');
+		commentTxtDiv.className = 'geCommentTxt';
+		mxUtils.write(commentTxtDiv, comment.content || '');
+		cdiv.appendChild(commentTxtDiv);
+		
+		var actionsDiv = document.createElement('div');
+		actionsDiv.className = 'geCommentActions';
+		var actionsList = document.createElement('ul');
+		actionsList.className = 'geCommentActionsList';
+		actionsDiv.appendChild(actionsList);
+		
+		function addAction(name, evtHandler, hide)
+		{
+			var action = document.createElement('li');
+			action.className = 'geCommentAction';
+			var actionLnk = document.createElement('a');
+			actionLnk.className = 'geCommentActionLnk';
+			mxUtils.write(actionLnk, name);
+			action.appendChild(actionLnk);
+			
+			mxEvent.addListener(actionLnk, 'click', function(evt)
+			{
+				evtHandler(evt, comment);
+				evt.preventDefault();
+				mxEvent.consume(evt);
+			});
+			
+			actionsList.appendChild(action);
+			
+			if (hide) action.style.display = 'none';
+		};
+		
+		function collectReplies()
+		{
+			var replies = [];
+			var pdiv = cdiv;
+			
+			function collectReplies(comment) 
+			{
+				replies.push(pdiv);
+				
+				if (comment.replies != null)
+				{
+					for (var i = 0; i < comment.replies.length; i++) 
+					{
+						pdiv = pdiv.nextSibling;
+						collectReplies(comment.replies[i]); 
+					}
+				}	
+			}
+			
+			collectReplies(comment);
+			
+			return {pdiv: pdiv, replies: replies};
+		};
+		
+		function addReply(initContent, editIt, saveCallback, doResolve, doReopen)
+		{
+			var pdiv = collectReplies().pdiv;
+			
+			var newReply = file.newComment(initContent, editorUi.getCurrentUser());
+			newReply.pCommentId = comment.id;
+			
+			if (comment.replies == null) comment.replies = [];
+			
+			var replyComment = addComment(newReply, comment.replies, pdiv, level + 1);
+
+			function doAddReply()
+			{
+				showBusy(replyComment);
+				
+				comment.addReply(newReply, function(id)
+				{
+					newReply.id = id;
+					comment.replies.push(newReply);
+					showDone(replyComment);
+					
+					if (saveCallback) saveCallback();
+					
+				}, function(err)
+				{
+					doEdit();
+					showError(replyComment);
+					editorUi.handleError(err, null, null, null,
+						mxUtils.htmlEntities(mxResources.get('objectNotFound')));
+				}, doResolve, doReopen);				
+			};
+			
+			function doEdit()
+			{
+				editComment(newReply, replyComment, function(newReply)
+				{
+					doAddReply();
+				}, true);
+			};
+
+			if (editIt)
+			{
+				doEdit();
+			}
+			else
+			{
+				doAddReply();
+			}
+		};
+		
+		if (!readOnly && (level == 0 || canReplyToReplies))
+		{
+			addAction(mxResources.get('reply'), function()
+			{
+				addReply('', true);
+			}, comment.isResolved);
+		}
+		
+		var user = editorUi.getCurrentUser();
+		
+		if (user != null && user.id == comment.user.id && !readOnly)
+		{
+			addAction(mxResources.get('edit'), function()
+			{
+				function doEditComment()
+				{
+					editComment(comment, cdiv, function()
+					{
+						showBusy(cdiv);
+						
+						comment.editComment(comment.content, function()
+						{
+							showDone(cdiv);
+						}, function(err)
+						{
+							showError(cdiv);
+							doEditComment();
+							editorUi.handleError(err, null, null, null,
+								mxUtils.htmlEntities(mxResources.get('objectNotFound')));
+						});
+					});
+				};
+				
+				doEditComment();
+			}, comment.isResolved);
+			
+			addAction(mxResources.get('delete'), function()
+			{
+				editorUi.confirm(mxResources.get('areYouSure'), function()
+				{
+					showBusy(cdiv);
+					
+					comment.deleteComment(function()
+					{
+						var replies = collectReplies(comment).replies;
+						
+						for (var i = 0; i < replies.length; i++)
+						{
+							listDiv.removeChild(replies[i]);
+						}
+						
+						for (var i = 0; i < parentArr.length; i++)
+						{
+							if (parentArr[i] == comment) 
+							{
+								parentArr.splice(i, 1);
+								break;
+							}
+						}
+						
+						noComments.style.display = (listDiv.getElementsByTagName('div').length == 0) ? 'block' : 'none';
+					}, function(err)
+					{
+						showError(cdiv);
+						editorUi.handleError(err, null, null, null,
+							mxUtils.htmlEntities(mxResources.get('objectNotFound')));
+					});
+				});
+			}, comment.isResolved);
+		}
+		
+		if (!readOnly && level == 0) //Resolve is a top-level action only
+		{
+			function toggleResolve(evt)
+			{
+				function doToggle()
+				{
+					var resolveActionLnk = evt.target;
+					resolveActionLnk.innerHTML = '';
+
+					comment.isResolved = !comment.isResolved;
+					mxUtils.write(resolveActionLnk, comment.isResolved? mxResources.get('reopen') : mxResources.get('resolve'));
+					var actionsDisplay = comment.isResolved? 'none' : '';
+					var replies = collectReplies(comment).replies;
+
+					
+					var color = (uiTheme == 'dark') ? 'transparent' : (comment.isResolved? 'ghostWhite' : 'white');
+					
+					for (var i = 0; i < replies.length; i++)
+					{
+						replies[i].style.backgroundColor = color;
+						
+						var forOpenActions = replies[i].querySelectorAll('.geCommentAction');
+						
+						for (var j = 0; j < forOpenActions.length; j ++) 
+						{
+							if (forOpenActions[j] == resolveActionLnk.parentNode) continue;
+							
+							forOpenActions[j].style.display = actionsDisplay;
+						}
+
+						if (!resolvedChecked)
+						{
+							replies[i].style.display = 'none';
+						}
+					}
+					
+					var divs = listDiv.getElementsByTagName('div');
+					var visibleCount = 0;
+					
+					for (var i = 0; i < divs.length; i++)
+					{
+						if (divs[i].style.display != 'none' && divs[i].parentNode == listDiv)
+						{
+							visibleCount++;
+						}
+					}
+					
+					noComments.style.display = (visibleCount == 0) ? 'block' : 'none';
+				};
+				
+				if (comment.isResolved)
+				{
+					addReply(mxResources.get('reOpened') + ': ', true, doToggle, false, true);
+				}
+				else
+				{
+					addReply(mxResources.get('markedAsResolved'), false, doToggle, true);
+				}
+			};
+			
+			addAction(comment.isResolved? mxResources.get('reopen') : mxResources.get('resolve'), toggleResolve);
+		}
+		
+		cdiv.appendChild(actionsDiv);
+		
+		if (parent != null) 
+		{
+			listDiv.insertBefore(cdiv, parent.nextSibling);
+		}
+		else
+		{
+			listDiv.appendChild(cdiv);
+		}
+		
+		for (var i = 0; comment.replies != null && i < comment.replies.length; i++)
+		{
+			var reply = comment.replies[i];
+			reply.isResolved = comment.isResolved; //copy isResolved to child comments (replies)
+			addComment(reply, comment.replies, null, level + 1, showResolved);
+		}
+		
+		return cdiv;
+	};
+
+	if (!readOnly)
+	{
+		var addLink = link.cloneNode();
+		addLink.innerHTML = '<div class="geSprite geSprite-plus" style="display:inline-block;"></div>';
+		addLink.setAttribute('title', mxResources.get('create') + '...');
+		
+		mxEvent.addListener(addLink, 'click', function(evt)
+		{
+			var newComment = file.newComment('', editorUi.getCurrentUser());
+			var newCommentDiv = addComment(newComment, comments, null, 0);
+			
+			function doAddComment()
+			{
+				editComment(newComment, newCommentDiv, function(newComment)
+				{
+					showBusy(newCommentDiv);
+					
+					file.addComment(newComment, function(id)
+					{
+						newComment.id = id;
+						comments.push(newComment);
+						showDone(newCommentDiv);
+					}, function(err)
+					{
+						showError(newCommentDiv);
+						doAddComment();
+						editorUi.handleError(err, null, null, null,
+							mxUtils.htmlEntities(mxResources.get('objectNotFound')));
+					});
+				}, true);
+			}
+			
+			doAddComment();
+			evt.preventDefault();
+			mxEvent.consume(evt);
+		});
+		
+		ldiv.appendChild(addLink);
+	}
+
+	var resolvedLink = link.cloneNode();
+	resolvedLink.innerHTML = '<img src="/images/check.png" style="width: 16px; padding: 2px;">';
+	resolvedLink.setAttribute('title', mxResources.get('showResolved'));
+	var resolvedChecked = false;
+	
+	if (uiTheme == 'dark')
+	{
+		resolvedLink.style.filter = 'invert(100%)';
+	}
+	
+	mxEvent.addListener(resolvedLink, 'click', function(evt)
+	{
+		resolvedChecked = !resolvedChecked;
+		
+		this.className = resolvedChecked? 'geButton geCheckedBtn' : 'geButton';
+		refresh();
+		
+		evt.preventDefault();
+		mxEvent.consume(evt);
+	});
+	
+	ldiv.appendChild(resolvedLink);
+	
+	var refreshLink = link.cloneNode();
+	refreshLink.innerHTML = '<img src="/images/update16.png" style="width: 16px; padding: 2px;">';
+	refreshLink.setAttribute('title', mxResources.get('refresh'));
+
+	if (uiTheme == 'dark')
+	{
+		refreshLink.style.filter = 'invert(100%)';
+	}
+	
+	mxEvent.addListener(refreshLink, 'click', function(evt)
+	{
+		refresh();
+		
+		evt.preventDefault();
+		mxEvent.consume(evt);
+	});
+	
+	ldiv.appendChild(refreshLink);
+
+	div.appendChild(ldiv);	
+
+	var comments = [];
+
+	var refresh = mxUtils.bind(this, function()
+	{
+		listDiv.innerHTML = '<div style="padding-top:10px;text-align:center;"><img src="/images/spin.gif" valign="middle"> ' +
+			mxUtils.htmlEntities(mxResources.get('loading')) + '...</div>';
+		
+		file = editorUi.getCurrentFile();
+		
+		canReplyToReplies = file.canReplyToReplies();
+		
+		if (file != null)
+		{
+			file.getComments(function(list)
+			{
+				function sortReplies(replies)
+				{
+					if (replies != null)
+					{
+						//Sort replies old to new
+						replies.sort(function(r1, r2)
+						{
+							return new Date(r1.modifiedDate) - new Date(r2.modifiedDate);
+						});
+						
+						for (var i = 0; i < replies.length; i++)
+						{
+							sortReplies(replies[i].replies);
+						}						
+					}
+				};
+				
+				//Sort comments old to new
+				list.sort(function(c1, c2)
+				{
+					return new Date(c1.modifiedDate) - new Date(c2.modifiedDate);
+				});
+
+				listDiv.innerHTML = '';
+				listDiv.appendChild(noComments);
+				noComments.style.display = 'block';
+				comments = list;
+				
+				for (var i = 0; i < comments.length; i++)
+				{
+					sortReplies(comments[i].replies);
+					addComment(comments[i], comments, null, 0, resolvedChecked);
+				}
+			}, function()
+			{
+				listDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('error'));
+			});
+		}
+		else
+		{
+			//TODO if file is null, close the dialog
+			listDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('error'));
+		}
+	});
+
+	refresh();
+	
+	this.refreshComments = refresh;
+
+	//Refresh the modified date of each comment if the window is visible
+	var refreshCommentsTime = mxUtils.bind(this, function()
+	{
+		if (!this.window.isVisible()) return; //only update if it is visible
+		
+		var modDateDivs = listDiv.querySelectorAll('.geCommentDate');
+		var modDateDivsMap = {};
+		
+		for (var i = 0; i < modDateDivs.length; i++)
+		{
+			var div = modDateDivs[i];
+			modDateDivsMap[div.getAttribute('data-commentId')] = div;
+		}
+		
+		function processComment(comment) 
+		{
+			var div = modDateDivsMap[comment.id];
+			
+			if (div == null) return; //resolved comments
+			
+			writeCommentDate(comment, div);
+			
+			for (var i = 0; comment.replies != null && i < comment.replies.length; i++)
+			{
+				processComment(comment.replies[i]);
+			}
+		};
+		
+		for (var i = 0; i < comments.length; i++)
+		{
+			processComment(comments[i]);
+		}
+	});
+
+	//Periodically refresh time every one minute
+	setInterval(refreshCommentsTime, 60000);
+	this.refreshCommentsTime = refreshCommentsTime;
+	
+	this.window = new mxWindow(mxResources.get('comments'), div, x, y, w, h, true, true);
+	this.window.minimumSize = new mxRectangle(0, 0, 300, 200);
+	this.window.destroyOnClose = false;
+	this.window.setMaximizable(false);
+	this.window.setResizable(true);
+	this.window.setClosable(true);
+	this.window.setVisible(true);
+	
+	this.window.setLocation = function(x, y)
+	{
+		var iw = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth;
+		var ih = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight;
+		
+		x = Math.max(0, Math.min(x, iw - this.table.clientWidth));
+		y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48));
+
+		if (this.getX() != x || this.getY() != y)
+		{
+			mxWindow.prototype.setLocation.apply(this, arguments);
+		}
+	};
+	
+	var resizeListener = mxUtils.bind(this, function()
+	{
+		var x = this.window.getX();
+		var y = this.window.getY();
+		
+		this.window.setLocation(x, y);
+	});
+	
+	mxEvent.addListener(window, 'resize', resizeListener);
+
+	this.destroy = function()
+	{
+		mxEvent.removeListener(window, 'resize', resizeListener);
+		this.window.destroy();
+	}
+};

+ 51 - 0
src/main/webapp/js/diagramly/DrawioComment.js

@@ -0,0 +1,51 @@
+
+DrawioComment = function(file, id, content, modifiedDate, createdDate, isResolved, user)
+{
+	// The file having this comment 
+	this.file = file;
+	
+	// Unique ID
+	this.id = id;
+	
+	// Comment contents
+	this.content = content;
+	
+	// Comment modified date
+	this.modifiedDate = modifiedDate;
+	
+	// Comment created date
+	this.createdDate = createdDate;
+	
+	// Is comment resolved
+	this.isResolved = isResolved;
+	
+	// User created this comment
+	// Type: DrawioUser
+	this.user = user;
+	
+	this.replies = [];
+};
+
+DrawioComment.prototype.addReplyDirect = function(reply)
+{
+	if (reply != null)
+		this.replies.push(reply);
+};
+
+DrawioComment.prototype.addReply = function(reply, success, error, doResolve, doReopen)
+{
+	//Placeholder
+	success();
+};
+
+DrawioComment.prototype.editComment = function(newContent, success, error)
+{
+	//Placeholder
+	success();
+};
+
+DrawioComment.prototype.deleteComment = function(success, error)
+{
+	//Placeholder
+	success();
+};

+ 33 - 1
src/main/webapp/js/diagramly/DrawioFile.js

@@ -2023,4 +2023,36 @@ DrawioFile.prototype.destroy = function()
 		this.sync.destroy();
 		this.sync = null;
 	}
-};
+};
+
+/**
+ * Get comments of the file
+ */
+DrawioFile.prototype.getComments = function(success, error)
+{
+	success([]); //placeholder
+};
+
+/**
+ * Add a comment to the file
+ */
+DrawioFile.prototype.addComment = function(comment, success, error)
+{
+	success(Date.now()); //placeholder
+};
+
+/**
+ * Can add a reply to a reply
+ */
+DrawioFile.prototype.canReplyToReplies = function()
+{
+	return true;
+};
+
+/**
+ * Get a new comment object
+ */
+DrawioFile.prototype.newComment = function(content, user)
+{
+	return new DrawioComment(this, null, content, Date.now(), Date.now(), false, user);
+};

+ 1 - 1
src/main/webapp/js/diagramly/DriveClient.js

@@ -54,7 +54,7 @@ DriveClient.prototype.scopes = (urlParams['photos'] == '1') ?
  * Contains the hostname of the old app.
  */
 DriveClient.prototype.allFields = 'kind,id,parents,headRevisionId,etag,title,mimeType,modifiedDate,' +
-	'editable,copyable,labels,properties,downloadUrl,webContentLink,userPermission,fileSize';
+	'editable,copyable,canComment,labels,properties,downloadUrl,webContentLink,userPermission,fileSize';
 
 /**
  * Fields required for catchin up.

+ 70 - 0
src/main/webapp/js/diagramly/DriveComment.js

@@ -0,0 +1,70 @@
+DriveComment = function(file, id, content, modifiedDate, createdDate, isResolved, user, pCommentId)
+{
+	DrawioComment.call(this, file, id, content, modifiedDate, createdDate, isResolved, user);
+	this.pCommentId = pCommentId; //a reply
+};
+
+//Extends DrawioComment
+mxUtils.extend(DriveComment, DrawioComment);
+
+DriveComment.prototype.addReply = function(reply, success, error, doResolve, doReopen)
+{
+	var body = {'content': reply.content};
+	
+	if (doResolve) 
+	{
+		body.verb = 'resolve';
+	} 
+	else if (doReopen) 
+	{
+		body.verb = 'reopen';
+	}
+	
+	this.file.ui.drive.executeRequest(gapi.client.drive.replies.insert(
+		{
+			'fileId': this.file.getId(),
+			'commentId': this.id,
+			'resource': body
+		}),
+		mxUtils.bind(this, function(resp)
+		{
+			success(resp.replyId); //pass comment id
+		}), error);
+};
+
+DriveComment.prototype.editComment = function(newContent, success, error)
+{
+	this.content = newContent;
+	var body = {'content': newContent};
+	 
+	this.file.ui.drive.executeRequest(
+		this.pCommentId?
+		gapi.client.drive.replies.patch({
+			'fileId': this.file.getId(),
+			'commentId': this.pCommentId,
+			'replyId': this.id,
+			'resource': body
+		}) :
+		gapi.client.drive.comments.patch({
+			'fileId': this.file.getId(),
+			'commentId': this.id,
+			'resource': body
+		}),
+	success, error);
+};
+
+DriveComment.prototype.deleteComment = function(success, error)
+{
+	this.file.ui.drive.executeRequest(
+		this.pCommentId?
+		gapi.client.drive.replies.delete({
+			'fileId': this.file.getId(),
+			'commentId': this.pCommentId,
+			'replyId': this.id
+		}):
+		gapi.client.drive.comments.delete({
+			'fileId': this.file.getId(),
+			'commentId': this.id
+		}),
+	success, error);
+};

+ 71 - 0
src/main/webapp/js/diagramly/DriveFile.js

@@ -662,3 +662,74 @@ DriveFile.prototype.loadDescriptor = function(success, error)
 {
 	this.ui.drive.loadDescriptor(this.getId(), success, error);
 };
+
+/**
+ * Get comments of the file
+ */
+DriveFile.prototype.getComments = function(success, error)
+{
+	var currentUser = this.ui.getCurrentUser();
+	
+	function driveCommentToDrawio(file, gComment, pCommentId)
+	{
+		if (gComment.deleted) return null; //skip deleted comments
+		
+		var comment = new DriveComment(file, gComment.commentId || gComment.replyId, gComment.content, 
+				gComment.modifiedDate, gComment.createdDate, gComment.status == 'resolved',
+				gComment.author.isAuthenticatedUser? currentUser :
+				new DrawioUser(gComment.author.permissionId, gComment.author.emailAddress,
+						gComment.author.displayName, gComment.author.picture.url), pCommentId);
+		
+		for (var i = 0; gComment.replies != null && i < gComment.replies.length; i++)
+		{
+			comment.addReplyDirect(driveCommentToDrawio(file, gComment.replies[i], gComment.commentId));
+		}
+		
+		return comment;
+	};
+	
+	this.ui.drive.executeRequest(gapi.client.drive.comments.list({'fileId': this.getId()}),
+		mxUtils.bind(this, function(resp)
+	{
+		var comments = [];
+		
+		for (var i = 0; i < resp.items.length; i++)
+		{
+			var comment = driveCommentToDrawio(this, resp.items[i]);
+			
+			if (comment != null) comments.push(comment);
+		}
+		
+		success(comments);
+	}), error);
+};
+
+/**
+ * Add a comment to the file
+ */
+DriveFile.prototype.addComment = function(comment, success, error)
+{
+	var body = {'content': comment.content};
+	
+	this.ui.drive.executeRequest(gapi.client.drive.comments.insert({'fileId': this.getId(), 'resource': body}),
+		mxUtils.bind(this, function(resp)
+	{
+		success(resp.commentId); //pass comment id
+	}), error);
+};
+
+/**
+ * Can add a reply to a reply
+ */
+DriveFile.prototype.canReplyToReplies = function()
+{
+	return false;
+};
+
+/**
+ * Get a new comment object
+ */
+DriveFile.prototype.newComment = function(content, user)
+{
+	return new DriveComment(this, null, content, Date.now(), Date.now(), false, user);
+};

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 5 - 0
src/main/webapp/js/diagramly/Editor.js


+ 7 - 4
src/main/webapp/js/diagramly/EditorUi.js

@@ -3527,7 +3527,7 @@
 	 * @param {number} dx X-coordinate of the translation.
 	 * @param {number} dy Y-coordinate of the translation.
 	 */
-	EditorUi.prototype.handleError = function(resp, title, fn, invokeFnOnClose)
+	EditorUi.prototype.handleError = function(resp, title, fn, invokeFnOnClose, notFoundMessage)
 	{
 		var resume = (this.spinner != null && this.spinner.pause != null) ? this.spinner.pause() : function() {};
 		var e = (resp != null && resp.error != null) ? resp.error : resp;
@@ -3566,14 +3566,17 @@
 					}
 					else
 					{
-						msg = mxUtils.htmlEntities(mxResources.get('fileNotFoundOrDenied'));
+						msg = (notFoundMessage != null) ? notFoundMessage :
+							mxUtils.htmlEntities(mxResources.get('fileNotFoundOrDenied'));
 					}
 					
 					var id = window.location.hash;
 					
 					if (id != null && id.substring(0, 2) == '#G' && resp != null && resp.error != null &&
-						resp.error.errors != null && resp.error.errors.length > 0 &&
-						resp.error.errors[0].reason == 'fileAccess')
+						((resp.error.errors != null && resp.error.errors.length > 0 &&
+						resp.error.errors[0].reason == 'fileAccess') ||
+						(resp.error.data != null && resp.error.data.length > 0 &&
+						resp.error.data[0].reason == 'fileAccess')))
 					{
 						id = id.substring(2);
 						

+ 58 - 1
src/main/webapp/js/diagramly/Menus.js

@@ -2787,12 +2787,69 @@
 			                         'edit', '-', 'editLink', 'openLink', '-',
 			                         'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']);
 		})));
+
+		var action = editorUi.actions.addAction('comments', mxUtils.bind(this, function()
+		{
+			if (this.commentsWindow == null)
+			{
+				// LATER: Check outline window for initial placement
+				this.commentsWindow = new CommentsWindow(editorUi, document.body.offsetWidth - 380, 120, 300, 350);
+				//TODO Are these events needed?
+				this.commentsWindow.window.addListener('show', function()
+				{
+					editorUi.fireEvent(new mxEventObject('comments'));
+				});
+				this.commentsWindow.window.addListener('hide', function()
+				{
+					editorUi.fireEvent(new mxEventObject('comments'));
+				});
+				this.commentsWindow.window.setVisible(true);
+				editorUi.fireEvent(new mxEventObject('comments'));
+			}
+			else
+			{
+				this.commentsWindow.window.setVisible(!this.commentsWindow.window.isVisible());
+				this.commentsWindow.refreshCommentsTime();
+			}
+		}));
+		action.setToggleAction(true);
+		action.setSelectedCallback(mxUtils.bind(this, function() { return this.commentsWindow != null && this.commentsWindow.window.isVisible(); }));
+
+		// Destroys comments window to force update or disable if not supported
+		editorUi.editor.addListener('fileLoaded', mxUtils.bind(this, function()
+		{
+			if (this.commentsWindow != null)
+			{
+				this.commentsWindow.destroy();
+				this.commentsWindow = null;
+			}
+		}));
+		
+		// Extends toolbar dropdown to add comments
+		var viewPanelsMenu = this.get('viewPanels');
+		var viewPanelsFunct = viewPanelsMenu.funct;
 		
+		viewPanelsMenu.funct = function(menu, parent)
+		{
+			viewPanelsFunct.apply(this, arguments);
+			
+			var file = editorUi.getCurrentFile();
+			
+			if (file != null && file.constructor == DriveFile)
+			{
+				editorUi.menus.addMenuItems(menu, ['comments'], parent);
+			}
+		};
+
 		// Overrides view menu to add search and scratchpad
 		this.put('view', new Menu(mxUtils.bind(this, function(menu, parent)
 		{
+			var file = editorUi.getCurrentFile();
+
 			this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []).
-				concat(['outline', 'layers', '-']));
+				concat(['outline', 'layers']).concat((file != null && file.constructor == DriveFile) ?
+				['comments', '-'] : ['-']));
+			
 			this.addMenuItems(menu, ['-', 'search'], parent);
 			
 			if (isLocalStorage || mxClient.IS_CHROMEAPP)

+ 10 - 1
src/main/webapp/js/diagramly/Minimal.js

@@ -765,7 +765,16 @@ EditorUi.initMinimalTheme = function()
 			
 			ui.menus.addSubmenu('exportAs', menu, parent);
 
-			ui.menus.addMenuItems(menu, ['-', 'outline', 'layers', '-', 'find', 'tags'], parent);
+			ui.menus.addMenuItems(menu, ['-', 'outline', 'layers'], parent);
+			
+			var file = ui.getCurrentFile();
+			
+			if (file != null && file.constructor == DriveFile)
+			{
+				ui.menus.addMenuItems(menu, ['comments'], parent);
+			}
+			
+			ui.menus.addMenuItems(menu, ['-', 'find', 'tags'], parent);
 			
 			// Cannot use print in standalone mode on iOS as we cannot open new windows
 			if (!mxClient.IS_IOS || !navigator.standalone)

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 260 - 259
src/main/webapp/js/viewer.min.js


+ 10 - 0
src/main/webapp/resources/dia.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_am.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ar.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_bg.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_bn.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_bs.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ca.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_cs.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_da.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_de.txt

@@ -804,3 +804,13 @@ selectSiblings=Geschwisterknoten markieren
 selectParent=Vaterknoten markieren
 selectDescendants=Untergeordnete Knoten markieren
 lastSaved=Zuletzt gespeichert vor {1}
+resolve=Klären
+reopen=Erneut öffnen
+showResolved=Geklärte anzeigen
+reply=Antworten
+objectNotFound=Objekt nicht gefunden
+reOpened=Erneut geöffnet
+markedAsResolved=Als geklärt gekennzeichnet
+noCommentsFound=Keine Kommentare gefunden
+comments=Kommentare
+timeAgo=Vor {1}

+ 10 - 0
src/main/webapp/resources/dia_el.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_eo.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_es.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_et.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_fa.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_fi.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_fil.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_fr.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_gu.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_he.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_hi.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_hr.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_hu.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_i18n.txt

@@ -804,3 +804,13 @@ selectSiblings=selectSiblings
 selectParent=selectParent
 selectDescendants=selectDescendants
 lastSaved=lastSaved
+resolve=resolve
+reopen=reopen
+showResolved=showResolved
+reply=reply
+objectNotFound=objectNotFound
+reOpened=reOpened
+markedAsResolved=markedAsResolved
+noCommentsFound=noCommentsFound
+comments=comments
+timeAgo=timeAgo

+ 10 - 0
src/main/webapp/resources/dia_id.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_it.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ja.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_kn.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ko.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_lt.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_lv.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ml.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_mr.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ms.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_nl.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_no.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_pl.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_pt-br.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_pt.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ro.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ru.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_sk.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_sl.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_sr.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_sv.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_sw.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_ta.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_te.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_th.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_tr.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_uk.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_vi.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_zh-tw.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 10 - 0
src/main/webapp/resources/dia_zh.txt

@@ -804,3 +804,13 @@ selectSiblings=Select Siblings
 selectParent=Select Parent
 selectDescendants=Select Descendants
 lastSaved=Last saved {1} ago
+resolve=Resolve
+reopen=Re-open
+showResolved=Show Resolved
+reply=Reply
+objectNotFound=Object not found
+reOpened=Re-opened
+markedAsResolved=Marked as resolved
+noCommentsFound=No comments found
+comments=Comments
+timeAgo={1} ago

+ 6 - 0
src/main/webapp/styles/dark.css

@@ -149,4 +149,10 @@ html body .geStatusAlert {
 html body .geStatusAlert:hover {
 	background-color:#a20025;
 	border-color:#bd002b;
+}
+html body .geCommentContainer {
+	background-color: transparent;
+	border-width: 1px;
+	box-shadow: none;
+	color: inherit;
 }

+ 168 - 0
src/main/webapp/styles/grapheditor.css

@@ -237,6 +237,12 @@
 .geMenubarContainer .geItem:active {
 	background: #e8f0fe;
 }
+.geToolbarButton {
+	opacity: 0.6;
+}
+.geToolbarButton:active {
+	opacity: 0.2;
+}
 .mxDisabled:hover {
 	background:inherit !important;
 }
@@ -1262,3 +1268,165 @@ html td.mxWindowTitle {
     height: 27px;
     font-size: 14px;
 }
+/* Comments CSS */
+.geCommentsWin {
+	user-select: none;
+	border: 1px solid whiteSmoke;
+	height: 100%;
+	margin-bottom: 10px;
+	overflow: auto;	
+}
+
+.geCommentsToolbar {
+	position: absolute;
+	bottom: 0px;
+	left: 0px;
+	right: 0px;
+	overflow: hidden;
+	border-width: 1px 0px 0px 0px;
+	border-color: #c3c3c3;
+	border-style: solid;
+	display: block;
+	white-space: nowrap;
+}
+
+.geCommentsList {
+	position: absolute;
+	overflow: auto;
+	left: 0px;
+	right: 0px;
+	top: 0px;	
+}
+
+.geCommentContainer {
+	position: relative;
+	padding: 12px;
+	margin: 5px;
+	min-height: 50px;
+	display: block;
+	background-color: white;
+	border-width: 0px 0px 1px 0px;
+	border-color: #c3c3c3;
+	border-style: solid;
+	border-radius: 10px;
+	white-space: nowrap;
+	box-shadow: 2px 2px 6px rgba(60,64,67,.15);
+	color: #3C4043;
+}
+
+.geCommentHeader {
+	width: 100%;
+	height: 32px;
+}
+
+.geCommentUserImg {
+	width: 32px;
+	height: 32px;
+	border-radius: 50%;
+	float: left;
+	background-color: whitesmoke;
+}
+
+.geCommentHeaderTxt {
+	overflow: hidden;
+	height: 32px;
+	padding-left: 5px;
+}
+
+.geCommentUsername {
+	overflow: hidden;
+	height: 18px;
+	font-size: 15px;
+	font-weight: bold;
+	text-overflow: ellipsis;
+}
+
+.geCommentDate {
+	color: #707070;
+	overflow: hidden;
+	height: 14px;
+	font-size: 11px;
+	text-overflow: ellipsis;
+}
+
+.geCommentDate::first-letter {
+    text-transform: uppercase;
+}
+
+.geCommentTxt {
+	font-size: 14px;
+    padding-top: 5px;
+    white-space: normal;
+    min-height: 12px;
+}
+
+.geCommentEditTxtArea {
+    margin-top: 5px;
+    font-size: 14px !important;
+    min-height: 12px;
+    max-width: 100%;
+    min-width: 100%;
+	width: 100%;
+    box-sizing: border-box;
+}
+
+.geCommentEditBtns {
+	width: 100%;
+    box-sizing: border-box;
+    padding-top: 5px;
+    height: 20px;
+}
+
+.geCommentEditBtn {
+	padding: 3px 8px 3px 8px !important;
+    float: right !important;
+    margin-left: 5px;
+}
+
+.geCommentActions {
+	color: #707070;
+	font-size: 12px;
+}
+
+.geCommentActionsList {
+	list-style-type: disc;
+	margin: 0px;
+	padding: 10px 0 0 0;
+}
+
+.geCommentAction {
+	display: inline-block;
+    padding: 0;
+}
+
+.geCommentAction:before {
+	content: "\2022";
+	padding: 5px;
+} 
+
+.geCommentAction:first-child:before {
+	content: "";
+	padding: 0;
+} 
+
+.geCommentActionLnk {
+	cursor: pointer;
+	color: #707070;
+	text-decoration: none;
+}
+
+.geCommentActionLnk:hover {
+	text-decoration: underline;
+}
+
+.geCheckedBtn {
+	background-color: #ccc;
+    border-top: 1px solid black !important;
+    border-left: 1px solid black !important;
+}
+
+.geCommentBusyImg {
+	position: absolute;
+	top: 5px;
+	right: 5px;
+}