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