define () -> class CursorManager UPDATE_INTERVAL: 500 constructor: (@ide) -> @clients = {} @ide.socket.on "clientTracking.clientUpdated", (cursorUpdate) => @onRemoteClientUpdate(cursorUpdate) @ide.socket.on "clientTracking.clientDisconnected", (client_id) => @onRemoteClientDisconnect(client_id) @ide.editor.on "change:doc", (session) => @bindToAceSession(session) @ide.editor.on "mousemove", (e) => @mousePosition = e.position @updateVisibleNames() bindToAceSession: (session) -> @clients = {} @ide.editor.aceEditor.on "changeSelection", => @onLocalCursorUpdate() onLocalCursorUpdate: () -> if !@cursorUpdateTimeout? @cursorUpdateTimeout = setTimeout (=> @_sendLocalCursorUpdate() delete @cursorUpdateTimeout ), @UPDATE_INTERVAL _sendLocalCursorUpdate: () -> cursor = @ide.editor.getCursorPosition() if !@currentCursorPosition? or not (cursor.row == @currentCursorPosition.row and cursor.column == @currentCursorPosition.column) @currentCursorPosition = cursor @ide.socket.emit "clientTracking.updatePosition", { row: cursor.row column: cursor.column doc_id: @ide.editor.getCurrentDocId() } onRemoteClientUpdate: (clientData) -> if clientData.id != ide.socket.socket.sessionid client = @clients[clientData.id] ||= {} client.row = clientData.row client.column = clientData.column client.name = clientData.name client.doc_id = clientData.doc_id @redrawCursors() onRemoteClientDisconnect: (client_id) -> @removeCursor(client_id) delete @clients[client_id] removeCursor: (client_id) -> client = @clients[client_id] return if !client? @ide.editor.removeMarker(client.cursorMarkerId) delete client.cursorMarkerId redrawCursors: () -> for clientId, clientData of @clients do (clientId, clientData) => if clientData.cursorMarkerId? @removeCursor(clientId) if clientData.doc_id == @ide.editor.getCurrentDocId() colorId = @getColorIdFromName(clientData.name) clientData.cursorMarkerId = @ide.editor.addMarker { row: clientData.row column: clientData.column length: 1 }, "sharelatex-remote-cursor", (html, range, left, top, config) -> div = """