diff --git a/bundles/org.eclipse.orion.client.collab/web/orion/collab/otAdapters.js b/bundles/org.eclipse.orion.client.collab/web/orion/collab/otAdapters.js index bfceaa25fb..1819923537 100644 --- a/bundles/org.eclipse.orion.client.collab/web/orion/collab/otAdapters.js +++ b/bundles/org.eclipse.orion.client.collab/web/orion/collab/otAdapters.js @@ -40,7 +40,7 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function }; OrionSocketAdapter.prototype.constructor = OrionSocketAdapter; - + /** * Send authenticate message */ @@ -307,6 +307,7 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function case 'client-left': this.collabClient.removePeer(msg.clientId); + this.collabClient.textView._removePeerHighlight(msg.clientId); break; default: @@ -574,7 +575,7 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function }; OrionEditorAdapter.prototype.getSelection = function () { - return ot.Selection.createCursor(this.editor.getSelection().start); + return new ot.Selection([new ot.Selection.Range(this.editor.getSelection().start, this.editor.getSelection().end)]); }; OrionEditorAdapter.prototype.setSelection = function (selection) { @@ -608,15 +609,6 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function var lastLine = this.model.getLineCount()-1; var lineStartOffset = this.model.getLineStart(currLine); - if (offset) { - //decide whether or not it is worth sending (if line has changed or needs updating). - if (currLine !== this.myLine || currLine === lastLine || currLine === 0) { - // Send this change - } else { - return; - } - } - this.myLine = currLine; // Self-tracking @@ -630,7 +622,12 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function if (this.changeInProgress) { this.selectionChanged = true; } else { - this.trigger('selectionChange'); + if(!this.editor._listener.mouseDown){ + // Trigger 'selectionChange' (send messges) only if mouse is up. + // This prevents from sending multiple messages + // while user is selecting. + this.trigger('selectionChange'); + } } }; @@ -646,7 +643,10 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function var peer = this.collabClient.getPeer(clientId); var name = peer ? peer.name : undefined; color = peer ? peer.color : color; + this.updateLineAnnotation(clientId, selection, name, color); + this.updateHighlight(clientId, selection, name, color); + var self = this; return { clear: function() { @@ -655,6 +655,13 @@ define(['orion/collab/collabPeer', 'orion/collab/ot', 'orion/uiUtils'], function }; }; + OrionEditorAdapter.prototype.updateHighlight = function(id, selection, name, color, force) { + // Extracting 'highlight' information out of 'selection' object + var ranges = selection.ranges[0]; + + this.collabClient.textView._addHighlight(id, color, ranges.anchor, ranges.head); + } + OrionEditorAdapter.prototype.updateLineAnnotation = function(id, selection, name, color, force) { force = !!force; name = name || 'Unknown'; diff --git a/bundles/org.eclipse.orion.client.editor/web/orion/editor/textView.js b/bundles/org.eclipse.orion.client.editor/web/orion/editor/textView.js index 84abdfad51..4b564fdfb0 100644 --- a/bundles/org.eclipse.orion.client.editor/web/orion/editor/textView.js +++ b/bundles/org.eclipse.orion.client.editor/web/orion/editor/textView.js @@ -5531,7 +5531,8 @@ define("orion/editor/textView", [ //$NON-NLS-1$ _parent.appendChild(div1); div1.innerHTML = newArray(2).join("a"); //$NON-NLS-1$ rect1 = div1.getBoundingClientRect(); - charWidth = Math.ceil(rect1.right - rect1.left); + charWidth_float = rect1.right - rect1.left; + charWidth = Math.ceil(charWidth_float); if (this._wrapOffset || this._marginOffset) { div1.innerHTML = newArray(this._wrapOffset + 1 + (util.isWebkit ? 0 : 1)).join(" "); //$NON-NLS-1$ rect1 = div1.getBoundingClientRect(); @@ -5550,6 +5551,7 @@ define("orion/editor/textView", [ //$NON-NLS-1$ scrollWidth: scrollWidth, wrapWidth: wrapWidth, marginWidth: marginWidth, + charWidth_float: charWidth_float, charWidth: charWidth, invalid: invalid }; @@ -5967,6 +5969,7 @@ define("orion/editor/textView", [ //$NON-NLS-1$ /* Destroy DOM */ this._domSelection = null; + this._peerHighlight = null; this._clipboardDiv = null; this._rootDiv = null; this._scrollDiv = null; @@ -7370,6 +7373,14 @@ define("orion/editor/textView", [ //$NON-NLS-1$ } } }, + /** + * Update highlights + */ + _updatePeerHighlight: function() { + if (this._peerHighlight) { + this._peerHighlight.update(); + } + }, _update: function(hScrollOnly) { if (this._redrawCount > 0) { return; } if (this._updateTimer) { @@ -7752,7 +7763,9 @@ define("orion/editor/textView", [ //$NON-NLS-1$ } } } + this._updateDOMSelection(); + this._updatePeerHighlight(); if (needUpdate) { var ensureCaretVisible = this._ensureCaretVisible; @@ -7961,10 +7974,265 @@ define("orion/editor/textView", [ //$NON-NLS-1$ this.redraw(); this._resetLineWidth(); } - } - };//end prototype + }, + /** + * Called from 'otAdapter.js'. Initiates work on highlight on the 'textView' + * + * @param {String} id - Peer id + * @param {String} color - Color that will be used for highlight peer's selection + * @param {Int} start - Char where peer's selection started + * @param {Int} end - Char where peer's selection ended + */ + _addHighlight: function(id, color, start, end) { + // if the instance of 'textView' doesnt have an instance of PeerHighlight, + // then create one + if (this._peerHighlight === undefined) { + this._peerHighlight = new PeerHighlight(this); + } + + // Add highlight + this._peerHighlight.addHighlight(id, color, start, end); + }, + /** + * This function is called when a peer leaves. This function calls + * the 'removePeer' funtion of the 'PeerHighlight' instance of + * this TextView. + */ + _removePeerHighlight: function(id) { + this._peerHighlight.removePeer(id); + } + }; + + /** + * This class manages collab peer's highlights on user's screen + */ + function PeerHighlight(view) { + // List of class variables and functions + // + // Variables + // _view + // _divs + // _highlights + // + // Constructor + // PeerHighlight(view) + // + // Functions + // addHighlight + // createHighlightDiv + // update + // destroy + // + + /** + * Reference to parent 'textView' instance + */ + this._view = view; + + /** + * Array of all child 'divs' that (together) creates highlight + * + * _divs = [ + * userId1: { + * div1:
+ * div2: + * div3: + * }, + * userId2: { + * ... + * }, + * ... + * ] // end of _divs + * + */ + this._divs = []; + + /** + * Data structure that stores all highlights. + * This also enforces rendering of only one highlight at a time. + * Even though collab peers can highlight multiple sections on their side. + * + * _highlights = [ + * idOfUser1: { + * 'color': 'hexString', + * 'start': int, + * 'end': int + * }, + * idOfUser2: { + * ... + * }, + * ... + * ] + */ + this._highlights = []; + + } + + /** + * This function adds peer's highlight on the screen + */ + PeerHighlight.prototype.addHighlight = function(id, color, start, end) { + + // Add/update data structure + this._highlights[id] = { + 'color': color, + 'start': start, + 'end': end + }; + + // Update the views + this.update(); + } + + /** + * This function removes: + * - the peer (from '_highlights') and + * - The 3 div elements (in '_divs') that presents highlight of the peer + */ + PeerHighlight.prototype.removePeer = function(peerId) { + // Remove peer from data structure + delete this._highlights.peerId; + + // Remove divs + for (var divNum in this._divs[peerId]) { + this._divs[peerId][divNum].remove(); + } + } + + PeerHighlight.prototype.createHighlightDiv = function(color) { + var div = util.createElement(this._view._parent.ownerDocument, "div"); //$NON-NLS-1$ + + div.style.position = 'absolute'; + + // Styles + div.style.backgroundColor = color; + div.style.opacity = 0.25; + div.style.zIndex = 2; + + // Let clicks fall through the div + div.style.pointerEvents = 'none'; + + return div; + } + + /** + * This function updates highlight views + */ + PeerHighlight.prototype.update = function() { + // Get references and store them in local variables + var model = this._view._model; + var parent = this._view._clipDiv; + var lineOffsets = model._model._lineOffsets; + + + // Dimension constants + var lineHeight = this._view._getLineHeight(); + var charWidth = this._view._calculateMetrics().charWidth_float; + /** + * Relative position in the document + * that is currently at the top edge of the view + */ + var toppx = this._view.getTopPixel(); + var divVoffset = 0; + if (toppx === 0) { + divVoffset = 4; + } + // End of Dimension constants + + + // For all peer highlights + for (var userid in this._highlights) { + + // store start, end, and color of peer in local variable + var start = this._highlights[userid].start; + var end = this._highlights[userid].end; + var color = this._highlights[userid].color; + + // get line number of 'start' and 'end' (char location) of the highlight + var startLineNumber = model.getLineAtOffset(start); + var endLineNumber = model.getLineAtOffset(end); + // Convert them from 'line number' to 'px' + var startpx = startLineNumber * lineHeight; + var endpx = endLineNumber * lineHeight; + + + // Reference to the 3 divs + var div1, div2, div3; + if (this._divs[userid]){ + // Get divs if divs already exist for the peer + div1 = this._divs[userid].div1; + div2 = this._divs[userid].div2; + div3 = this._divs[userid].div3; + } else { + // Create divs if they dont already exist for this peer + div1 = this.createHighlightDiv(color); + div2 = this.createHighlightDiv(color); + div3 = this.createHighlightDiv(color); + + // Append Child + this._divs[userid] = {} + this._divs[userid].div1 = div1; + this._divs[userid].div2 = div2; + this._divs[userid].div3 = div3; + parent.appendChild(div1); + parent.appendChild(div2); + parent.appendChild(div3); + } + + + // ******************* Highlight div dimension logic ****************** // + // TODO Fix: account for 2px on the right. i.e. width = 100% - 2px + + // DIV 1 - for first line selection + div1.style.height = lineHeight + 'px'; + div1.style.top = (startpx - toppx + divVoffset) + 'px'; + div1.style.left = ((charWidth * (start - lineOffsets[startLineNumber])) + 2) + 'px'; + // End of div 1 + + // DIV 2 - for middle block + div2.style.top = (startpx - toppx + lineHeight + divVoffset) + 'px'; + div2.style.left = '2px'; + div2.style.width = '100%'; + // End of div 2 + + // DIV 3 - for trailing end + div3.style.height = lineHeight + 'px'; + div3.style.top = (endpx - toppx + divVoffset) + 'px'; + div3.style.left = '2px'; + // End of div 3 + + // If peer's whole selection is on one line + if (startLineNumber === endLineNumber) { + div1.style.width = ((end - start) * charWidth) + 'px'; + div2.style.height = '0px'; + div3.style.width = '0px'; + } else { + // If peer's selection spans multiple lines: draw... + // div1 only on first line, + // div2 for the middle block, and + // div3 for the trailing end of the peer's selection + div1.style.width = '100%'; + div2.style.height = ((endpx - toppx) - (startpx - toppx) - lineHeight) + 'px'; + div3.style.width = (charWidth * (end - lineOffsets[endLineNumber])) + 'px'; + } + // *************** End of Highlight div dimension logic *************** // + + } // end of for each peer highlight loop + + } // End of PeerHighlight.update() + + PeerHighlight.prototype.destroy = function() { + // Remove all highlight divs + for (var peerId in this._divs) { + this.removePeer(peerId); + } + + this._view = null; + this._divs = null; + this._highlights = null; + } + mEventTarget.EventTarget.addMixin(TextView.prototype); return {TextView: TextView}; }); - diff --git a/bundles/org.eclipse.orion.client.ui/web/defaults.pref b/bundles/org.eclipse.orion.client.ui/web/defaults.pref index 122d31be54..045991215c 100644 --- a/bundles/org.eclipse.orion.client.ui/web/defaults.pref +++ b/bundles/org.eclipse.orion.client.ui/web/defaults.pref @@ -1,15 +1,19 @@ { - "/plugins":{ - "plugins/orionSharedWorker.js": true, - "shell/plugins/shellPagePlugin.html": true, - "plugins/site/sitePlugin.html": true, - "plugins/languages/json/jsonPlugin.html": true, - "git/plugins/gitPlugin.html":true, - "webtools/plugins/webToolsPlugin.html":true, - "javascript/plugins/javascriptPlugin.html":true, - "edit/content/imageViewerPlugin.html":true, - "edit/content/jsonEditorPlugin.html":true, - "cfui/plugins/cFPlugin.html":true, - "cfui/plugins/cFDeployPlugin.html":true - } + "/plugins": { + "plugins/orionSharedWorker.js": true, + "shell/plugins/shellPagePlugin.html": true, + "plugins/site/sitePlugin.html": true, + "plugins/languages/json/jsonPlugin.html": true, + "git/plugins/gitPlugin.html": true, + "webtools/plugins/webToolsPlugin.html": true, + "javascript/plugins/javascriptPlugin.html": true, + "edit/content/imageViewerPlugin.html": true, + "edit/content/jsonEditorPlugin.html": true, + "cfui/plugins/cFPlugin.html": true, + "cfui/plugins/cFDeployPlugin.html": true, + "collab/plugins/collabPlugin.html": true + }, + "/collab": { + "hubUrl": "http://localhost:8082/" + } } diff --git a/modules/orionode.collab.hub/config.js b/modules/orionode.collab.hub/config.js index 94bf66e6c1..4cbe6d1bdb 100644 --- a/modules/orionode.collab.hub/config.js +++ b/modules/orionode.collab.hub/config.js @@ -17,7 +17,7 @@ module.exports = { /** * Orion url */ - 'orion': "http://localhost:8084/", + 'orion': "http://localhost:8081/", /** * Load url. Make sure end with / */ diff --git a/modules/orionode/endpoint.json b/modules/orionode/endpoint.json new file mode 100644 index 0000000000..d6e05ccf36 --- /dev/null +++ b/modules/orionode/endpoint.json @@ -0,0 +1,8 @@ +[{ + "endpoint": "/sharedWorkspace", + "module": "./lib/sharedWorkspace", + "extraOptions": { + "root": "/sharedWorkspace/tree/file", + "fileRoot": "/file" + } +}] diff --git a/modules/orionode/orion.conf b/modules/orionode/orion.conf index a62776f0b3..a677c25d0b 100644 --- a/modules/orionode/orion.conf +++ b/modules/orionode/orion.conf @@ -19,7 +19,7 @@ orion.oauth.google.client= orion.oauth.google.secret= -orion.single.user=true +orion.single.user=false orion.buildId= orion.autoUpdater.url= @@ -66,5 +66,5 @@ orion.mongodb.url=mongodb://localhost/orion_multitenant orion.mongodb.cf=false #The attributes for collaboration -orion.collab.enabled= -orion.jwt.secret= \ No newline at end of file +orion.collab.enabled=true +orion.jwt.secret=orion collab