Add support of showing authorship in editor and adjust related styles
This commit is contained in:
		
							parent
							
								
									d5549c6a84
								
							
						
					
					
						commit
						a14e7953b5
					
				| @ -65,7 +65,9 @@ function emitCheck(note) { | |||||||
|     var out = { |     var out = { | ||||||
|         updatetime: note.updatetime, |         updatetime: note.updatetime, | ||||||
|         lastchangeuser: note.lastchangeuser, |         lastchangeuser: note.lastchangeuser, | ||||||
|         lastchangeuserprofile: note.lastchangeuserprofile |         lastchangeuserprofile: note.lastchangeuserprofile, | ||||||
|  |         authors: note.authors, | ||||||
|  |         authorship: note.authorship | ||||||
|     }; |     }; | ||||||
|     realtime.io.to(note.id).emit('check', out); |     realtime.io.to(note.id).emit('check', out); | ||||||
| } | } | ||||||
| @ -314,6 +316,8 @@ function emitRefresh(socket) { | |||||||
|         ownerprofile: note.ownerprofile, |         ownerprofile: note.ownerprofile, | ||||||
|         lastchangeuser: note.lastchangeuser, |         lastchangeuser: note.lastchangeuser, | ||||||
|         lastchangeuserprofile: note.lastchangeuserprofile, |         lastchangeuserprofile: note.lastchangeuserprofile, | ||||||
|  |         authors: note.authors, | ||||||
|  |         authorship: note.authorship, | ||||||
|         permission: note.permission, |         permission: note.permission, | ||||||
|         createtime: note.createtime, |         createtime: note.createtime, | ||||||
|         updatetime: note.updatetime |         updatetime: note.updatetime | ||||||
|  | |||||||
| @ -49,6 +49,9 @@ body { | |||||||
|     min-width: 1.5em; |     min-width: 1.5em; | ||||||
|     text-align: right; |     text-align: right; | ||||||
| } | } | ||||||
|  | .CodeMirror-gutter.authorship-gutters { | ||||||
|  |     width: 8px; | ||||||
|  | } | ||||||
| .CodeMirror-matchingtag { | .CodeMirror-matchingtag { | ||||||
|     background: rgba(255, 255, 255, .1); |     background: rgba(255, 255, 255, .1); | ||||||
|     line-height: 1em; |     line-height: 1em; | ||||||
|  | |||||||
| @ -287,7 +287,7 @@ var editor = CodeMirror.fromTextArea(textit, { | |||||||
|     }, |     }, | ||||||
|     autoCloseTags: true, |     autoCloseTags: true, | ||||||
|     foldGutter: true, |     foldGutter: true, | ||||||
|     gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], |     gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"], | ||||||
|     extraKeys: defaultExtraKeys, |     extraKeys: defaultExtraKeys, | ||||||
|     flattenSpans: true, |     flattenSpans: true, | ||||||
|     addModeClass: true, |     addModeClass: true, | ||||||
| @ -2127,6 +2127,9 @@ socket.on('version', function (data) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  | var authors = []; | ||||||
|  | var authorship = []; | ||||||
|  | var authorshipMarks = {}; | ||||||
| function updateLastInfo(data) { | function updateLastInfo(data) { | ||||||
|     //console.log(data);
 |     //console.log(data);
 | ||||||
|     if (data.hasOwnProperty('createtime') && createtime !== data.createtime) { |     if (data.hasOwnProperty('createtime') && createtime !== data.createtime) { | ||||||
| @ -2142,7 +2145,212 @@ function updateLastInfo(data) { | |||||||
|         lastchangeuserprofile = data.lastchangeuserprofile; |         lastchangeuserprofile = data.lastchangeuserprofile; | ||||||
|         updateLastChangeUser(); |         updateLastChangeUser(); | ||||||
|     } |     } | ||||||
|  |     if (data.hasOwnProperty('authors') && authors !== data.authors) { | ||||||
|  |         authors = data.authors; | ||||||
|  |     } | ||||||
|  |     if (data.hasOwnProperty('authorship') && authorship !== data.authorship) { | ||||||
|  |         authorship = data.authorship; | ||||||
|  |         updateAuthorship(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | var updateAuthorship = _.throttle(updateAuthorshipInner, 50); | ||||||
|  | function initMark() { | ||||||
|  |     return { | ||||||
|  |         gutter: { | ||||||
|  |             userid: null, | ||||||
|  |             timestamp: null | ||||||
|  |         }, | ||||||
|  |         textmarkers: [] | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | function initMarkAndCheckGutter(mark, author, timestamp) { | ||||||
|  |     if (!mark) mark = initMark(); | ||||||
|  |     if (!mark.gutter.userid || mark.gutter.timestamp > timestamp) { | ||||||
|  |         mark.gutter.userid = author.userid; | ||||||
|  |         mark.gutter.timestamp = timestamp; | ||||||
|  |     } | ||||||
|  |     return mark; | ||||||
|  | } | ||||||
|  | var gutterStylePrefix = "border-left: 3px solid "; | ||||||
|  | var gutterStylePostfix = "; height: " + defaultTextHeight + "px; margin-left: 3px;"; | ||||||
|  | var textMarkderStylePrefix = "background-image: linear-gradient(to top, "; | ||||||
|  | var textMarkderStylePostfix = " 1px, transparent 1px);"; | ||||||
|  | var addStyleRule = (function () { | ||||||
|  |     var added = {}; | ||||||
|  |     var styleElement = document.createElement('style'); | ||||||
|  |     document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement); | ||||||
|  |     var styleSheet = styleElement.sheet; | ||||||
|  | 
 | ||||||
|  |     return function (css) { | ||||||
|  |         if (added[css]) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         added[css] = true; | ||||||
|  |         styleSheet.insertRule(css, (styleSheet.cssRules || styleSheet.rules).length); | ||||||
|  |     }; | ||||||
|  | }()); | ||||||
|  | function updateAuthorshipInner() { | ||||||
|  |     // ignore when ot not synced yet
 | ||||||
|  |     if (Object.keys(cmClient.state).length > 0) return; | ||||||
|  |     var authorMarks = {}; | ||||||
|  |     for (var i = 0; i < authorship.length; i++) { | ||||||
|  |         var atom = authorship[i]; | ||||||
|  |         var author = authors[atom[0]]; | ||||||
|  |         if (author) { | ||||||
|  |             var prePos = editor.posFromIndex(atom[1]); | ||||||
|  |             var preLine = editor.getLine(prePos.line); | ||||||
|  |             var postPos = editor.posFromIndex(atom[2]); | ||||||
|  |             var postLine = editor.getLine(postPos.line); | ||||||
|  |             if (prePos.ch == 0 && postPos.ch == postLine.length) { | ||||||
|  |                 for (var j = prePos.line; j <= postPos.line; j++) { | ||||||
|  |                     if (editor.getLine(j)) { | ||||||
|  |                         authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else if (postPos.line - prePos.line >= 1) { | ||||||
|  |                 var startLine = prePos.line; | ||||||
|  |                 var endLine = postPos.line; | ||||||
|  |                 if (prePos.ch == preLine.length) { | ||||||
|  |                     startLine++; | ||||||
|  |                 } else if (prePos.ch != 0) { | ||||||
|  |                     var mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3]); | ||||||
|  |                     var _postPos = { | ||||||
|  |                         line: prePos.line, | ||||||
|  |                         ch: preLine.length | ||||||
|  |                     }; | ||||||
|  |                     if (JSON.stringify(prePos) != JSON.stringify(_postPos)) { | ||||||
|  |                         mark.textmarkers.push({ | ||||||
|  |                             userid: author.userid, | ||||||
|  |                             pos: [prePos, _postPos] | ||||||
|  |                         }); | ||||||
|  |                         startLine++; | ||||||
|  |                     } | ||||||
|  |                     authorMarks[prePos.line] = mark; | ||||||
|  |                 } | ||||||
|  |                 if (postPos.ch == 0) { | ||||||
|  |                     endLine--; | ||||||
|  |                 } else if (postPos.ch != postLine.length) { | ||||||
|  |                     var mark = initMarkAndCheckGutter(authorMarks[postPos.line], author, atom[3]); | ||||||
|  |                     var _prePos = { | ||||||
|  |                         line: postPos.line, | ||||||
|  |                         ch: 0 | ||||||
|  |                     }; | ||||||
|  |                     if (JSON.stringify(_prePos) != JSON.stringify(postPos)) { | ||||||
|  |                         mark.textmarkers.push({ | ||||||
|  |                             userid: author.userid, | ||||||
|  |                             pos: [_prePos, postPos] | ||||||
|  |                         }); | ||||||
|  |                         endLine--; | ||||||
|  |                     } | ||||||
|  |                     authorMarks[postPos.line] = mark; | ||||||
|  |                 } | ||||||
|  |                 for (var j = startLine; j <= endLine; j++) { | ||||||
|  |                     if (editor.getLine(j)) { | ||||||
|  |                         authorMarks[j] = initMarkAndCheckGutter(authorMarks[j], author, atom[3]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 var mark = initMarkAndCheckGutter(authorMarks[prePos.line], author, atom[3]); | ||||||
|  |                 if (JSON.stringify(prePos) != JSON.stringify(postPos)) { | ||||||
|  |                     mark.textmarkers.push({ | ||||||
|  |                         userid: author.userid, | ||||||
|  |                         pos: [prePos, postPos] | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |                 authorMarks[prePos.line] = mark; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     var addTextMarkers = []; | ||||||
|  |     editor.eachLine(function (line) { | ||||||
|  |         var lineNumber = editor.getLineNumber(line); | ||||||
|  |         var currMark = authorMarks[lineNumber]; | ||||||
|  |         var author = currMark ? authors[currMark.gutter.userid] : null; | ||||||
|  |         if (currMark && author) { | ||||||
|  |             var className = 'authorship-gutter-' + author.color.substr(1); | ||||||
|  |             var gutters = editor.getLineHandle(lineNumber).gutterMarkers; | ||||||
|  |             if (!gutters || !gutters['authorship-gutters'] || | ||||||
|  |                 !gutters['authorship-gutters'].className || | ||||||
|  |                 !gutters['authorship-gutters'].className.indexOf(className) < 0) { | ||||||
|  |                 var styleString = gutterStylePrefix + author.color + gutterStylePostfix; | ||||||
|  |                 var rule = "." + className + "{" + styleString + "}"; | ||||||
|  |                 addStyleRule(rule); | ||||||
|  |                 var gutter = $('<div>', { | ||||||
|  |                     class: 'authorship-gutter ' + className, | ||||||
|  |                     title: author.name | ||||||
|  |                 }); | ||||||
|  |                 editor.setGutterMarker(lineNumber, "authorship-gutters", gutter[0]); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             editor.setGutterMarker(lineNumber, "authorship-gutters", null); | ||||||
|  |         } | ||||||
|  |         if (currMark && currMark.textmarkers.length > 0) { | ||||||
|  |             for (var i = 0; i < currMark.textmarkers.length; i++) { | ||||||
|  |                 var textMarker = currMark.textmarkers[i]; | ||||||
|  |                 if (textMarker.userid != currMark.gutter.userid) { | ||||||
|  |                     addTextMarkers.push(textMarker); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |     var allTextMarks = editor.getAllMarks(); | ||||||
|  |     for (var i = 0; i < allTextMarks.length; i++) { | ||||||
|  |         var _textMarker = allTextMarks[i]; | ||||||
|  |         var pos = _textMarker.find(); | ||||||
|  |         var found = false; | ||||||
|  |         for (var j = 0; j < addTextMarkers.length; j++) { | ||||||
|  |             var textMarker = addTextMarkers[j]; | ||||||
|  |             var author = authors[textMarker.userid]; | ||||||
|  |             var className = 'authorship-inline-' + author.color.substr(1); | ||||||
|  |             var obj = { | ||||||
|  |                 from: textMarker.pos[0], | ||||||
|  |                 to: textMarker.pos[1] | ||||||
|  |             }; | ||||||
|  |             if (JSON.stringify(pos) == JSON.stringify(obj) && _textMarker.className && | ||||||
|  |                 _textMarker.className.indexOf(className) > -1) { | ||||||
|  |                 addTextMarkers.splice(j, 1); | ||||||
|  |                 j--; | ||||||
|  |                 found = true; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!found && _textMarker.className && _textMarker.className.indexOf('authorship-inline') > -1) { | ||||||
|  |             _textMarker.clear(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     for (var i = 0; i < addTextMarkers.length; i++) { | ||||||
|  |         var textMarker = addTextMarkers[i]; | ||||||
|  |         var author = authors[textMarker.userid]; | ||||||
|  |         var rgbcolor = hex2rgb(author.color); | ||||||
|  |         var colorString = "rgba(" + rgbcolor.red + "," + rgbcolor.green + "," + rgbcolor.blue + ",0.7)"; | ||||||
|  |         var styleString = textMarkderStylePrefix + colorString + textMarkderStylePostfix; | ||||||
|  |         var className = 'authorship-inline-' + author.color.substr(1); | ||||||
|  |         var rule = "." + className + "{" + styleString + "}"; | ||||||
|  |         addStyleRule(rule); | ||||||
|  |         var _textMarker = editor.markText(textMarker.pos[0], textMarker.pos[1], { | ||||||
|  |             className: 'authorship-inline ' + className, | ||||||
|  |             title: author.name | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     authorshipMarks = authorMarks; | ||||||
|  | } | ||||||
|  | editor.on('update', function () { | ||||||
|  |     $('.authorship-gutter:not([data-original-title])').tooltip({ | ||||||
|  |         container: '.CodeMirror-lines', | ||||||
|  |         placement: 'right', | ||||||
|  |         delay: { "show": 500, "hide": 100 } | ||||||
|  |     }); | ||||||
|  |     $('.authorship-inline:not([data-original-title])').tooltip({ | ||||||
|  |         container: '.CodeMirror-lines', | ||||||
|  |         placement: 'bottom', | ||||||
|  |         delay: { "show": 500, "hide": 100 } | ||||||
|  |     }); | ||||||
|  |     // clear tooltip which described element has been removed
 | ||||||
|  |     $('[id^="tooltip"]').each(function (index, element) { | ||||||
|  |         $ele = $(element); | ||||||
|  |         if ($('[aria-describedby="' + $ele.attr('id') + '"]').length <= 0) $ele.remove(); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
| socket.on('check', function (data) { | socket.on('check', function (data) { | ||||||
|     //console.log(data);
 |     //console.log(data);
 | ||||||
|     updateLastInfo(data); |     updateLastInfo(data); | ||||||
|  | |||||||
| @ -4,4 +4,4 @@ | |||||||
|  * @link https://github.com/NextStepWebs/codemirror-spell-checker |  * @link https://github.com/NextStepWebs/codemirror-spell-checker | ||||||
|  * @license MIT |  * @license MIT | ||||||
|  */ |  */ | ||||||
| .CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){border-bottom:1px dashed rgba(255,0,0,.8)} | .CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){border-bottom:2px dotted rgba(255,0,0,.8)} | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user