1function P2PCollab(ui) 2{ 3 socket = io(App.SOCKET_IO_SRV); 4 5 var svgP1 = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="684.000000pt" height="1024.000000pt" viewBox="0 0 684.000000 1024.000000" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)" stroke="none" fill="'; 6 var svgP2 = '<path d="M0 5305 l0 -4940 568 567 c1170 1168 1637 1627 1644 1613 4 -7 242 -579 529 -1271 286 -693 523 -1262 527 -1266 4 -3 368 175 809 395 l802 402 -539 1294 c-297 712 -540 1296 -540 1298 0 2 682 3 1515 3 833 0 1515 3 1515 8 0 4 -1537 1544 -3415 3422 l-3415 3415 0 -4940z m3091 1175 l2604 -2599 -1304 -1 c-1236 0 -1303 -1 -1299 -17 3 -10 265 -641 582 -1402 318 -761 582 -1395 587 -1408 8 -22 -3 -29 -366 -210 -241 -121 -378 -184 -384 -178 -5 6 -262 622 -572 1370 -309 748 -564 1362 -566 1365 -2 2 -428 -420 -946 -938 -518 -518 -945 -942 -950 -942 -4 0 -7 1701 -7 3780 0 2224 4 3780 9 3780 5 0 1181 -1170 2612 -2600z"/></g></svg>'; 7 8 var graph = ui.editor.graph; 9 var userCount = 0; 10 var userColors = [ 11 '#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', 12 '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', 13 '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', 14 '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', 15 '#000000' 16 ]; 17 var connectedUsers = {}, messageId = 1, clientLastMsgId = {}; 18 var myClientId, newClients = {}, p2pClients = {}, useSocket = true, fileJoined = false; 19 20 function sendMessage(type, data) 21 { 22 var user = ui.getCurrentUser(); 23 24 if (!fileJoined || user == null || user.email == null) return; 25 26 var msg = JSON.stringify({from: myClientId, id: messageId, type: type, 27 userId: user.id, username: user.displayName, data: data}); 28 messageId++; 29 30 if (useSocket) 31 { 32 socket.emit('message', msg); 33 } 34 35 for (p2pId in p2pClients) 36 { 37 p2pClients[p2pId].send(msg); 38 } 39 }; 40 41 this.sendMessage = sendMessage; 42 43 graph.addMouseListener( 44 { 45 startX: 0, 46 startY: 0, 47 scrollLeft: 0, 48 scrollTop: 0, 49 mouseDown: function(sender, me) {}, 50 mouseMove: function(sender, me) //TODO debounce this function 51 { 52 var tr = graph.view.translate; 53 var s = graph.view.scale; 54 sendMessage('cursor', {x: me.graphX / s - tr.x, y: me.graphY / s - tr.y}); 55 }, 56 mouseUp: function(sender, me) {} 57 }); 58 59 function processMsg(msg) 60 { 61 msg = JSON.parse(msg); 62 63 //Safeguard from duplicate messages 64 if (clientLastMsgId[msg.from] >= msg.id) return; 65 66 clientLastMsgId[msg.from] = msg.id; 67 var username = msg.username? msg.username : 'Anonymous'; 68 var userId = msg.userId; 69 var cursor; 70 71 if (connectedUsers[userId] == null) 72 { 73 var clr = userColors[userCount]; 74 75 connectedUsers[userId] = { 76 cursor: document.createElement('div'), 77 index: userCount, 78 color: clr 79 }; 80 81 userCount++; 82 cursor = connectedUsers[userId].cursor; 83 cursor.style.position = 'absolute'; 84 cursor.style.zIndex = 5000; 85 var svg = 'data:image/svg+xml;base64,' + btoa(svgP1 + clr + '">' + svgP2); 86 cursor.innerHTML = '<img src="' + svg + '" style="width:16px"><div style="color:' + clr + '">' + 87 username + '</div>'; 88 document.body.appendChild(cursor); 89 } 90 else 91 { 92 cursor = connectedUsers[userId].cursor; 93 } 94 95 var msgData = msg.data; 96 97 switch (msg.type) 98 { 99 case 'cursor': 100 var tr = graph.view.translate; 101 var s = graph.view.scale; 102 var container = ui.diagramContainer; 103 var offset = mxUtils.getOffset(container); 104 105 msgData.x = (tr.x + msgData.x) * s - container.scrollLeft + offset.x; 106 msgData.y = (tr.y + msgData.y) * s - container.scrollTop + offset.y; 107 108 cursor.style.left = msgData.x + 'px'; 109 cursor.style.top = msgData.y + 'px'; 110 break; 111 case 'diff': 112 var file = ui.getCurrentFile(); 113 114 if (file.sync != null) 115 { 116 file.sync.p2pCatchup(msgData.data, msgData.from, msgData.to, msgData.id, file.getDescriptor(), function() 117 { 118 console.log('Diff Synced'); 119 }, function() 120 { 121 console.log('Diff Error'); 122 }); 123 } 124 break; 125 } 126 } 127 128 socket.on('message', processMsg); 129 130 function createPeer(id, initiator) 131 { 132 if (!SimplePeer.WEBRTC_SUPPORT) 133 { 134 return; 135 } 136 137 var p = new SimplePeer({ 138 initiator: initiator 139 }); 140 141 p.on('signal', function(data) 142 { 143 socket.emit('sendSignal', {to: id, from: myClientId, signal: data}); 144 }); 145 146 p.on('error', function(err) 147 { 148 delete newClients[id]; 149 console.log('error', err); //TODO Handle errors 150 }); 151 152 p.on('connect', function() 153 { 154 p2pClients[id] = p; 155 delete newClients[id]; 156 157 if (Object.keys(newClients).length == 0) 158 { 159 useSocket = false; 160 socket.emit('movedToP2P', ''); 161 } 162 }); 163 164 p.on('close', function() 165 { 166 delete p2pClients[id]; 167 }); 168 169 p.on('data', processMsg); 170 171 newClients[id] = p; 172 173 return p; 174 }; 175 176 socket.on('clientsList', function(data) 177 { 178 myClientId = data.cId; 179 180 for (var i = 0; i < data.list.length; i++) 181 { 182 createPeer(data.list[i], true); 183 } 184 }); 185 186 socket.on('signal', function(data) 187 { 188 var p; 189 190 if (newClients[data.from]) 191 { 192 p = newClients[data.from]; 193 } 194 else 195 { 196 p = createPeer(data.from, false); 197 useSocket = true; 198 } 199 200 p.signal(data.signal); 201 }); 202 203 socket.on('newClient', function(clientId) 204 { 205 useSocket = true; 206 }); 207 208 209 this.joinFile = function(channelId) 210 { 211 socket.emit('join', {name: channelId}); 212 fileJoined = true; 213 }; 214}; 215