Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions site/examples/acecursors/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>ACE in Action</title>
<style type="text/css" media="screen">
*{
margin: 0;
padding: 0;
}
#editor {
width:600px;
height:700px;
}
</style>
</head>
<body>

<div id="editor">function foo(items) {
var x = "All this is syntax highlighted";
return x;
}</div>

<button id="start-togetherjs" type="button"
onclick="TogetherJS(this); return false"
data-end-togetherjs-html="End TogetherJS">
Start TogetherJS
</button>


<script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/ace.js" type="text/javascript" charset="utf-8"></script>

<script>
// TogetherJS configuration would go here, but we'll talk about that
// later
TogetherJSConfig_hubBase = "http://127.0.0.1:8080";
TogetherJSConfig_dontShowClicks = '#editor'

</script>
<script src="/togetherjs/build/togetherjs-min.js"></script>
<script>
var editor = ace.edit("editor");
var session = editor.getSession()
editor.setTheme("ace/theme/monokai");
session.setMode("ace/mode/javascript");
</script>
</body>
</html>
110 changes: 107 additions & 3 deletions togetherjs/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating", "ot"], function ($, util, session, elementFinder, eventMaker, templating, ot) {
define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating", "ot", "peers"], function ($, util, session, elementFinder, eventMaker, templating, ot, peers) {
var forms = util.Module("forms");
var assert = util.assert;

Expand Down Expand Up @@ -101,8 +101,19 @@ define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating"
constructor: function (el) {
this.element = $(el);
assert(this.element.hasClass("ace_editor"));
this.cursorMarkers= {};
this._change = this._change.bind(this);
this._editor().document.on("change", this._change);
this._changeSelection = this._changeSelection.bind(this);
this._syncCursor = ace.require("ace/lib/lang").delayedCall(this._changeSelection);
this._delayedChangeSelection = this._delayedChangeSelection.bind(this);
this._editor().editor.on("changeSelection", this._delayedChangeSelection);
//changeSelection doesn't fire when a user presses Esc to leave the multi-select
//mode, so we need this extra event listener
this._editor().editor.selection.on("singleSelect", this._delayedChangeSelection);

this.peerUpdate = this.peerUpdate.bind(this);
peers.on("new-peer identity-updated status-updated url-updated idle-updated", this.peerUpdate);
},

tracked: function (el) {
Expand All @@ -111,14 +122,82 @@ define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating"

destroy: function (el) {
this._editor().document.removeListener("change", this._change);
this._editor().editor.off("changeSelection", this._changeSelection);
peers.off("new-peer identity-updated status-updated url-updated idle-updated", this.peerUpdate);
},

update: function (msg) {
if (msg.value) {
this.init(msg);
return;
}
this._editor().document.getDocument().applyDeltas([msg.delta]);
if(msg.hasOwnProperty('delta')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a space after if here and below

this._editor().document.getDocument().applyDeltas([msg.delta]);
}
if(msg.hasOwnProperty('cursors')) {
this.updateCursorMarker(msg);
}
},

peerUpdate: function (peer) {
//status-updated
if (peer.status=='bye') {
this.peerBye(peer);
return;
}
//new-peer, identity-updated, url-updated, idle-updated
if( this.cursorMarkers.hasOwnProperty(peer.id)) {
var msg = {
peer: peer,
cursors: this.cursorMarkers[peer.id].cursors
};
this.updateCursorMarker(msg);
}
},

peerBye: function (peer) {
if ( this.cursorMarkers.hasOwnProperty(peer.id)) {
var oldMarkers = this.cursorMarkers[peer.id].markers;
oldMarkers.forEach(function(markerId){
this._editor()
.document
.removeMarker(markerId);
}, this);
delete this.cursorMarkers[peer.id];
}
},

updateCursorMarker: function (msg) {
if (msg === undefined) {
return;
}
if ( this.cursorMarkers.hasOwnProperty(msg.peer.id)) {
var oldMarkers = this.cursorMarkers[msg.peer.id].markers;
oldMarkers.forEach(function(markerId){
this._editor()
.document
.removeMarker(markerId);
}, this);
}
var markers = msg.cursors.map(function(cursor) {
var Range = ace.require('ace/range').Range;
var range = new Range(cursor.row, cursor.column,
cursor.row, cursor.column + 1);

var markerId = this._editor()
.document
.addMarker(range, "ace_selection", drawMarker, false);
return markerId;
}, this);
this.cursorMarkers[msg.peer.id] = {
markers:markers,
cursors:msg.cursors
};
function drawMarker(stringBuilder, range, left, top, config) {
var color = "background-color:"+ msg.peer.color;
var opacity = (msg.peer.url != session.currentUrl() || msg.peer.idle=="inactive") ? 0.5 : 1;
stringBuilder.push("<div class='ace_selection' style='", "opacity:", opacity, ";", "left:", left, "px;", "top:", top + 5, "px;", "height:", config.lineHeight - 5, "px;", "width:", 2, "px;", color || "", "'></div>", "<div class='ace_selection' style='", "opacity:", opacity, ";", "left:", left - 2, "px;", "top:", top, "px;", "height:", 5, "px;", "width:", 6, "px;", color || "", "'></div>");
}
},

init: function (update, msg) {
Expand All @@ -137,7 +216,7 @@ define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating"
return this.element[0].env;
},

_change: function (e) {
_change: function (e, document) {
// FIXME: I should have an internal .send() function that automatically
// asserts !inRemoteUpdate, among other things
if (inRemoteUpdate) {
Expand All @@ -151,6 +230,31 @@ define(["jquery", "util", "session", "elementFinder", "eventMaker", "templating"
element: elementFinder.elementLocation(this.element),
delta: JSON.parse(JSON.stringify(e.data))
});
},

_changeSelection: function () {
// FIXME: I should have an internal .send() function that automatically
// asserts !inRemoteUpdate, among other things
if (inRemoteUpdate) {
return;
}
var selection = this._editor().document.getSelection();
var cursors = [];
if (selection.inMultiSelectMode) {
cursors = selection.getAllRanges().map(function(range) {return range.cursor; });
} else {
cursors.push(selection.getCursor());
}
session.send({
type: "form-update",
tracker: this.trackerName,
element: elementFinder.elementLocation(this.element),
cursors: cursors
});
},

_delayedChangeSelection: function(){
this._syncCursor.delay(50);
}
});

Expand Down
2 changes: 1 addition & 1 deletion togetherjs/youtubeVideos.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function ($, util, session, elementFinder) {
// if the iframe's unique id is already set, skip it
if (($(iframe).attr("src") || "").indexOf("youtube") != -1 && !$(iframe).attr("id")) {
$(iframe).attr("id", "youtube-player"+i);
$(iframe).attr("ensablejsapi", 1);
$(iframe).attr("enablejsapi", 1);
youTubeIframes[i] = iframe;
}
});
Expand Down