1 // wonder_core.js
  2 //----------------
  3 
  4 
  5 // -------------------------------
  6 // Enums.js
  7 // -------------------------------
  8 
  9 /**
 10  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
 11  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
 12  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
 13  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
 14  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
 15  * @author Kay Haensge <Kay.Haensge@telekom.de>
 16  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
 17  */
 18 
 19 /**
 20  * Enumeration for the {@link Message} types
 21  * @readonly
 22  * @enum {string}
 23  */
 24 var MessageType = {
 25         /** Message to invite a peer to a conversation. */
 26         INVITATION              : "invitation",
 27         /** Answer for conversation accepted. */
 28         ACCEPTED                : "accepted",
 29         /** Message contains connectivity candidate */
 30         CONNECTIVITY_CANDIDATE  : "connectivityCandidate",
 31         /** Answer for conversation not accepted. */
 32         NOT_ACCEPTED            : "notAccepted",
 33         /** Message to cancel an invitation */
 34         CANCEL                  : "cancel",
 35         /** Message to add a {@link Resource} to the conversation */
 36         ADD_RESOURCE            : "addResource",
 37         /** Message to remove a {@link Participant} from the conversation */
 38         REMOVE_PARTICIPANT      : "removeParticipant",
 39         /** Message to finish the communication with a peer */
 40         BYE                     : "bye",
 41         /** Message to add a new {@link Resource} */
 42         UPDATE                  : "update",
 43         /** Answer to add a new {@link Resource} */
 44         UPDATED                 : "updated",
 45     
 46         /** Message to offer a role (TO BE IMPLEMENTED) */
 47         OFFER_ROLE              : "offerRole",
 48         /** Message to setup redirection (TO BE IMPLEMENTED) */
 49         REDIRECT                : "redirect",
 50         /** Message to remove a {@link Resource} from the conversation (TO BE IMPLEMENTED) */
 51         RESOURCE_REMOVED        : "resourceRemoved",
 52         /** Message to share a {@link Resource} in the conversation (TO BE IMPLEMENTED) */
 53         SHARE_RESOURCE          : "shareResource"
 54 };
 55 
 56 /**
 57  * Enumeration for the {@link Participant} statuses
 58  * @readonly
 59  * @enum {string}
 60  */
 61 var ParticipantStatus = {
 62         WAITING                 : "waiting",
 63         PENDING                 : "pending",
 64         PARTICIPATING           : "participating",
 65         NOT_PARTICIPATING       : "not_participating",
 66         PARTICIPATED            : "participated",
 67         MISSED                  : "missed",
 68         FAILED                  : "failed",
 69         CREATED                 : "created",
 70         ACCEPTED                : "accepted"
 71 };
 72 
 73 var ConversationStatus = {
 74         OPENED                  : "opened",
 75         INACTIVE                : "inactive",
 76         FAILED                  : "failed",
 77         ACTIVE                  : "active",
 78         CLOSED                  : "closed",
 79         CREATED                 : "created",
 80         RECORDING               : "recording",
 81         PLAYING                 : "playing",
 82         PAUSED                  : "paused",
 83         STOPPED                 : "stopped"
 84 
 85 };
 86 
 87 var ConversationTopicStatus = {
 88         PENDING                 : "pending",
 89         ACTIVE                  : "active",
 90         INACTIVE                : "inactive",
 91         CLOSED                  : "closed"
 92 };
 93 
 94 var IdentityStatus = {
 95         IDLE                    : "idle",
 96         UNAVAILABLE             : "unavailable",
 97         BUSY                    : "busy",
 98         AVAILABLE               : "available",
 99         ON_CONVERSATION         : "onConversation"
100 };
101 
102 /**
103  * Enumeration for the {@link Resource} types
104  * @readonly
105  * @enum {string}
106  */
107 var ResourceType = {
108         /** Webcam + microphone as source for mediastream */
109         AUDIO_VIDEO             : "audioVideo",
110         /** Microphone as source for audiostream */
111         AUDIO_MIC               : "audioMic",
112         /** Webcam as source for videostream */
113         VIDEO_CAM               : "videoCam",
114         /** Plain text chat */
115         CHAT                    : "chat",
116         /** File sending (TO BE IMPLEMENTED)*/
117         FILE                    : "file",
118         /** Screen content as source for videostream (TO BE IMPLEMENTED)*/
119         SCREEN                  : "screen",
120         /** Other types */
121         OTHER                   : "other",
122         /** Photo (TO BE IMPLEMENTED) */
123         PHOTO                   : "photo",
124         /** Video (TO BE IMPLEMENTED) */
125         VIDEO                   : "video",
126         /** Music (TO BE IMPLEMENTED) */
127         MUSIC                   : "music"
128 };
129 
130 
131 var ResourceStatus = {
132         NEW                     : "new",
133         SHARED                  : "shared",
134         NOT_SHARED              : "notShared",
135         PLAYING                 : "playing",
136         PAUSED                  : "paused",
137         STOPPED                 : "stopped",
138         ENDED                   : "ended",
139         RECORDING               : "recording",
140         LIVE                    : "live"
141 };
142 // -------------------------------
143 // adapter.js
144 // -------------------------------
145 
146 var RTCPeerConnection = null;
147 var getUserMedia = null;
148 var attachMediaStream = null;
149 var reattachMediaStream = null;
150 var webrtcDetectedBrowser = null;
151 var webrtcDetectedVersion = null;
152 
153 function trace(text) {
154   // This function is used for logging.
155   if (text[text.length - 1] == '\n') {
156     text = text.substring(0, text.length - 1);
157   }
158   console.log((performance.now() / 1000).toFixed(3) + ": " + text);
159 }
160 
161 if (navigator.mozGetUserMedia) {
162   console.log("This appears to be Firefox");
163 
164   webrtcDetectedBrowser = "firefox";
165 
166   webrtcDetectedVersion =
167                   parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1]);
168 
169   // The RTCPeerConnection object.
170   RTCPeerConnection = mozRTCPeerConnection;
171 
172   // The RTCSessionDescription object.
173   RTCSessionDescription = mozRTCSessionDescription;
174 
175   // The RTCIceCandidate object.
176   RTCIceCandidate = mozRTCIceCandidate;
177 
178   // Get UserMedia (only difference is the prefix).
179   // Code from Adam Barth.
180   getUserMedia = navigator.mozGetUserMedia.bind(navigator);
181 
182   // Creates iceServer from the url for FF.
183   createIceServer = function(url, username, password) {
184     var iceServer = null;
185     var url_parts = url.split(':');
186     if (url_parts[0].indexOf('stun') === 0) {
187       // Create iceServer with stun url.
188       iceServer = { 'url': url };
189     } else if (url_parts[0].indexOf('turn') === 0 &&
190                (url.indexOf('transport=udp') !== -1 ||
191                 url.indexOf('?transport') === -1)) {
192       // Create iceServer with turn url.
193       // Ignore the transport parameter from TURN url.
194       var turn_url_parts = url.split("?");
195       iceServer = { 'url': turn_url_parts[0],
196                     'credential': password,
197                     'username': username };
198     }
199     return iceServer;
200   };
201 
202   // Attach a media stream to an element.
203   attachMediaStream = function(element, stream) {
204     console.log("Attaching media stream");
205     element.mozSrcObject = stream;
206     element.play();
207   };
208 
209   reattachMediaStream = function(to, from) {
210     console.log("Reattaching media stream");
211     to.mozSrcObject = from.mozSrcObject;
212     to.play();
213   };
214 
215   // Fake get{Video,Audio}Tracks
216   MediaStream.prototype.getVideoTracks = function() {
217     return [];
218   };
219 
220   MediaStream.prototype.getAudioTracks = function() {
221     return [];
222   };
223 } else if (navigator.webkitGetUserMedia) {
224   console.log("This appears to be Chrome");
225 
226   webrtcDetectedBrowser = "chrome";
227   webrtcDetectedVersion =
228              parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]);
229 
230   // Creates iceServer from the url for Chrome.
231   createIceServer = function(url, username, password) {
232     var iceServer = null;
233     var url_parts = url.split(':');
234     if (url_parts[0].indexOf('stun') === 0) {
235       // Create iceServer with stun url.
236       iceServer = { 'url': url };
237     } else if (url_parts[0].indexOf('turn') === 0) {
238       if (webrtcDetectedVersion < 28) {
239         // For pre-M28 chrome versions use old TURN format.
240         var url_turn_parts = url.split("turn:");
241         iceServer = { 'url': 'turn:' + username + '@' + url_turn_parts[1],
242                       'credential': password };
243       } else {
244         // For Chrome M28 & above use new TURN format.
245         iceServer = { 'url': url,
246                       'credential': password,
247                       'username': username };
248       }
249     }
250     return iceServer;
251   };
252 
253   // The RTCPeerConnection object.
254   RTCPeerConnection = webkitRTCPeerConnection;
255 
256   // Get UserMedia (only difference is the prefix).
257   // Code from Adam Barth.
258   getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
259 
260   // Attach a media stream to an element.
261   attachMediaStream = function(element, stream) {
262     if (typeof element.srcObject !== 'undefined') {
263       element.srcObject = stream;
264     } else if (typeof element.mozSrcObject !== 'undefined') {
265       element.mozSrcObject = stream;
266     } else if (typeof element.src !== 'undefined') {
267       element.src = URL.createObjectURL(stream);
268     } else {
269       console.log('Error attaching stream to element.');
270     }
271   };
272 
273   reattachMediaStream = function(to, from) {
274     to.src = from.src;
275   };
276 
277   // The representation of tracks in a stream is changed in M26.
278   // Unify them for earlier Chrome versions in the coexisting period.
279   if (!webkitMediaStream.prototype.getVideoTracks) {
280     webkitMediaStream.prototype.getVideoTracks = function() {
281       return this.videoTracks;
282     };
283     webkitMediaStream.prototype.getAudioTracks = function() {
284       return this.audioTracks;
285     };
286   }
287 
288   // New syntax of getXXXStreams method in M26.
289   if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
290     webkitRTCPeerConnection.prototype.getLocalStreams = function() {
291       return this.localStreams;
292     };
293     webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
294       return this.remoteStreams;
295     };
296   }
297 } else {
298   console.log("Browser does not appear to be WebRTC-capable");
299 }
300 
301 // -------------------------------
302 // helpfunctions.js
303 // -------------------------------
304 
305 /**
306 * @ignore
307 */
308 
309 function uuid4() {
310     return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
311 }
312 
313 function guid() {
314     return uuid4() + uuid4() + '-' + uuid4() + '-' + uuid4() + '-' + uuid4()
315             + '-' + uuid4() + uuid4() + uuid4();
316 }
317 // -------------------------------
318 // Identity.js
319 // -------------------------------
320 
321 /**
322  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
323  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
324  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
325  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
326  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
327  * @author Kay Haensge <Kay.Haensge@telekom.de>
328  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
329  */
330     
331 /*
332  * TODO: State-machine for transitions of IdentityStatus should be described
333  * TODO: Questions about MessagingStub-Listeners 
334  * Why do the addListener and removeListener methods have a Message as second param?
335  * What is the purpose? Which message type would it be?
336  */
337 
338 /**
339  * @class
340  * The Identity represents a user and contains all information needed to support 
341  * Conversation services including the service endpoint to retrieve the protocol stack 
342  * (Messaging Stub) that will be used to establish a signalling channel 
343  * with the Identity domain messaging server.
344  * </br><b>NOTE:</b> Identities are only created by using the corresponding create-methods of the Idp.
345  * This constructor will throw an exception if it is used directly.
346  */
347 function Identity(rtcIdentity, idpRtcIdentity) {
348 
349 	if ( rtcIdentity )
350 		throw "Illegal attempt to create an Identity --> Please use Idp.getInstance().createIdentity(...) instead."
351 	
352 	// We use a string, RTCIdentityAssertion is not available.
353 	this.rtcIdentity = idpRtcIdentity;
354 	this.id = new Date().getTime();
355 	console.log( "####################");
356 	console.log( "created new Identiy for: " + this.rtcIdentity + " with id = " + this.id);
357 	console.trace();
358 	console.log( "####################");
359 	this.idp = "";
360 	this.status = IdentityStatus.IDLE; // not initialized yet TODO: do state changes in a central place
361 	
362 	this.messagingStubLibUrl = "";
363 	this.messagingStub;
364 	this.notificationAddress;
365 	this.messagingAddress;
366 	this.credentials;
367 	this.tone;
368 	this.avatar;
369 };
370 
371 /**
372  * This method downloads a messaging stub and keeps a reference to it in a local
373  * attribute, if not already done before. That means the download will only be performed once.
374  * After download it invokes the given callback with a reference to the downloaded MessagingStub.
375  * 
376  * @param callback {callback(MessagingStub)} callback that is invoked with messagingStub as param; if download failed then the stub param is empty
377  */
378 Identity.prototype.resolve = function( callback ) {
379 	var that = this;
380 	console.log( "resolving identity: " + this.rtcIdentity);
381 	if ( ! this.messagingStub.impl ) {
382 		// not resolved yet --> let's ask Idp for a stub with the same downloadUri
383 		var knownStub = Idp.getInstance().getResolvedStub(this.messagingStubLibUrl);
384 		if ( knownStub) {
385 			this.messagingStub = knownStub;
386 			callback(this.messagingStub);
387 			return;
388 		}
389 		
390 		console.log( "downloading Messaging stub from: " + this.messagingStubLibUrl );
391 
392 		// parse the downloadURL to get the name of the Stub
393 		var pathArr = this.messagingStubLibUrl.split("/");
394 		var stubName = pathArr[pathArr.length-1];
395 		stubName = stubName.substring(0, stubName.lastIndexOf("."));
396 		console.log("stub-name is: " + stubName );
397 		
398 		var check = function(stub, callback, count) {
399 			if ( typeof(window[stub]) == "function" ) {
400 				// instantiate stub
401 				var messagingStub = new window[stub]();
402 				//  should be an object now
403 				if ( typeof(messagingStub) == "object" ) {
404 					// assign the new messagingStub object to the "impl" field of the container stub
405 					that.messagingStub.setImpl(messagingStub);
406 					that.messagingStub.message = "stub downloaded from: " + that.messagingStubLibUrl;
407 					// return container-stub in callback
408 					callback(that.messagingStub);
409 				}
410 			}
411 			else {
412 				count++;
413 				if ( count < 20 )
414 					setTimeout( check, 500,  stub, callback, count );
415 				else {
416 					callback(); 
417 				}
418 			}
419 		};
420 		this.loadJSfile( this.messagingStubLibUrl );
421 		setTimeout( check, 100, stubName, callback, 0 );
422 	}
423 	else {
424 		console.log( this.rtcIdentity + ": no need to download stub from: " + this.messagingStubLibUrl);
425 		callback( this.messagingStub );
426 	}
427 };
428 
429 
430 /**@ignore 
431  * 
432  * @param subscriber :
433  *            Identity ... The identity of the subscriber
434  */
435 Identity.prototype.subscribe = function(subscriber) {
436 	// TODO: This is presence related stuff --> postponed
437 };
438 
439 /**@ignore 
440  * publish
441  * 
442  * @param status :
443  *            String ... The status to publish
444  * @param target :
445  *            Identity [] ... The identity/identities to publish the status to
446  * @param context :
447  *            String ... The context
448  * 
449  */
450 Identity.prototype.publish = function(status, target, context) {
451 	// TODO: This is presence related stuff --> postponed
452 	// TODO Send a publish message
453 };
454 
455 /**@ignore 
456  * getStatus
457  * 
458  * @returns IdentityStatus ... gets the status attribute for a participant
459  * 
460  */
461 Identity.prototype.getStatus = function() {
462 	return this.status;
463 };
464 
465 /**@ignore 
466  * getMessagingStubDownloadUrl
467  * 
468  * @returns IdentityStatus ... gets the status attribute for a participant
469  * 
470  */
471 Identity.prototype.getMessagingStubDownloadUrl = function() {
472 	//will be needed to the Idp 
473 	return this.messagingStubDownloadUrl;
474 };
475 
476 
477 /**@ignore
478  *
479  * OnLastMessagingListerner is invoked by the MessagingStub as soon as the last listener has un-subscribed.
480  * We use this callback to disconnect and unload the MessagingStub. 
481  * 
482  */
483 Identity.prototype.onLastMessagingListener = function() {
484 	if ( this.messagingStub )
485 		this.messagingStub.disconnect();
486 	// "undefine" messagingStub 
487 	delete this.messagingStub;
488 };
489 
490 /**@ignore 
491  * Simple function to load an external Javascript file at runtime.
492  * TODO: This might be a security risk, because the file is executed immediately.
493  * @param url ... url of the file to be loaded
494  */
495 Identity.prototype.loadJSfile = function(url) {
496 	var fileref = document.createElement('script');
497 	fileref.setAttribute("type", "text/javascript");
498 	fileref.setAttribute("src", url);
499 	if (typeof fileref != "undefined")
500 		document.getElementsByTagName("head")[0].appendChild(fileref);
501 };
502 
503 
504 // -------------------------------
505 // Idp.js
506 // -------------------------------
507 
508 /**
509  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
510  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
511  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
512  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
513  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
514  * @author Kay Haensge <Kay.Haensge@telekom.de>
515  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
516  */
517 
518 /**
519  * The Idp is a singleton object, there will always be just one instance of it, 
520  * no matter how often the constructor is called.
521  * @class
522  */
523 function Idp(rtcIdentity, options) {
524 
525 	// ensure that there is always just a singleton instance of the Idp, 
526 	// no matter how often the constructor is called
527 	if (arguments.callee.singleton) {
528 		return arguments.callee.singleton;
529 	}
530 	arguments.callee.singleton = this;
531 
532 	if ( ! options )
533 		options = {};
534 	//needs to know the domain of my identity
535 	var that = this;
536 	this.domain = options.domain || '150.140.184.246';
537 	this.port = options.port || '28017';
538 	this.protocol = options.protocol || 'https';
539 	this.path = options.path || '';
540 	this.messagingstubs = [];
541 	this.identities = []; // to collect all created Identities [ messagingAddress , Identity]
542 	this.pendingIdentities = {};
543 	this.ownMessagingLibUrl;
544 	this.ownRtcIdentity = rtcIdentity;
545 	// initialize with a new MessagingStub delegator/container
546 	this.myOwnMessagingStub = new MessagingStub();
547 	console.log("created idp with domain:port: " + this.domain + ":" + this.port);
548 }
549 
550 /**
551  * This is a getter for an already created instance of the IDP.
552  * The params are optional. In case there was no instance already created before, 
553  * the params can also be given here and will then be used for initial creation of the object.
554  */
555 Idp.getInstance = function(rtcIdentity, options) {
556 	return new Idp(rtcIdentity, options);
557 };
558 
559 
560 Idp.prototype.checkForIdentity = function(rtcIdentity) {
561 	// do we already have this rtcIdentity in the identities array ?
562 	for (var i = 0; i < this.identities.length; i++) {
563 		if (rtcIdentity == this.identities[i].identity.rtcIdentity) {
564 			return this.identities[i].identity;
565 		}
566 	}
567 	return null;
568 };
569 
570 Idp.prototype.getResolvedStub = function(downloadUri) {
571  	// do we already have a stub for the same downloadUri with impl != undefined
572  	for (var i = 0; i < this.messagingstubs.length; i++) {
573 	 	if (downloadUri == this.messagingstubs[i].name) {
574 	 		if ( this.messagingstubs[i].messagingStub.impl)
575 	 			return this.messagingstubs[i].messagingStub;
576 	 	}
577  	}
578  	return null;
579 };
580 
581 /**
582  * This method takes either a single rtcIdentity or an array of rtcIdentities and 
583  * creates Identity objects from them. The successfully created Identities are 
584  * then returned in an Array in the success callback.
585  * If one or more rtcIdentities can't be created then the returned array is shorter 
586  * than the given array.
587  */
588 Idp.prototype.createIdentities = function(rtcIdentities, onSuccessCallback, onErrorCallback) {
589 	console.log("Idp > createIdentities.");
590 	var results = [];
591 	var ids = [];
592 	var count = 0;
593 	var that = this;
594 
595 	if (! (rtcIdentities instanceof Array)) {
596 		ids.push(rtcIdentities);
597 	}
598 	else {
599 		ids = rtcIdentities;
600 	}
601 
602 	var internalSuccessCallback = function(identity) {
603 		count++;
604 		results.push(identity);
605 		if(count < ids.length)
606 			that.createIdentity(ids[count], internalSuccessCallback, internalErrorCallback);
607 		else //if(count == ids.length){
608 			onSuccessCallback(results);
609 			
610 	};
611 
612 	var internalErrorCallback = function() {
613 		console.log("Idp > internalErrorCallback error");
614 		count++;
615 		if (count == ids.length)
616 			onSuccessCallback(results);
617 	}
618 
619     
620     this.createIdentity(ids[0], internalSuccessCallback, internalErrorCallback);
621 };
622 
623 
624 Idp.prototype.createIdentity = function(rtcIdentity, onSuccessCallback, onErrorCallback) {
625 
626 	// handle potentially wrong usage 
627 	// check for the possibility that the given rtcIdentity is not a String but already of type identity
628 	if (rtcIdentity instanceof Identity)
629 		// if yes return it and stop
630 		return rtcIdentity;
631 
632 	// handle potentially wrong usage 
633 	if (rtcIdentity instanceof Array) {
634 		throw "Wrong usage. Don't call createIdentity() with an array as first param --> use createIdentities() instead"
635 		return;
636 	}
637 
638 	// does the Idp already know this identity ?
639 	var that = this;
640 	var identity = this.checkForIdentity(rtcIdentity);
641 
642 	// if identity already known, return it and stop
643 	if (identity) {
644 		onSuccessCallback(identity);
645 		return;
646 	}
647 	
648 	var pendingIdentity = this.pendingIdentities[rtcIdentity];
649 	
650 	console.log("pendingIdentity: " + pendingIdentity);
651 	if ( pendingIdentity && rtcIdentity == pendingIdentity.rtcIdentity ) {
652 		console.log("pendingIdentity matches rtcIdentity: " + rtcIdentity + " --> returning it");
653 		onSuccessCallback(pendingIdentity);
654 		return;
655 	}
656 	console.log("no matching pendingIdentity --> creating new one");
657 	pendingIdentity = new Identity(null, rtcIdentity);
658 	this.pendingIdentities[rtcIdentity] = pendingIdentity;
659 
660 	// do a lookup in the IDP-DB
661     loadJSfile('http://' + this.domain + ':' + this.port + '/webrtc/users/?filter_rtcIdentity=' + rtcIdentity +'&jsonp=returnIdentity');
662     //loadJSfile(this.protocol + '://' + this.domain + ':' + this.port + '/' + this.path + rtcIdentity);
663 
664 	// @callback function in the url
665 	returnIdentity = function(data) {
666 		//see if the identity exists
667 		if (data.total_rows > 0) {
668 			var localStubURL = data.rows[0].localMsgStubURL;
669 			var generalStubURL = data.rows[0].messagingStubURL;
670 
671 			// first identity is expected to be the own identity !?
672 			if (that.identities.length <= 0) {
673 				// use local stub url, if available, general stub if no local available
674 				if (localStubURL) {
675 					console.log("found localMsgStubURL for IDP owning Identity: " + localStubURL);
676 					that.ownMessagingLibUrl = localStubURL;
677 				}
678 				else
679 					that.ownMessagingLibUrl = generalStubURL;
680 			}
681 
682 			var existStub = false;
683 			var index = null;
684 
685 			// invoke without rtcIdentity, otherwise exception will be thrown
686 			//identity = new Identity(null, data.rows[0].rtcIdentity);
687 			identity = pendingIdentity;
688 
689 			// if new identity has the same local stub url as the idp itself, then use this one
690 			if (localStubURL === that.ownMessagingLibUrl) {
691 				console.log("use localMsgStubURL for new Identity: " + localStubURL);
692 				identity.messagingStubLibUrl = that.ownMessagingLibUrl;
693 			}
694 			else
695 				identity.messagingStubLibUrl = generalStubURL;
696 
697 			//create the identity with the right fields
698 			identity.messagingAddress = data.rows[0].messagingAddress;
699 			identity.idp = that;
700 
701 			identity.credentials = {
702 				"username": data.rows[0].rtcIdentity,
703 				"password": data.rows[0].password
704 			};
705 
706 			//if it is equal then messagingStub of remote equals to mine
707 			if (typeof that.ownMessagingLibUrl !== 'undefined') {
708 				if (that.ownMessagingLibUrl == identity.messagingStubLibUrl) {
709 					identity.messagingStub = that.myOwnMessagingStub;
710 				} else {
711 					for (var i = 0; i < that.messagingstubs.length; i++) {
712 						//compare if already exist
713 						if (that.messagingstubs[i].name == identity.messagingStubLibUrl) {
714 							existStub = true;
715 							index = i;
716 							break;
717 						}
718 					}
719 					if (existStub) {
720 						//if exist the stub then identity stub equals to the exist one
721 						identity.messagingStub = that.messagingstubs[index].messagingStub;
722 					} else {
723 						// @pchainho TODO should only instantiate a general messagingStub, the MessagingStub lib is not downloaded at this point
724 						var stub = new MessagingStub();
725 						identity.messagingStub = stub;
726 						that.messagingstubs.push({
727 							"name": identity.messagingStubLibUrl,
728 							"messagingStub": stub //put the general messagingstub in a way that can be shared to the other clients and then use It
729 						});
730 					}
731 				}
732 			}
733 			that.identities.push({
734 				"messagingAddress": data.rows[0].messagingAddress,
735 				"identity": identity
736 			});
737 			if ( pendingIdentity && rtcIdentity == pendingIdentity.rtcIdentity ) {
738 				console.log("cleaning up pendingIdentity");
739 				delete that.pendingIdentities[rtcIdentity];
740 			}
741 			onSuccessCallback(identity);
742 		} else {
743 			onErrorCallback();
744 		}
745 	};
746 }
747 
748 // @pchainho retrieve Identity from its Messaging Address. Needed to process received messages in the Stub
749 
750 Idp.prototype.getIdentity = function(messageAddress) {
751 	for (var i = 0; i < this.identities.length; i++) {
752 		if (this.identities[i].messagingAddress === messageAddress)
753 			return this.identities[i].identity;
754 	}
755 
756 }
757 //load the file with the information about an identity
758 loadJSfile = function(url) {
759 	var fileref = document.createElement('script');
760 	fileref.setAttribute("type", "text/javascript");
761 	fileref.setAttribute("src", url);
762 	if (typeof fileref != "undefined")
763 		document.getElementsByTagName("head")[0].appendChild(fileref);
764 };
765 
766 
767 
768 /*
769  This class implement the creation of remote identities and the creation of me
770  
771  In the creation of this.me: 
772  this.createIdentity will be called and create an identity with the parameters that are retrieved on the "GET" operation.
773  After that do a callback function with the identity information as a parameter.
774  Then: this.me = identity; and do the resolve. I added a the listener parameter to this identity add the application listener to his identity.
775  and then connects to the stub that is returned by the resolve.
776  
777  
778  For remote identities. will verify if this.me is already defined if it is not callbackerror (not implemented yet),
779  if this.me is defined then will create the identity with the fields retrieved from the idpserver and then add the stub
780  to this.messagingstubs and the identity with the correspondent messagingServer in the this.identities array.
781  
782  The getIdentity is not working yet <- fix it soon */
783 
784 // -------------------------------
785 // Message.js
786 // -------------------------------
787 
788 /**
789  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
790  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
791  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
792  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
793  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
794  * @author Kay Haensge <Kay.Haensge@telekom.de>
795  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
796  */
797 
798 
799 /**
800  * @class
801  * Message - This class is a data-holder for all messages that are sent between the domains.  
802  * @param {Identity} from - Sender of the message
803  * @param {Identity[]} [to] - Recipients of the message
804  * @param {MessageBody} body - Message body (a json struct)
805  * @param  {MessageType} type - Type of the Message (@see MessageType)
806  * @param  {string} [context] - ID of the conversation. (Optional. For conversation related messages it is mandatory.)
807  */
808 function Message(from, to, body, type, context) {
809     // generate unique id for this message
810     this.id = guid();
811     this.from = from;
812     this.to = to;
813     this.body = body;
814     this.type = type;
815 
816     // Optional. For conversation related messages it is mandatory 
817     this.contextId = context;
818 
819     this.reply_to_uri;
820     this.previous;
821 }
822 
823 /**
824  * @ignore
825  *
826  * newReplyMessage - This is a special factory method for a "reply-message".
827  * It takes a previous message and swaps their from and to fields.
828  * 
829  * @param {MessageBody} body - Message body (a json struct)
830  * @param {Message} previousMessage - Message to generate the reply from.
831  * @param {MessageType} type - Message type.
832  * @param {Identity} me - reply_to_uri identity.
833  */
834 Message.prototype.newReplyMessage = function(body, previousMessage, type, me) {
835     if (!previousMessage || !body || !type)
836         return;
837     // create a new message with swapped from and to fields (taken from previous message)
838     // DONE: take Myself as from, take all previous.to - ME plus original sender as to
839     var to = new Array();
840 
841     // Take all previous to - ME
842     previousMessage.to.every(function(element, index, array) {
843         if (element != me)
844             to.push(element);
845     });
846 
847     // + original sender
848     to.push(previousMessage.from);
849 
850     var rm = new Message(me, to, body, type);
851     rm.previous = previousMessage;
852     rm.reply_to_uri = me;
853     return (rm);
854 };
855 // -------------------------------
856 // MessageFactory.js
857 // -------------------------------
858 
859 /**
860  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
861  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
862  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
863  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
864  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
865  * @author Kay Haensge <Kay.Haensge@telekom.de>
866  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
867  */
868 
869 
870 /**
871  * @class
872  * This class creates WONDER-compliant messages. Please note that all functions in this class are static, so there is no need to create MessageFactory objects.
873  */
874 function MessageFactory() {
875 }
876 
877 
878 /**
879  * createInvitationMessage - Creates an Invitation message, the connectionDescription field will be empty and has to be filled before sending.
880  *
881  *
882  * @param {Identity} from - The {@link Identity} that figures as sender of the message.
883  * @param {Identity[]} to - The Array of {@link Identity} that figures as receiver of the message. 
884  * @param {string} contextId - The contextId of the conversation related to the invitation.
885  * @param {ResourceConstraints} constraints - The resource constraints for the resources initialized on conversation start.
886  * @param {string} [conversationURL] - The URL of the conversation (optional).
887  * @param {string} [subject] - The subject of the conversation. (optional).
888  * @param {Identity} [hosting] - The host of the conversation (optional). [NOT IMPLEMENTED, by default the host will be the one starting the conversation]
889  * @return The created Message
890  *
891  */
892 MessageFactory.createInvitationMessage = function(from, to, contextId, constraints, conversationURL, subject, hosting, agenda, peers) {
893     var invitationBody = new Object();
894     invitationBody.conversationURL = conversationURL;
895     invitationBody.connectionDescription = "";
896     invitationBody.subject = subject;
897     invitationBody.hosting = hosting;
898     invitationBody.agenda = agenda;
899     invitationBody.peers = peers;
900     invitationBody.constraints = constraints;
901 
902     var invitationMessage = new Message(from, to, invitationBody, MessageType.INVITATION, contextId);
903     return invitationMessage;
904 }
905 
906 /**
907  * createAnswerMessage - Creates an Answer message, the connectionDescription field will be empty and has to be filled before sending.
908  *
909  * @param {Identity} from - The {@link Identity} that figures as sender of the message.
910  * @param {Identity[]} to - The Array of {@link Identity} that figures as receiver of the message. 
911  * @param {string} contextId - The contextId of the conversation related to the invitation.
912  * @param {ResourceConstraints} constraints - The resource constraints for the resources initialized on conversation start.
913  * @param {Identity} [hosting] - The host of the conversation (optional). [NOT IMPLEMENTED, by default the host will be the one starting the conversation]
914  * @param {Identity[]} connected - Array of {@link Identity} that are already connected to the conversation. Used to establish the order in the connection flow for multiparty.
915  * @return The created Message
916  *
917  */
918 MessageFactory.createAnswerMessage =  function(from, to, contextId, constraints, hosting, connected) {
919     var answerBody = new Object();
920     answerBody.connectionDescription = "";
921     answerBody.hosting = hosting;
922     answerBody.connected = connected;
923     answerBody.constraints = constraints;
924     
925     var answerMessage = new Message(from,to,answerBody,MessageType.ACCEPTED,contextId);
926     return answerMessage;
927 }
928 
929 /**
930  * createCandidateMessage - Creates a Message containing an ICE candidate
931  *
932  * @param {Identity} from - The {@link Identity} that figures as sender of the message.
933  * @param {Identity[]} to - The Array of {@link Identity} that figures as receiver of the message. 
934  * @param {string} contextId - The contextId of the conversation related to the invitation.
935  * @param {string} label - The label of the candidate.
936  * @param {string} id - The id of the candidate.
937  * @param {string} candidate - The ICE candidate string.
938  * @param {boolean} lastCandidate - Boolean indicating if the candidate is the last one. If true, include the full SDP in the candidate parameter for compatibility with domains that don't support trickling.
939  * @return The created Message
940  *
941  */
942 
943 MessageFactory.createCandidateMessage = function (from, to, contextId, label, id, candidate, lastCandidate) {
944     if (lastCandidate == false) {
945         var candidateBody = new Object();
946         candidateBody.label = label;
947         candidateBody.id = id;
948         candidateBody.candidateDescription = candidate;
949         candidateBody.lastCandidate = false;
950     }
951     else{
952         var candidateBody = new Object();
953         candidateBody.label = label;
954         candidateBody.id = id;
955         candidateBody.candidateDescription = "";
956         candidateBody.connectionDescription = candidate;
957         candidateBody.lastCandidate = true;
958     }
959     
960     var candidateMessage = new Message(from,to,candidateBody,MessageType.CONNECTIVITY_CANDIDATE,contextId);
961     return candidateMessage;
962 }
963 
964 
965 
966 /**
967  * createUpdateMessage - Creates an Update message, the newConnectionDescription field will be empty and has to be filled before sending.
968  *
969  *
970  * @param {Identity} from - The {@link Identity} that figures as sender of the message.
971  * @param {Identity[]} to - The Array of {@link Identity} that figures as receiver of the message. 
972  * @param {string} contextId - The contextId of the conversation related to the invitation.
973  * @param {ResourceConstraints} newConstraints - The resource constraints for the resources to update.
974  * @return The created Message
975  *
976  */
977 MessageFactory.createUpdateMessage = function(from, to, contextId, newConstraints) {
978     var updatebody = new Object();
979     updatebody.newConnectionDescription = "";
980     updatebody.newConstraints = newConstraints;
981     //updatebody.hosting = hosting;
982     //updatebody.agenda = agenda;
983     //updatebody.dataCodecs = dataCodecs;
984 
985     var updateMessage = new Message(from, to, updatebody, MessageType.UPDATE, contextId);
986     return updateMessage;
987 }
988 
989 
990 /**
991  * createUpdatedMessage - Creates an Updated message, the newConnectionDescription field will be empty and has to be filled before sending.
992  *
993  *
994  * @param {Identity} from - The {@link Identity} that figures as sender of the message.
995  * @param {Identity[]} to - The Array of {@link Identity} that figures as receiver of the message. 
996  * @param {string} contextId - The contextId of the conversation related to the invitation.
997  * @param {ResourceConstraints} newConstraints - The resource constraints for the resources to update.
998  * @return The created Message
999  *
1000  */
1001 MessageFactory.createUpdatedMessage = function(from, to, contextId, newConstraints) {
1002     var updatebody = new Object();
1003     updatebody.newConnectionDescription = "";
1004     updatebody.newConstraints = newConstraints;
1005     //updatebody.hosting = hosting;
1006     //updatebody.agenda = agenda;
1007     //updatebody.dataCodecs = dataCodecs;
1008     
1009     var updateMessage = new Message(from, to, updatebody, MessageType.UPDATED, contextId);
1010     return updateMessage;
1011 }
1012 
1013 
1014 MessageFactory.createNotAccepted =  function(message) {
1015     var notAcceptedMessage = new Message(message.to[0], message.from,"",MessageType.NOT_ACCEPTED, message.contextId);
1016     return notAcceptedMessage;
1017 }
1018 
1019 
1020 // -------------------------------
1021 // MessagingStub.js
1022 // -------------------------------
1023 
1024 /**
1025  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1026  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1027  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1028  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1029  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1030  * @author Kay Haensge <Kay.Haensge@telekom.de>
1031  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1032  */
1033 
1034 /**
1035  *
1036  * @class
1037  * The MessagingStub implements the the protocol Stack used to communicate with a certain Messaging server.
1038  * It defines a set of methods that must be implemented in order to support a new domain.
1039  * 
1040  */
1041 function MessagingStub() {
1042 	this.impl = null;
1043 	this.message = "No implementation downloaded and assigned to this stub yet!";
1044 
1045 	// do the listener handling already here
1046 	this.listeners = new Array(new Array(), new Array(), new Array());
1047 	this.buffer = new Array();
1048 }
1049 
1050 
1051 MessagingStub.prototype.setImpl = function(stubImplementation) {
1052 	this.impl = stubImplementation;
1053 	// put a ref to the base stub into the impl
1054 	this.impl.baseStub = this;
1055 };
1056 
1057 /** @ignore
1058  *
1059  * addListener - Adds a listener. If the listener exists with the same contextID, doesn't add it again.
1060  * @param {StubEvtHandler} listener - Listener to execute its do(message) when a new message arrives. 
1061  * @param {URI} [rtcIdentity] - The RTCIdentity of the Identity to be notified. (optional)
1062  * @param {string} [contextId] - The ID of the context to be notified. If not specified it will receive invitation messages and messages without contextID. (optional)
1063  */
1064 MessagingStub.prototype.addListener = function(listener, rtcIdentity, contextId) {
1065 	// Checks that the listener is not on the list with the same context already
1066 	var index = 0;
1067 	while (this.listeners[0].indexOf(listener, index) != -1) {
1068 		index = this.listeners[0].indexOf(listener, index);
1069 		if (this.listeners[2][index] == contextId && this.listeners[1][index] == rtcIdentity)
1070 			return;
1071 	}
1072 
1073 	// Adds the listener with the contextID to the listeners list.
1074 	this.listeners[0].push(listener);
1075 	this.listeners[1].push(rtcIdentity);
1076 	this.listeners[2].push(contextId);
1077 
1078 	for (var i = this.buffer.length - 1; i > -1; i--) {
1079 		var message = this.buffer[i];
1080 		var filtered_listeners = [];
1081 		var idx = this.listeners[1].indexOf(message.from.rtcIdentity);
1082 		while (idx != -1) {
1083 			if (this.listeners[2][idx] == message.contextId)
1084 				filtered_listeners.push(this.listeners[0][idx]);
1085 			idx = this.listeners.indexOf(message.from.rtcIdentity, idx + 1);
1086 		}
1087 		filtered_listeners.forEach(function(element, index, array) {
1088 			element(message);
1089 		});
1090 		if (filtered_listeners.length != 0)
1091 			this.buffer.splice(i, 1);
1092 	}
1093 };
1094 
1095 
1096 /** 
1097  * sendMessage - Sends the specified message.
1098  * @param {Message} message - Message to send. 
1099  */
1100 MessagingStub.prototype.sendMessage = function(message) {
1101 	if (this.impl) {
1102 		this.impl.sendMessage(message);
1103 	}
1104 	else {
1105 		console.log(this.message);
1106 	}
1107 };
1108 
1109 
1110 /**
1111  * @ignore
1112  * 
1113  * removeListener - Removes a listener.
1114  * @param {StubEvtHandler} listener - Listener to execute its do(message) when a new message arrives. 
1115  * @param {URI} [rtcIdentity] - The RTCIdentity of the Identity. (required only if the listener to remove included it)
1116  * @param {string} [contextId] - The ID of the context. (required only if the listener to remove included it)
1117  */
1118 MessagingStub.prototype.removeListener = function (listener, rtcIdentity, contextId) {
1119     var index = 0;
1120     if (!listener && !contextId) {
1121         while (this.listeners[1].indexOf(rtcIdentity, index) != -1) {
1122             index = this.listeners[1].indexOf(rtcIdentity, index);
1123             this.listeners[0].splice(index, 1);
1124             this.listeners[1].splice(index, 1);
1125             this.listeners[2].splice(index, 1);
1126         }
1127     } else {
1128 
1129 
1130         while (this.listeners[0].indexOf(listener, index) != -1) {
1131             index = this.listeners[0].indexOf(listener, index);
1132             if (this.listeners[2][index] == contextId && this.listeners[1][index] == rtcIdentity) {
1133                 this.listeners[0].splice(index, 1);
1134                 this.listeners[1].splice(index, 1);
1135                 this.listeners[2].splice(index, 1);
1136                 break; // Because in addListener already checks that there is only one.
1137             }
1138         }
1139     }
1140 };
1141 
1142 /**
1143  * Creates the connection, connects to the server and establish the callback to the listeners on new message. 
1144  * @param {URI} ownRtcIdentity - URI with the own RTCIdentity used to connect to the Messaging Server. 
1145  * @param {Object} credentials - Credentials to connect to the server.
1146  * @param {callback} callbackFunction - Callback to execute when the connection is done.
1147  */
1148 MessagingStub.prototype.connect = function(ownRtcIdentity, credentials, callbackFunction, errorCallbackFunction) {
1149 	if (this.impl) {
1150 		this.impl.connect(ownRtcIdentity, credentials, callbackFunction, errorCallbackFunction);
1151 	}
1152 	else {
1153 		console.log(this.message);
1154 	}
1155 };
1156 
1157 
1158 /**
1159  * disconnect - Disconnects from the server.
1160  */
1161 MessagingStub.prototype.disconnect = function() {
1162 	if (this.impl) {
1163 		this.impl.disconnect();
1164 	}
1165 	else {
1166 		console.log(this.message);
1167 	}
1168 };
1169 
1170 
1171 /**
1172  * @ignore
1173  * 
1174  * getListeners - Gets the list of listeners.
1175  * @returns [listener[], rtcIdentity[], contextID[]] Returns an 2-D array of listeners, rtcIdentities and contextIDs
1176  */
1177 MessagingStub.prototype.getListeners = function() {
1178 	return this.listeners;
1179 };
1180 
1181 /**
1182 * @ignore
1183 */
1184 MessagingStub.prototype.sendOtherMessages = function(message){
1185 
1186 	var filtered_listeners = [];
1187 	var idx = this.listeners[1].indexOf(message.from.rtcIdentity);
1188 	while (idx != -1) {
1189 		if (this.listeners[2][idx] == message.contextId)
1190 			filtered_listeners.push(this.listeners[0][idx]);
1191 		idx = this.listeners.indexOf(message.from.rtcIdentity, idx + 1);
1192 	}
1193 	filtered_listeners.every(function(element, index, array) {
1194 		element(message);
1195 	});
1196 	if (filtered_listeners.length == 0) {
1197 		this.buffer.push(message);
1198 	}
1199 }
1200 
1201 /**
1202 * @ignore
1203 */
1204 MessagingStub.prototype.deliverMessage = function(message) {
1205 	console.log("S->C: ", message);
1206 	// Filter the listeners to redirect the message
1207 	var that = this;
1208 	
1209 	if (message.type == MessageType.INVITATION || !message.contextId)
1210 	{
1211 		if(this.listeners[0].length == 1){
1212 			console.log("Registered an Handler: ", message);
1213 			var filtered_listeners = [];
1214 			var idx = this.listeners[2].indexOf(undefined);
1215 			while (idx != -1) {
1216 				filtered_listeners.push(this.listeners[0][idx]);
1217 				idx = this.listeners.indexOf("", idx + 1);
1218 			}
1219 			filtered_listeners.every(function(element, index, array) {
1220 				element(message);
1221 			});
1222 
1223 		}else{
1224 			this.sendOtherMessages(message);
1225 		}
1226 		//regist an handler with contextId
1227 	}
1228 	else
1229 	{
1230 		this.sendOtherMessages(message);
1231 	}
1232 					
1233 };
1234 
1235 // -------------------------------
1236 // Codec.js
1237 // -------------------------------
1238 
1239 /**
1240  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1241  * @author <a href="mailto:paulo-g-chainho@ptinovacao.pt">Paulo Chainho</a>
1242  * @author <a href="mailto:Steffen.Druesedow@telekom.de">Steffen Druesedow</a>
1243  * @author <a href="mailto:Miguel.Seijo@telekom.de">Miguel Seijo</a>
1244  * @author <a href="mailto:vasco-m-amaral@ptinovacao.pt">Vasco Amaral</a>
1245  * @author <a href="mailto:Kay.Haensge@telekom.de">Kay Haensge</a>
1246  * @author <a href="mailto:luis-f-oliveira@ptinovacao.pt">Luis Oliveira</a>
1247  */
1248 
1249 
1250 /**
1251  * @class
1252  * The Codec is used by Resources that are shared on top of the Data Channel, 
1253  * like file sharing and Textual Chat, to decode and code the data
1254  * in a consistent way by all the peers. The Data Codec may also be 
1255  * downloaded on the fly by the peers.
1256  * 
1257  */
1258 
1259 function Codec(type, CodecLibUrl, dataBroker){
1260     this.listeners = [];
1261     this.id = guid();
1262     this.type = type;
1263     this.description;
1264     this.CodecLibUrl = CodecLibUrl;
1265     this.mime_type;
1266     this.dataBroker = dataBroker;
1267     this.chunkLength = 1000;
1268     this.arrayToStoreChunks = [];
1269 }
1270 
1271 
1272 /**
1273  * Sends a Message from channel in the DataBroker.
1274  * 
1275  * @param {String} - String with the content of a message, is a simple string even for chat and file.
1276  *
1277  */
1278 
1279 Codec.prototype.send = function( input ){
1280     var aux = JSON.parse(input);
1281     //How to change the mimetype of input.body is equal to this.mimetype 
1282     if(!this.dataBroker)
1283         return;
1284     if(this.type=="chat"){
1285         this.dataBroker.send(input);
1286     }else{ // case file Sharing...
1287         var reader = new window.FileReader();
1288 
1289         // get file from system
1290         var fileElement = document.getElementById(aux.body);
1291         var file = fileElement.files[0];
1292         var thatCodec = this;
1293         var readFile = function(event,text){
1294             var data = {}; // data object to transmit over data channel
1295 
1296             if (event) text = event.target.result; // on first invocation
1297 
1298             if (text.length > 1000) {
1299                 data.message = text.slice(0, 1000); // getting chunk using predefined chunk length
1300             } else {
1301                 data.message = text;
1302                 data.last = true;
1303             }
1304             aux.body = data;
1305             thatCodec.dataBroker.send(JSON.stringify(aux));
1306 
1307             var remainingDataURL = text.slice(data.message.length);
1308             if (remainingDataURL.length) setTimeout(function () {
1309                 readFile(null, remainingDataURL); // continue transmitting
1310             }, 500)
1311         };
1312         reader.readAsDataURL(file);
1313         reader.onload =readFile;
1314     }
1315 
1316 }
1317 
1318 /**
1319  * getReport function.
1320  * @param.. reportHandler.
1321  */
1322 
1323 Codec.prototype.getReport = function( reportHandler ){
1324 
1325 
1326 }
1327 
1328 
1329 /**
1330  * Receives a Message from channel that is designated to this codec to present it in the application.
1331  * 
1332  * @param {JsonObject} - JsonObject with the content of a message, is a DataMessage Type.
1333  *
1334  */
1335 
1336 Codec.prototype.onData = function( dataMsg ){
1337 
1338     //take data and treat it
1339     console.log(this.listeners);
1340     if(this.type=="chat"){
1341         this.listeners.every(function(element, index, array){
1342             element('ondatamessage',dataMsg);
1343         });
1344     }else{
1345         //var data = JSON.parse(dataMsg.body.message);
1346         this.arrayToStoreChunks.push(dataMsg.body.message);
1347         if (dataMsg.body.last) {
1348             this.saveToDisk(this.arrayToStoreChunks.join(''), 'fileName');
1349             this.arrayToStoreChunks = []; // resetting array
1350         }
1351     }
1352 }
1353 
1354 
1355 /**
1356  * Adds a Listener to the codec that will handle all the messages to the application.
1357  * 
1358  * @param {Listener} - A Listener function that should be defined in the application.
1359  *
1360  */
1361 
1362 Codec.prototype.addListener = function( listener ){
1363 
1364     this.listeners.push(listener);
1365 
1366 }
1367 
1368 /**
1369  * Removes a Listener to the codec that will handle all the messages to the application.
1370  * 
1371  * @param {Listener} - The listener that should be removed.
1372  *
1373  */
1374 
1375 Codec.prototype.removeListener = function( listener ){
1376 
1377     var index = 0;
1378     if(this.listeners.indexOf(listener, index) !== -1){
1379         index = this.listeners.indexOf(listener, index);
1380         this.listeners.splice(index, 1);
1381     }
1382 
1383 
1384 }
1385 
1386 
1387 /**
1388  * Adds a DataBroker to the codec.
1389  * 
1390  * @param {dataBroker} - A DataBroker object. 
1391  *
1392  */
1393 
1394 Codec.prototype.setDataBroker = function( dataBroker ){
1395 
1396     this.dataBroker = dataBroker;
1397 
1398 }
1399 
1400 /**
1401  * Store the received files in the harddisk.
1402  * 
1403  * @param {fileUrl} - File URL where the applicationr receives the information about the file.
1404  * @param {fileName} - String which should contain the name to the file to store in the disk.
1405  *
1406  */
1407 
1408 Codec.prototype.saveToDisk= function(fileUrl, fileName) {
1409     var save = document.createElement('a');
1410     save.href = fileUrl;
1411     save.target = '_blank';
1412     save.download = fileName || fileUrl;
1413     var evt = document.createEvent('MouseEvents');
1414     evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
1415 
1416     save.dispatchEvent(evt);
1417 
1418     (window.URL || window.webkitURL).revokeObjectURL(save.href);
1419 }
1420 // -------------------------------
1421 // DataCodec.js
1422 // -------------------------------
1423 
1424 /**
1425  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1426  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1427  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1428  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1429  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1430  * @author Kay Haensge <Kay.Haensge@telekom.de>
1431  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1432  */
1433 
1434 /**
1435  * Class DataMessage
1436  * creates all the json associated to the codecs messages 
1437  * @class
1438  */
1439 
1440 function DataMessage (codecId, to, body){
1441 
1442 
1443 	this.codecId = codecId;
1444 	this.to = to; //in case empty it sends the message to all clients
1445 	this.body = body;
1446 }
1447 // -------------------------------
1448 // DataMessage.js
1449 // -------------------------------
1450 
1451 /**
1452  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1453  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1454  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1455  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1456  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1457  * @author Kay Haensge <Kay.Haensge@telekom.de>
1458  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1459  */
1460 
1461 /**
1462  * Class DataMessage
1463  * creates all the json associated to the codecs messages 
1464  * @class
1465  */
1466 
1467 function DataMessage (codecId, to, from, body){
1468 
1469 
1470 	this.codecId = codecId;
1471 	this.to = to; //in case empty it sends the message to all clients
1472 	this.body = body;
1473 	this.from = from;
1474 }
1475 // -------------------------------
1476 // DataBroker.js
1477 // -------------------------------
1478 
1479 /**
1480  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1481  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1482  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1483  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1484  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1485  * @author Kay Haensge <Kay.Haensge@telekom.de>
1486  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1487  */
1488 
1489 /**
1490  * @class
1491  * The DataBroker Class handles all the operations to choose the right codecs
1492  * and the channels to send, receive and handle messages related to the Data.
1493  * 
1494  */
1495 function DataBroker(){
1496 
1497 	this.codecs = [];
1498 	this.channels = [];
1499 }
1500 
1501 /**
1502  * Receives a Message from channel that is designated to this codec to present it in the application.
1503  * 
1504  * @param {JsonObject} - JsonObject with the content of a message, is a DataMessage Type.
1505  *
1506  */
1507 DataBroker.prototype.onDataChannelEvt = function( msg ){
1508 
1509 	var that = this;
1510 	console.log(msg);
1511 
1512 	for(var i = 0; i < this.channels.length; i++){
1513 		this.channels[i].channel.onmessage = function(msg){
1514 			var msgObject = JSON.parse(msg.data);
1515 			console.log("MESSAGE: ", msg);
1516 			for(var i = 0; i < that.codecs.length; i++){
1517 				console.log("that.codecs[i].id: ", that.codecs[i].id);
1518 				console.log("msgObject.codecId: ", msgObject.codecId);
1519 				if( that.codecs[i].id == msgObject.codecId ){
1520 					console.log("that.codecs[i], ", that.codecs[i]);
1521 					that.codecs[i].onData(msgObject);
1522 					break;
1523 				}
1524 					
1525 			}
1526 		}	
1527 	}
1528 	
1529 }
1530 
1531 /**
1532  * Adds a Codec to the DataBroker.
1533  * 
1534  * @param {Codec} - A Codec object. 
1535  *
1536  */
1537 
1538 DataBroker.prototype.addCodec = function(codec){
1539     codec.dataBroker=this;
1540 	this.codecs.push(codec);
1541 
1542 }
1543 
1544 /**
1545  * Removes a Codec from the DataBroker.
1546  * 
1547  * @param {Codec} - A Codec object. 
1548  *
1549  */
1550 DataBroker.prototype.removeCodec = function(codec){
1551 
1552 
1553 	//removecodec
1554 
1555 }
1556 
1557 /**
1558  * Adds a DataChannel to the Codec with the respective identity
1559  * 
1560  * @param {DataChannel} - A DataChannel object. 
1561  * @param {Identity} - An Identity object. 
1562  *
1563  */
1564 
1565 DataBroker.prototype.addDataChannel = function(dataChannel, identity){
1566 
1567 	var channel = {
1568 		"identity": identity,
1569 		"channel": dataChannel
1570 	};
1571 	this.channels.push(channel);
1572 
1573 }
1574 
1575 /**
1576  * Removes DataChannel from the codec with the respective identity
1577  * 
1578  * @param {Identity} - An Identity object. 
1579  *
1580  */
1581 
1582 DataBroker.prototype.removeDataChannel = function(identity){
1583     this.channels.forEach(function(element, index, array){
1584         if(element.identity==identity) array.splice(index,1);
1585     });
1586 
1587 }
1588 
1589 /**
1590  * Sends a Message from channel in the DataBroker.
1591  * 
1592  * @param {String} - String with the content of a message
1593  *
1594  */
1595 
1596 DataBroker.prototype.send = function( msg ){
1597 	
1598 	console.log("MENSAGEM: ", msg);
1599 	var index = -1;
1600 	var msgObject = JSON.parse(msg);
1601 	
1602 	if( msgObject.to == "" || typeof msgObject.to === 'undefined' ){
1603 		for(var i = 0; i < this.channels.length; i++){
1604 			console.log("channels");
1605 			this.channels[i].channel.send(msg);
1606 		}
1607 			
1608 	}
1609 	else {
1610 		for(var i = 0; i < this.channels.length; i++){
1611 			if( this.channels[i].identity.rtcIdentity === msgObject.to )
1612 				index = i;
1613 		}
1614 		if(index !== -1)
1615 			this.channels[index].channel.send(msg);
1616 	}
1617 
1618 	console.log(this.channels);
1619 	console.log(this.codecs);
1620 
1621 }// wonder_conversation.js
1622 //-----------------------
1623 
1624 
1625 // -------------------------------
1626 // Resource.js
1627 // -------------------------------
1628 
1629 /**
1630  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1631  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1632  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1633  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1634  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1635  * @author Kay Haensge <Kay.Haensge@telekom.de>
1636  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1637  */
1638 
1639 
1640 /**
1641  * @class
1642  * The Resource class represents the digital assets that are shared among participants in the conversation.
1643  * 
1644  * @param {ResourceConstraint} resourceConstraint - Constraints of the Resource. Object with the following syntax {type: ResourceType, constraints: codec or MediaStreamConstraints}
1645  * @param {Codec} [codec] - For data types only, Codec used.
1646  *
1647  */
1648 
1649 function Resource( resourceConstraint, codec ) {
1650 
1651   this.constraint = resourceConstraint;      
1652   this.connections = new Array();
1653   this.owner;
1654     
1655   if(codec) 
1656   {
1657       this.codec = codec;
1658       this.constraint.constraints=codec;
1659   }
1660 }
1661 
1662 
1663 /**
1664  * Resource class
1665  */
1666 /*function Resource() {
1667 	this.id;                        // We add a resource ID to identify it.
1668 	this.type;                      // type of resource.
1669     this.stream;                    // StreamTrack (http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediastreamtrack) for this communication.
1670     this.data;                      // dataChannel for communication.
1671     this.evtHandler;                // eventHandler.
1672     this.status;                    // status this resource.
1673     this.owner;                     // owner this resource/stream.
1674     //this.codecs;                  <- still no discuss
1675     var thisresource = this;
1676     // Question: how to handle the creation of PeerConnections and add local MediaStreams? do we need them stored at each Resource having Tracks from this stream??
1677     // TODO: add stream attribute and rename "stream" to "streamTrack"
1678  	
1679     /**
1680      * private method setStream
1681      *
1682      * @param stream..
1683      */
1684  /*   setStream = function(stream){
1685         thisresource.stream = stream;
1686     }
1687 
1688     /**
1689      * private method setData
1690      *
1691      * @param data
1692      */
1693 /*    setData = function(data){
1694       thisresource.data = data;
1695     };
1696 
1697     /**
1698      * private method setStatus
1699      *
1700      * @param status ... sets the status attribute for a resource
1701      */
1702 /*    this.setStatus = function(status){
1703       // TODO: ensure the transition in the state machine is allowed otherwise callback error
1704       switch(thisresource.status){
1705           case ResourceStatus.RECORDING:
1706               if(status != ResourceStatus.NOT_SHARED || status != ResourceStatus.ENDED || status != ResourceStatus.PLAYING){
1707                   console.log("error setStatus" + status);
1708                   break;
1709               }
1710               thisresource.status = status;
1711               break;
1712           case ResourceStatus.NOT_SHARED:
1713               if(status != ResourceStatus.SHARED || status != ResourceStatus.PLAYING || status != ResourceStatus.ENDED){
1714                   console.log("error setStatus" + status);
1715                   break;
1716               }
1717               thisresource.status = status;
1718               break;
1719           case ResourceStatus.PAUSED:
1720               if(status != ResourceStatus.STOPPED){
1721                   console.log("error setStatus" + status);
1722                   break;
1723               }
1724               thisresource.status = status
1725               break
1726           case ResourceStatus.PLAYING:
1727               if(status != ResourceStatus.PAUSED || status != ResourceStatus.STOPPED || status != ResourceStatus.SHARED || status != ResourceStatus.NOT_SHARED){
1728                   console.log("error setStatus" + status);
1729                   break;
1730               }
1731               thisresource.status = status;
1732               break;
1733           case ResourceStatus.SHARED:
1734               if(status != ResourceStatus.NOT_SHARED || status != ResourceStatus.RECORDING || status != ResourceStatus.PLAYING || status != ResourceStatus.ENDED){
1735                   console.log("error setStatus" + status);
1736                   break;
1737               }
1738               thisresource.status = status;
1739               break;
1740           case ResourceStatus.STOPPED:
1741               console.log("transition is not allowed");
1742               break;
1743           case ResourceStatus.ENDED:
1744               if(status != ResourceStatus.PLAYING){
1745                   console.log("error setStatus" + status);
1746                   break;
1747               }
1748               thisresource.status = status;
1749               break;
1750           case ResourceStatus.LIVE:
1751               if(status != ResourceStatus.SHARED || status != ResourceStatus.NOT_SHARED){
1752                   console.log("error setStatus" + status);
1753                   break;
1754               }
1755               thisresource.status = status;
1756               break;
1757           case ResourceStatus.NEW:
1758               if(status != ResourceStatus.LIVE){
1759                   console.log("error setStatus" + status);
1760                   break;
1761               }
1762               thisresource.status = status;
1763               break;
1764           default:
1765             thisresource.status = status;
1766             break;
1767       }
1768        
1769     }
1770 };
1771 
1772 /**
1773  * createStream
1774  * 
1775  * @param owner  : Participant ... owner of the stream
1776  * @param stream : MediaStream ... the stream
1777  * @param type   : ResourceType... type of resource
1778  */
1779 /*Resource.prototype.createStream = function(owner,stream,type) {
1780     if(! stream || ! owner) return ;
1781     else{
1782         this.owner=owner;
1783         this.stream=stream;
1784         this.type = type;
1785         this.id = guid();
1786         this.setStatus(ResourceStatus.NEW);
1787     }
1788 
1789     // TODO  Where/how to assign the type,data,connection,evtHandler and status?
1790 
1791 };
1792 
1793 
1794 /**
1795  * getStatus
1796  * 
1797  * @returns ResourceStatus ... returns the resource status
1798  */
1799 /*Resource.prototype.getStatus = function(){
1800     return this.status;
1801 };
1802 
1803 
1804 /**
1805  * destroy
1806  * 
1807  */
1808 /*Resource.prototype.stop = function(){
1809 // @pchainho TODO: only applicable for local Resources. 
1810 
1811     this.stream = null;
1812     //this.setStatus(ResourceStatus.ENDED); // @pchainho I guess this should only be done when "ended" event is fired. To study how to address Data Channel resources
1813 	
1814 	// TODO: depending on the type of the Resource it may imply the invocation of browser APIs eg for MediaStreamTracks call its operation stop()
1815 };
1816 
1817 /**
1818  * share
1819  * 
1820  * @param shared : Boolean ... establishes if the resource is shared or not
1821  * 
1822  */
1823 /*Resource.prototype.share = function(shared){
1824 // @pchainho TODO: only applicable for local Resources. For MediaStreamTracks call its operation stop()
1825 
1826     if(shared) this.setStatus( ResourceStatus.SHARED );
1827     else this.setStatus( ResourceStatus.NOT_SHARED);
1828 	// TODO: depending on the type of the Resource it may imply the invocation of browser APIs
1829 };
1830 
1831 /**
1832  * record
1833  * 
1834  */
1835 /*Resource.prototype.record = function(){
1836     // TODO Complete the function
1837 };
1838 
1839 /**
1840  * play
1841  * 
1842  * @param timing : Number ... the time to start playing
1843  * 
1844  */
1845 /*Resource.prototype.play = function(timing){
1846     // this.status = ResourceStatus.PLAYING;
1847     // TODO What to do with the timing ? How to get it playing ?
1848 };
1849 
1850 /**
1851  * pause
1852  * 
1853  */
1854 /*Resource.prototype.pause = function(){
1855     // this.status = ResourceStatus.PAUSED;
1856 };
1857 
1858 /**
1859  * create
1860  * 
1861  * @param owner : Participant ... specifies the owner of the resource
1862  * @param type  : ResourceType ... specifies the resource type
1863  * 
1864  */
1865 /*Resource.prototype.create = function(owner, type){
1866     if(! owner || ! type)return ;
1867     else{
1868         this.owner=owner;
1869         this.type=type;
1870         this.id=guid();                  // unique uid for resource
1871         this.setStatus(ResourceStatus.NEW);
1872     }
1873     // TODO  Where/how to assign the stream,data,connection,evtHandler and status?
1874 };  
1875 
1876 /**
1877  * createData
1878  * 
1879  * @param owner : Participant ... specifies the owner of the resource
1880  * @param type  : ResourceType ... specifies the resource type
1881  * @param data  : DataChannel ... specifies the data channel
1882  * 
1883  */
1884 /*Resource.prototype.createData = function(owner, type, data){
1885 
1886     if(! owner || ! type || ! data ) return ;
1887     else {
1888         this.owner=owner;
1889         this.type=type;
1890         this.data=data;
1891     }
1892     // TODO  Where/how to assign the stream,connection,evtHandler and status?
1893 };
1894 
1895 /**
1896  * EventHandler
1897  *
1898  * @param that
1899  */
1900 /*stream.started = function(self){
1901 
1902 }
1903 
1904 /**
1905  * EventHandler
1906  *
1907  * @param that
1908  *//*
1909 function mute(self){
1910 
1911 }
1912 
1913 /**
1914  * EventHandler
1915  *
1916  * @param that
1917  *//*
1918 function unmute(self){
1919 
1920 }
1921 
1922 /**
1923  * EventHandler
1924  *
1925  * @param that
1926  *//*
1927 function overconstrained(self){
1928 
1929 }
1930 
1931 /**
1932  * EventHandler
1933  *
1934  * @param that
1935  */
1936  /*
1937 function ended(that){
1938 
1939 }*/
1940 // -------------------------------
1941 // Participant.js
1942 // -------------------------------
1943 
1944 /**
1945  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
1946  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
1947  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
1948  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
1949  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
1950  * @author Kay Haensge <Kay.Haensge@telekom.de>
1951  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
1952  */
1953 
1954 /**
1955  * @class
1956  * The Participant class handles all operations needed to manage the participation of an 
1957  * Identity (User) in a conversation including the WebRTC PeerConnection functionalities. 
1958  * The Local Participant is associated with the Identity that is using the Browser while the 
1959  * Remote Participant is associated to remote Identities (users) involved in the conversation.
1960  * 
1961  */
1962 function Participant() {
1963     
1964     this.identity = "";                         // Identity of the participant
1965     this.RTCPeerConnection = "";                // RTCPeerConnection for that participant
1966     this.status = "";                           // Status
1967     this.me = "";                               // Participant representing the user of the browser. 
1968     this.rtcEvtHandler = "";                    // Event handler for WebRTC events
1969     this.msgHandler = "";                       // Event handler for signalling events
1970     this.contextId = "";                        // Context ID of the conversation the participant belongs to.
1971     this.resources = new Array();               // Resources of the participant
1972     this.hosting = "";                          // Hosting participant of the conversation this participant belongs to.
1973     this.connectedIdentities = new Array();     // For multiparty, array of Identities of the connected peers.
1974     this.dataBroker;                            // DataBroker for WebRTC DataChannel.
1975     
1976     /*********************************
1977      *        PRIVATE METHODS        *
1978      * TODO: try to have them accessible from other Wonder classes eg Conversation and MessagingSub but not visible from the App */
1979     /*********************************/
1980 
1981     /**
1982     * @ignore
1983     */
1984     var thisParticipant = this;
1985     setStatus = function(status) {
1986 
1987         //verify if all of the previous status allows to the participant to pass to the next status
1988         //if it is possible just setStatus, else return a message error.
1989         // (@ pchainho) check if the transition is allowed by the state machine.
1990 
1991         switch (thisParticipant.status) {
1992             case ParticipantStatus.CREATED:
1993                 if (status != ParticipantStatus.WAITING && status != ParticipantStatus.PENDING) {
1994                     return false;
1995                 }
1996                 else {
1997                     thisParticipant.status = status;
1998                     console.log(thisParticipant);
1999                     return true;
2000 
2001                 }
2002                 break;
2003             case ParticipantStatus.ACCEPTED:
2004                 if (status != ParticipantStatus.PARTICIPATING && status != ParticipantStatus.FAILED) {
2005                     return false;
2006                 }
2007                 else {
2008                     thisParticipant.status = status;
2009                     console.log(thisParticipant);
2010                     return true;
2011                 }
2012                 break;
2013             case ParticipantStatus.PARTICIPATING:
2014                 if (status != ParticipantStatus.PARTICIPATED && status != ParticipantStatus.NOT_PARTICIPATING) {
2015                     return false;
2016                 }
2017                 else {
2018                     thisParticipant.status = status;
2019                     console.log(thisParticipant);
2020                     return true;
2021                 }
2022                 break;
2023             case ParticipantStatus.PENDING:
2024                 if (status != ParticipantStatus.ACCEPTED && status != ParticipantStatus.MISSED) {
2025                     return false;
2026                 }
2027                 else {
2028                     thisParticipant.status = status;
2029                     console.log(thisParticipant);
2030                     return true;
2031                 }
2032                 break;
2033             case ParticipantStatus.FAILED:
2034                 console.log("transition is not allowed");
2035                 return false;
2036                 break;
2037             case ParticipantStatus.MISSED:
2038                 console.log("transition is not allowed");
2039                 return false;
2040                 break;
2041             case ParticipantStatus.PARTICIPATED:
2042                     thisParticipant.status = status;
2043                     console.log(thisParticipant);
2044                     return true;
2045                 break;
2046             case ParticipantStatus.WAITING:
2047                 if (status != ParticipantStatus.PARTICIPATING && status != ParticipantStatus.FAILED) {
2048                     return false;
2049                 }
2050                 else {
2051                     thisParticipant.status = status;
2052                     console.log(thisParticipant);
2053                     return true;
2054                 }
2055                 break;
2056             case ParticipantStatus.NOT_PARTICIPATING:
2057                     thisParticipant.status = status;
2058                     console.log(thisParticipant);
2059                     return true;
2060 
2061                 break;
2062             default:
2063                 thisParticipant.status = status;
2064                 console.log(thisParticipant);
2065                 return true;
2066                 break;
2067         }
2068     };
2069 }
2070 ;
2071 
2072 /**
2073  * Creates the local participant and initializes its resources.
2074  * 
2075  * @param {Identity} identity - {@link Identity} of the participant
2076  * @param {ResourceConstraints[]} resourceConstraints - Array of constraints for the initial resources of the local participant. (CURRENT IMPLEMENTATION WILL TAKE THE FIRST ONE)
2077  * @param {onRTCEvt} rtcEvtHandler - Callback function that handles WebRTC Events.
2078  * @param {onMessage} msgHandler - Callback function that handles signaling Events.
2079  * @param {callback} callback - Callback function for success creation of the local participant.
2080  * @param {errorCallback} errorCallback - Callback function for errors in the creation of the local participant.
2081  *
2082  */
2083 
2084 Participant.prototype.createMyself = function(identity, resourceConstraints, rtcEvtHandler, msgHandler, callback, errorCallback) {
2085 
2086     this.identity = identity;
2087     this.me = this;
2088     this.rtcEvtHandler = rtcEvtHandler;
2089     this.msgHandler = msgHandler;
2090 
2091     setStatus(ParticipantStatus.CREATED);   // @pchainho TODO: to catch errors
2092 
2093 
2094     var doGetUserMedia = false;
2095     var doDataChannel = false;
2096     var conversationResource = false;
2097     var constraints = new Object();
2098     constraints.audio = false;
2099     constraints.video = false;
2100 
2101 
2102     // Create RTCPeerConnection
2103     try {
2104         // Create an RTCPeerConnection via the polyfill (adapter.js).
2105         var pc = new RTCPeerConnection({'iceServers': new Array()});
2106         console.log('Created RTCPeerConnnection.');
2107     }
2108     catch (e) {
2109         console.log('Failed to create PeerConnection, exception: ' + e.message);
2110         alert('Cannot create RTCPeerConnection object; \
2111           WebRTC is not supported by this browser.');
2112         return;
2113     }
2114 
2115     this.setRTCPeerConnection(pc);
2116 
2117 
2118 
2119 
2120     // TODO: Solve the problem where for many resourceConstraints, we would have a loop with callbacks inside.
2121     // Process the constraints, ordering them by media ones and data ones (maybe in 2 arrays). Merge if necessary
2122     //resourceConstraints=resourceConstraints[0]; // <dirtyFix>
2123     var thatIdentity = this.identity;
2124     var thatResources = this.resources;
2125     var thatDataBroker = this.dataBroker;
2126     var that = this;
2127     var numbResource = 0;
2128     var flag = false;
2129     var dataBroker;
2130     var one = false;
2131     var getMedia = function(numbResource,recursiveCallback){
2132         if(resourceConstraints.length > numbResource){
2133 
2134             if (doGetUserMedia === true && resourceConstraints[numbResource].direction!="in" && resourceConstraints[numbResource].type == "audioVideo") {
2135                 var thisParticipant = that;
2136                 flag = true;
2137                 // TODO: Merge the media constraints so there is only 1 getUserMedia call (or maybe 2 if screensharing)
2138                 // Loop/cascade callback so all the media is added and resources created.
2139                 getUserMedia(constraints, function (stream) {
2140                     var evt=new Object();
2141                     evt.stream=stream;
2142                     thisParticipant.rtcEvtHandler('onaddlocalstream',evt);
2143                     console.log(numbResource);
2144 
2145                     pc.addStream(stream);
2146                     var resource = new Resource(resourceConstraints[numbResource -1]);
2147                     resource.id=stream.id;
2148                     //resource.constraint = resourceConstraints[numbResource];
2149                     console.log( resourceConstraints[numbResource]);
2150                     resource.constraint.constraints = {id: stream.id};
2151                     resource.owner = that.identity;
2152                     resource.connections.push(pc);
2153                     thisParticipant.resources.push(resource);
2154 
2155                     callback();
2156                 }, errorCallback);
2157                 // return;
2158             }
2159             if (doDataChannel === true && resourceConstraints[numbResource].direction!="in" &&  (resourceConstraints[numbResource].type != "audioVideo") ) {
2160                 // Loop so all the codecs are created and initialized.
2161                 var numb = numbResource;
2162                 var sucess = callback;
2163                 var creatResources = function(recursive,numb,sucess){
2164                     if(resourceConstraints.length>numb){
2165                         var codec = new Codec(resourceConstraints[numb].type);
2166                         //codec.id=resourceConstraints[numb].id;
2167                         console.log(codec);
2168                         if(resourceConstraints[numb].id) codec.id = resourceConstraints[numb].id;
2169                         var resource = new Resource(resourceConstraints[numb], codec);
2170                         console.log(resource);
2171                         resource.connections.push(pc);
2172                         resource.owner = thatIdentity;
2173                         thatResources.push(resource);
2174                         console.log(thatResources)
2175                         var evt = new Object();
2176                         evt.codec = codec;
2177                         that.onRTCEvt('onResourceParticipantAddedEvt', evt);
2178 
2179                         numb++;
2180                         recursive(recursive,numb,sucess);
2181                     }else{
2182                         // sucess()
2183                     }
2184                 }
2185                 creatResources(creatResources,numb,sucess);
2186 
2187             }
2188             numbResource++;
2189             recursiveCallback(numbResource,recursiveCallback);
2190         }else{
2191             if(flag != true ){
2192                 callback();
2193             }
2194         }
2195     }
2196     var iteration=0;
2197     var creatConstraints = function(iteration, callbackRecursive){
2198         if(resourceConstraints.length>iteration){
2199             switch (resourceConstraints[iteration].type) {
2200 
2201                 case ResourceType.AUDIO_MIC:
2202                     constraints.audio = true;
2203                     doGetUserMedia = true;
2204                     break;
2205                 case ResourceType.VIDEO_CAM:
2206                     constraints.video = true;
2207                     doGetUserMedia = true;
2208                     break;
2209                 case ResourceType.AUDIO_VIDEO:
2210                     constraints.video = true;
2211                     constraints.audio = true;
2212                     doGetUserMedia = true;
2213                     break;
2214                 case ResourceType.SCREEN:
2215                     constraints.video =  {
2216                         mandatory: {
2217                             chromeMediaSource: 'screen',
2218                             maxWidth: 1280,
2219                             maxHeight: 720
2220                         },
2221                         optional: []
2222                     };
2223                     constraints.audio = false;
2224                     doGetUserMedia = true;
2225                     break;
2226                 case ResourceType.FILE:
2227                     doDataChannel = true;
2228                     break;
2229                 case ResourceType.CHAT:
2230                     doDataChannel = true;
2231                     break;
2232             }
2233             iteration++;
2234             callbackRecursive(iteration,callbackRecursive);
2235         }else{
2236             getMedia(numbResource,getMedia);
2237         }
2238 
2239     }
2240 
2241     creatConstraints(iteration,creatConstraints);
2242 };
2243 
2244 
2245 
2246 
2247 /**
2248  * Creates a remote participant
2249  * 
2250  * @param {Identity} identity - {@link Identity} of the participant
2251  * @param {Participant} myParticipant - {@link Participant} representing the local user of the application. 
2252  * @param {string} contextId - Identifier of the conversation this participant belongs to. 
2253  * @param {ResourceConstraints[]} resourceConstraints - Array of constraints for the initial resources of the remote participant (CURRENT IMPLEMENTATION WILL TAKE THE FIRST ONE).
2254  * @param {onRTCEvt} rtcEvtHandler - Callback function that handles WebRTC Events.
2255  * @param {onMessage} msgHandler - Callback function that handles signaling Events.
2256  * @param {RTCIceServer} iceServers - Configuration parameters for ICE servers. {@link http://www.w3.org/TR/webrtc/#widl-RTCConfiguration-iceServers}
2257  *
2258  */
2259 
2260 Participant.prototype.createRemotePeer = function(identity, myParticipant, contextId, resourceConstraints, rtcEvtHandler, msgHandler, iceServers) {
2261     // # if we follow the state diagram, it will be needed to set the status to created 
2262     setStatus(ParticipantStatus.CREATED);
2263     this.identity = identity;
2264     this.me = myParticipant;
2265     this.rtcEvtHandler = rtcEvtHandler;
2266     this.msgHandler = msgHandler;
2267     this.contextId = contextId;
2268     var channel;
2269     var thisParticipant = this;
2270     var oneDataChannel = false;
2271     constraints = resourceConstraints;
2272     var ite = 0;
2273     var that = this;
2274     //media and Data
2275     var getMedia = function(ite,functionCallback,pc,oneDataChannel){
2276         if(constraints.length > ite){
2277             var type;
2278             if(that.me.getResources(constraints[ite])[0] == undefined){
2279                 type == null;
2280             }
2281             else{
2282                 type = that.me.getResources(constraints[ite])[0].constraint.type;
2283             }
2284             // create data channel and setup chat
2285             if(data && constraints[ite].direction!="in" && (type == "chat" || type == "file" )){
2286                 console.log("Creating remote peer with local constraints: ",  constraints);
2287 
2288                 if(oneDataChannel == false){
2289                     oneDataChannel = true;
2290                     channel = pc.createDataChannel("dataChannel");
2291 
2292                     //thisParticipant.setDataBroker(constraints.constraints.dataBroker);
2293                     thisParticipant.dataBroker.addDataChannel(channel, thisParticipant.identity);
2294                     channel.onopen = function () {
2295                         thisParticipant.dataBroker.onDataChannelEvt();
2296                         //thisParticipant.onRTCEvt("onResourceParticipantAddedEvt", resourceData);
2297                     }
2298 
2299                     // setup chat on incoming data channel
2300                     pc.ondatachannel = function (evt) {
2301                         channel = evt.channel;
2302                     };
2303                     var resourceData;
2304                 }
2305                 resourceData = that.me.getResources(constraints[ite])[0];
2306                 resourceData.connections.push(pc);
2307             }
2308 
2309             if(media && constraints[ite].direction!="in" && (type =="audioVideo" )){
2310                 var resourceMedia = that.me.getResources(constraints[ite])[0];
2311                 var stream = that.me.RTCPeerConnection.getStreamById(resourceMedia.constraint.constraints.id);
2312                 pc.addStream(stream);
2313                 resourceMedia.connections.push(pc);
2314             }
2315 
2316             if (constraints[ite].direction != "out") {
2317                 if (media === true) {
2318                     var resource = new Resource(resourceConstraints);
2319                     resource.owner = that.identity;
2320                     resource.connections.push(pc);
2321                     thisParticipant.resources.push(resource);
2322                 }
2323                 if (data === true) {
2324                     var resource = new Resource(resourceConstraints);
2325                     //var codec = new Codec(resourceConstraints.type);
2326                     //resource.codec = codec;
2327                     resource.owner = that.identity;
2328                     resource.connections.push(pc);
2329                     thisParticipant.resources.push(resource);
2330                 }
2331             }
2332             ite++;
2333             that.setRTCPeerConnection(pc);
2334             functionCallback(ite,functionCallback,pc,oneDataChannel);
2335         }else{
2336             // Create RTCPeerConnection
2337 
2338         }
2339     }
2340 
2341 
2342     // end Media and Data
2343     var media = false;
2344     var data = false;
2345     var iteration = 0;
2346     var creatResource = function(iteration,callbackFunction){
2347         if(constraints.length > iteration){
2348             switch (constraints[iteration].type) {
2349                 case ResourceType.AUDIO_MIC:
2350                     media = true;
2351                     break;
2352                 case ResourceType.VIDEO_CAM:
2353                     media = true;
2354                     break;
2355                 case ResourceType.AUDIO_VIDEO:
2356                     media = true;
2357                     break;
2358                 case ResourceType.SCREEN:
2359                     media = true;
2360                     break;
2361                 case ResourceType.FILE:
2362                     data = true;
2363                     break;
2364                 case ResourceType.CHAT:
2365                     data = true;
2366                     break;
2367             }
2368             iteration++;
2369             callbackFunction(iteration,callbackFunction);
2370         }else{
2371             try {
2372                 // Create an RTCPeerConnection via the polyfill (adapter.js).
2373                 var mediaConstraints = {optional: [{RtpDataChannels: true}]};
2374                 if(!iceServers) iceServers = {'iceServers': new Array()};
2375                 console.log('Creating RTCPeerConnnection with:\n' + '  config: \'' + JSON.stringify(iceServers) + '\';\n' + '  constraints: \'' + JSON.stringify(mediaConstraints) + '\'.');
2376 
2377                 var pc = new RTCPeerConnection(iceServers, mediaConstraints);
2378 
2379 
2380             }
2381             catch (e) {
2382                 console.log('Failed to create PeerConnection, exception: ' + e.message);
2383                 alert('Cannot create RTCPeerConnection object; \
2384           WebRTC is not supported by this browser.');
2385                 return;
2386             }
2387             getMedia(ite,getMedia,pc,oneDataChannel);
2388         }
2389     }
2390     creatResource(iteration,creatResource);
2391 }
2392 
2393 
2394 
2395 
2396 
2397 
2398 /**@ignore
2399  * setRTCPeerConnection
2400  * 
2401  * @param RTCPeerConnection : PeerConnection ... sets the connection attribute for a participant
2402  */
2403 Participant.prototype.setRTCPeerConnection = function(RTCPeerConnection) {
2404     var thisParticipant = this;
2405     this.RTCPeerConnection = RTCPeerConnection;
2406 
2407     /**
2408      * onsignalingstatechange
2409      *
2410      * It is called any time the readyState changes
2411      */
2412     this.RTCPeerConnection.onsignalingstatechange = function(evt) {
2413         thisParticipant.onRTCEvt("onsignalingstatechange", evt);
2414     };
2415 
2416     /**
2417      * oniceconnectionstatechange
2418      *
2419      *  It is called any time the iceConnectionState changes.
2420      */
2421     this.RTCPeerConnection.oniceconnectionstatechange = function(evt) {
2422         thisParticipant.onRTCEvt("oniceconnectionstatechange", evt);
2423     };
2424 
2425     /**
2426      * onaddstream
2427      *
2428      *   It is called any time a MediaStream is added by the remote peer
2429      */
2430 
2431     this.RTCPeerConnection.onaddstream = function(evt) {
2432         thisParticipant.onRTCEvt("onaddstream", evt);
2433     };
2434 
2435     /**
2436      * onicecandidate 
2437      *
2438      */
2439 
2440     this.RTCPeerConnection.onicecandidate = function(evt) {
2441         thisParticipant.onRTCEvt("onicecandidate", evt);
2442     };
2443 
2444     /**
2445      * onnegotiationneeded
2446      *
2447      */
2448 
2449     this.RTCPeerConnection.onnegotiationneeded = function(evt) {
2450         thisParticipant.onRTCEvt("onnegotiationneeded", evt);
2451     };
2452 
2453     /**
2454      * ondatachannel
2455      *
2456      */
2457 
2458     this.RTCPeerConnection.ondatachannel = function(evt) {
2459         thisParticipant.onRTCEvt("ondatachannel", evt);
2460     };
2461 
2462     /**
2463      * onremovestream
2464      *
2465      *  It is called any time a MediaStream is removed by the remote peer. 
2466      */
2467 
2468     this.RTCPeerConnection.onremovestream = function(evt) {
2469         thisParticipant.onRTCEvt("onremovestream", evt);
2470     };
2471 
2472 
2473 };
2474 
2475 
2476 /**
2477  * Returns a reference to the RTCPeerConnection that is established with this participant.
2478  * 
2479  * @returns PeerConnection ... gets the connection attribute for a participant
2480  * 
2481  */
2482 Participant.prototype.getRTCPeerConnection = function() {
2483     return this.RTCPeerConnection;
2484 };
2485 
2486 
2487 
2488 /**
2489  * This callback type is called `onRTCEvt` and handles the WebRTC events from the RTCPeerConnection.
2490  *
2491  * @callback onRTCEvt
2492  * @param {event} event - Event from {@link http://www.w3.org/TR/webrtc/#event-summary} + 'onResourceParticipantAddedEvt' + 'onaddlocalstream'.
2493  * @param {evt} evt - Returned stream, candidate, etc. see {@link http://www.w3.org/TR/webrtc/#event-summary}
2494  */
2495 Participant.prototype.onRTCEvt = function(event, evt) {
2496     // TODO To implement and pass the events up
2497     switch (event) {
2498 
2499         case 'onnegotiationneeded':
2500             this.rtcEvtHandler(event, evt);
2501             break;
2502         case 'onicecandidate':
2503             if (evt.candidate) {
2504                 var message = new MessageFactory.createCandidateMessage("","","",evt.candidate.sdpMLineIndex,evt.candidate.sdpMid,evt.candidate.candidate,false);
2505             } else {
2506                 var message = new MessageFactory.createCandidateMessage("","","","","",this.RTCPeerConnection.localDescription,true);
2507                 console.log("End of Ice Candidates");
2508             }
2509             this.sendMessage(message.body,MessageType.CONNECTIVITY_CANDIDATE,"",function(){},function(){});
2510             this.rtcEvtHandler(event, evt);
2511             break;
2512         case 'onsignalingstatechange':
2513             this.rtcEvtHandler(event, evt);
2514             break;
2515         case 'onaddstream':
2516             console.log("stream added");
2517             this.rtcEvtHandler(event, evt);
2518             break;
2519         case 'onremovestream':
2520             this.rtcEvtHandler(event, evt);
2521             break;
2522         case 'oniceconnectionstatechange':
2523             this.rtcEvtHandler(event, evt);
2524             break;
2525         case 'ondatachannel':
2526             this.rtcEvtHandler(event, evt);
2527             break;
2528         default:
2529             this.rtcEvtHandler(event, evt);
2530             break;
2531 
2532     }
2533 }
2534 
2535 /**
2536  * This callback type is called `onRTCEvt` and handles the WebRTC events from the RTCPeerConnection.
2537  *
2538  * @callback onMessage
2539  * @param {Message} message - {@link Message} received
2540  */
2541 Participant.prototype.onMessage = function(message) {
2542 
2543     switch (message.type) {
2544 
2545         case MessageType.ACCEPTED:
2546             var mediaConstraints = message.body.constraints;
2547             var that = this;
2548             var exist = false;
2549             if(typeof message.body.connectionDescription !== 'undefined' && message.body.connectionDescription !== ""){
2550 
2551                 console.log("Participant " + this.identity.rtcIdentity + " received accepted.");
2552             var description = new RTCSessionDescription(message.body.connectionDescription);
2553             this.RTCPeerConnection.setRemoteDescription(description,
2554                     onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
2555                 console.log("Remote Description set: ", this);  
2556                 this.getResources(mediaConstraints)[0].constraint=mediaConstraints;
2557 
2558                 if(this.me.identity.rtcIdentity == this.hosting.rtcIdentity){
2559                     //see if the hosting is equal to this.me
2560                     //send a accepted message with no SDP
2561                     this.me.connectedIdentities.push(message.from.rtcIdentity);
2562                     var answerBody = new Object();
2563                     answerBody.connected = this.me.connectedIdentities;
2564                     answerBody.from = message.from;
2565                     answerBody.to = "";
2566                     this.me.sendMessage(answerBody, MessageType.ACCEPTED, mediaConstraints);
2567                 }
2568             }else{
2569                 if(message.body.connected.length != 0){
2570                     for(var i = 0; i < message.body.connected.length; i++){
2571                         //ignore the message if my rtcIdentity is in the this.connectedIdentities
2572                         if(message.body.connected[i] == that.me.identity.rtcIdentity){
2573                             exist = true;
2574                             break;
2575                         }
2576                     }
2577                     if(!exist){
2578                         //if not send a message to the all of candidates
2579                         that.sendMessage("", MessageType.INVITATION, mediaConstraints);
2580                     }
2581                 }
2582             }          
2583             
2584             
2585             this.msgHandler(message);
2586             break;
2587         case MessageType.CONNECTIVITY_CANDIDATE:
2588             if (message.body.lastCandidate) {
2589                 console.log("Participant " + this.identity.rtcIdentity + " reached End of Candidates.");
2590             }
2591             else
2592             {
2593                 var candidate = new RTCIceCandidate({
2594                     sdpMLineIndex: message.body.label,
2595                     sdpMid: message.body.id,
2596                     candidate: message.body.candidateDescription
2597                 });
2598                 console.log("Participant " + this.identity.rtcIdentity + " added a candidate:", candidate);
2599                 this.RTCPeerConnection.addIceCandidate(candidate);
2600             }
2601             break;
2602         case MessageType.NOT_ACCEPTED:
2603             this.leave(false);
2604             console.log("Participant received NOT_ACCEPTED");
2605             this.msgHandler(message);
2606             break;
2607         case MessageType.CANCEL:
2608             this.msgHandler(message);
2609             break;
2610         case MessageType.ADD_RESOURCE:
2611             this.msgHandler(message);
2612             break;
2613         case MessageType.UPDATE:
2614             this.msgHandler(message);
2615             break;
2616         case MessageType.UPDATED:
2617              var description = new RTCSessionDescription(message.body.newConnectionDescription);
2618                 this.RTCPeerConnection.setRemoteDescription(description,
2619                     onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
2620                 console.log("Remote Description set: ", description);
2621                 this.getResources(mediaConstraints)[0].constraint=mediaConstraints;
2622                 this.msgHandler(message);
2623             break;
2624         case MessageType.REDIRECT:
2625             this.msgHandler(message);
2626             break;
2627         case MessageType.BYE:
2628             this.leave(false);
2629             console.log("Participant received BYE");
2630             this.msgHandler(message);
2631             break;
2632         case MessageType.OFFER_ROLE:
2633             this.msgHandler(message);
2634             break;
2635         case MessageType.INVITATION:
2636             // IF GETS HERE IT IS NORMAL FOR THE MULTIPARTY
2637             var mediaConstraints = message.body.constraints;
2638             var description = new RTCSessionDescription(message.body.connectionDescription);
2639             this.RTCPeerConnection.setRemoteDescription(description, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
2640             
2641             this.sendMessage(answerBody, MessageType.ACCEPTED, mediaConstraints);
2642             //this.msgHandler(message);
2643             break;
2644         case MessageType.RESOURCE_REMOVED:
2645             this.msgHandler(message);
2646             break;
2647         case MessageType.REMOVE_PARTICIPANT:
2648             this.msgHandler(message);
2649             break;
2650         case MessageType.SHARE_RESOURCE:
2651             this.msgHandler(message);
2652             break;
2653         default:
2654             // forward to application level
2655             this.msgHandler(message);
2656             break;
2657     }
2658 }
2659 
2660 
2661 /** @ignore */
2662 Participant.prototype.connectStub = function(callback) {
2663     var thisParticipant = this;
2664     thisParticipant.identity.resolve(function(stub) {// @pchainho: why is this needed?
2665         stub.addListener(thisParticipant.onMessage.bind(thisParticipant), thisParticipant.identity.rtcIdentity, thisParticipant.contextId);
2666         stub.connect(thisParticipant.me.identity.rtcIdentity,thisParticipant.me.identity.credentials,callback);// @pchainho: we are using here the credentials of other users??
2667     });
2668 }
2669 
2670 
2671 /**
2672  *
2673  * The method will create the message and send it to the participant. 
2674  * 
2675  * @param {MessageBody} messageBody - The body of the message (depends on the MessageType)
2676  * @param {MessageType} messageType - The type of the message.
2677  * @param {ResourceConstraints} [constraints] - For the messages that imply information about the Resources, contraints about them. 
2678  * @param {callback} callback - Callback for successful sending.
2679  * @param {errorCallback} errorCallback - Error Callback
2680  *
2681  */
2682 Participant.prototype.sendMessage = function(messageBody, messageType, constraints, callback, errorCallback) {
2683     // Sends the message
2684 
2685 
2686     sdpConstraints = {'mandatory': {'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true}};
2687 
2688     var message = new Message();
2689     var thisParticipant = this;
2690     console.log(this.resources)
2691     console.log(constraints)
2692     var i;
2693     /* for(i = 0;i<constraints.length;i++){
2694      if(constraints[i].type==ResourceType.CHAT || constraints[i].type==ResourceType.FILE)
2695      {
2696      var codec = new Codec(constraints[i].type, constraints[i].CodecLibUrl);
2697      codec.id = constraints[i].id;
2698      codec.description = constraints[i].description;
2699      codec.mime_type = constraints[i].mime_type;
2700      constraints[i] = codec;
2701      }
2702      }*/
2703 
2704 
2705     switch(messageType){
2706         case MessageType.INVITATION:
2707             console.log("MessageType.INVITATION: ", messageBody);
2708             console.log("constrains",constraints)
2709             // define new type of constraints because when isn't possible to send the DataBroker in message...
2710             var constraintsAux =new Array();
2711             var i;
2712             var iteration;
2713 
2714             for(iteration=0;iteration<constraints.length;iteration++){
2715                 var aux = new Object()
2716                 if(constraints[iteration].constraints){
2717                     aux.id = constraints[iteration].constraints.id;
2718                 }else{
2719                     aux.id = constraints[iteration].id;
2720                 }
2721                 aux.type = constraints[iteration].type;
2722                 aux.direction =  constraints[iteration].direction;
2723                 constraintsAux.push(aux);
2724             }
2725 
2726             console.log("invite")
2727             if (!messageBody) message = MessageFactory.createInvitationMessage(this.me.identity, this.identity, this.contextId, constraintsAux);
2728             else message = MessageFactory.createInvitationMessage(this.me.identity, this.identity, this.contextId, constraintsAux, messageBody.conversationURL, messageBody.subject, messageBody.hosting, messageBody.agenda, messageBody.peers);
2729             setStatus(ParticipantStatus.PENDING); // TODO: OR WAITING?? Check for errors.
2730 
2731             this.RTCPeerConnection.createOffer(function (sessionDescription) {
2732                 thisParticipant.RTCPeerConnection.setLocalDescription(sessionDescription, function () {
2733                     console.log("Local description set: ", sessionDescription);
2734                     message.body.connectionDescription = thisParticipant.RTCPeerConnection.localDescription;
2735 
2736                     console.log("Sending message with constraints: ", constraints);
2737 
2738                     if (!thisParticipant.identity.messagingStub){
2739                         errorCallback("Messaging Stub not well initialized");
2740                         errorCallback;
2741                     }
2742                     else thisParticipant.identity.messagingStub.sendMessage(message);
2743 
2744                     if (callback)
2745                         callback();
2746 
2747                 }, function(error){errorCallback(error)});
2748             }, function(error){errorCallback(error)}, sdpConstraints);
2749 
2750             break;
2751         case MessageType.ACCEPTED:
2752             var constraintsAux =new Array();
2753             var i;
2754             for(i =0;i<constraints.length;i++){
2755                 var aux = new Object();
2756                 aux.id = constraints[i].id;
2757                 aux.type = constraints[i].type;
2758                 aux.direction =  constraints[i].direction;
2759                 constraintsAux.push(aux);
2760             }
2761             console.log(messageBody);
2762             console.log("this.me.identity.rtcIdentity: ", this.me.identity.rtcIdentity);
2763             console.log("this.hosting.rtcIdentity: ", this.hosting.rtcIdentity);
2764             //if(this.me.identity.rtcIdentity === thisParticipant.identity.rtcIdentity){
2765             if(this.me.identity.rtcIdentity === this.hosting.rtcIdentity){
2766                 //send a accepted message with no SDP inside
2767                 var message = new Object();
2768                 message = MessageFactory.createAnswerMessage(messageBody.from, "", thisParticipant.contextId, constraintsAux, "", messageBody.connected);
2769                 message.body.from = messageBody.from.rtcIdentity;
2770                 thisParticipant.identity.messagingStub.sendMessage(message);
2771 
2772             }
2773             else{
2774                 if(!messageBody) message = MessageFactory.createAnswerMessage(this.me.identity,this.identity,this.contextId, constraintsAux);
2775                 else message = MessageFactory.createAnswerMessage(this.me.identity,this.identity,this.contextId, constraintsAux, messageBody.hosting);
2776 
2777                 setStatus(ParticipantStatus.ACCEPTED);
2778 
2779                 this.RTCPeerConnection.createAnswer(function (sessionDescription) {
2780                     thisParticipant.RTCPeerConnection.setLocalDescription(sessionDescription, function () {
2781                         console.log("Local description set: ", sessionDescription);
2782                         message.body.connectionDescription = thisParticipant.RTCPeerConnection.localDescription;
2783 
2784                         if (!thisParticipant.identity.messagingStub){
2785                             errorCallback("Messaging Stub not well initialized");
2786                             return;
2787                         }
2788                         else thisParticipant.identity.messagingStub.sendMessage(message);
2789 
2790                         if (callback)
2791                             callback();
2792 
2793                     }, function(error){errorCallback(error)});
2794                 }, function(error){
2795                     console.log("error: ", error);
2796                 }, sdpConstraints);
2797             }
2798 
2799             break;
2800         case MessageType.CONNECTIVITY_CANDIDATE:
2801             //console.log("Missing data for connectivity candidate", messageBody);
2802             if(messageBody.lastCandidate){
2803                 if(errorCallback)
2804                     errorCallback("Missing data for connectivity candidate");
2805                 // SD: bugfix, we also need context.id etc. in this message
2806                 message = MessageFactory.createCandidateMessage( this.me.identity,this.identity,this.contextId,"",messageBody.id,messageBody.connectionDescription,true);
2807                 thisParticipant.identity.messagingStub.sendMessage(message);
2808                 return;
2809             }
2810             else message = MessageFactory.createCandidateMessage(this.me.identity,this.identity,this.contextId,messageBody.label,messageBody.id,messageBody.candidateDescription,messageBody.lastCandidate);
2811 
2812             if (!thisParticipant.identity.messagingStub){
2813                 errorCallback("Messaging Stub not well initialized");
2814                 return;
2815             }
2816             else thisParticipant.identity.messagingStub.sendMessage(message);
2817             break;
2818         case MessageType.BYE:
2819 
2820 
2821             message = new Message(this.me.identity,this.identity,"",MessageType.BYE,this.contextId);
2822             if (!thisParticipant.identity.messagingStub){
2823                 errorCallback("Messaging Stub not well initialized");
2824                 return;
2825             }
2826             else
2827             {
2828                 thisParticipant.identity.messagingStub.sendMessage(message);
2829                 setStatus(ParticipantStatus.NOT_PARTICIPATING); // TODO: CHECK IF ITS THE CORRECT STATE
2830             }
2831             console.log("Call terminated");
2832             break;
2833         case MessageType.REMOVE_PARTICIPANT:
2834 
2835 
2836             message = new Message(this.me.identity,this.identity,"",MessageType.REMOVE_PARTICIPANT,this.contextId);
2837             if (!thisParticipant.identity.messagingStub){
2838                 errorCallback("Messaging Stub not well initialized");
2839                 return;
2840             }
2841             else
2842             {thisParticipant.identity.messagingStub.sendMessage(message);
2843                 setStatus(ParticipantStatus.NOT_PARTICIPATING); // TODO: CHECK IF ITS THE CORRECT STATE
2844             }
2845             console.log("Call terminated");
2846             break;
2847         case MessageType.UPDATE:
2848             console.log("UPDATE");
2849 
2850             console.log("MESSAGE: ", message);
2851             message = MessageFactory.createUpdateMessage(this.me.identity,this.identity,this.contextId, messageBody.newConstraints);
2852             console.log(message);
2853 
2854             this.RTCPeerConnection.createOffer(function (sessionDescription) {
2855                 thisParticipant.RTCPeerConnection.setLocalDescription(sessionDescription, function () {
2856                     console.log("Local description set: ", sessionDescription);
2857                     message.body.newConnectionDescription = thisParticipant.RTCPeerConnection.localDescription;
2858                     if (!thisParticipant.identity.messagingStub){
2859                         errorCallback("Messaging Stub not well initialized");
2860                         return;
2861                     }
2862                     else thisParticipant.identity.messagingStub.sendMessage(message);
2863 
2864                 }, function(error){errorCallback(error)});
2865             }, function(error){errorCallback(error)}, sdpConstraints);
2866 
2867             break;
2868 
2869         case MessageType.UPDATED:
2870             //
2871             console.log("MESSAGE: ", message);
2872             message = MessageFactory.createUpdatedMessage(this.me.identity,this.identity,this.contextId, messageBody.newConstraints);
2873             console.log(message);
2874 
2875             console.log("VIDEO REMOTO: ", this.RTCPeerConnection.getRemoteStreams()[0].getVideoTracks());
2876             console.log("AUDIO REMOTO: ", this.RTCPeerConnection.getRemoteStreams()[0].getAudioTracks());
2877 
2878 
2879             this.RTCPeerConnection.createAnswer(function (sessionDescription) {
2880                 thisParticipant.RTCPeerConnection.setLocalDescription(sessionDescription, function () {
2881                     console.log("Local description set: ", sessionDescription);
2882                     message.body.newConnectionDescription = thisParticipant.RTCPeerConnection.localDescription;
2883 
2884                     if (!thisParticipant.identity.messagingStub){
2885                         errorCallback("Messaging Stub not well initialized");
2886                         return;
2887                     }
2888                     else thisParticipant.identity.messagingStub.sendMessage(message);
2889 
2890                     if (callback)
2891                         callback();
2892 
2893                 }, function(error){errorCallback(error)});
2894             }, function(error){errorCallback(error)}, sdpConstraints);
2895             break;
2896     }
2897     console.log('Sending: ', message);
2898 }
2899 
2900 
2901 /**
2902  * 
2903  * The Participant leaves the Conversation removing all resources shared in the conversation. 
2904  * Participant status is changed accordingly.
2905  *
2906  * @param {boolean} sendMessage - If true a BYE message will be sent to the participant before removing it. If false the participant will be removed locally from the conversation without sending any message.
2907  * 
2908  */
2909 
2910 Participant.prototype.leave = function(sendMessage) {
2911     setStatus(ParticipantStatus.PARTICIPATED);
2912     this.identity.messagingStub.removeListener("",this.identity.rtcIdentity,"");
2913     if(this == this.me){
2914         this.RTCPeerConnection.getLocalStreams().forEach(function(element, index, array){
2915             array[index].stop();
2916         });
2917     }
2918     else{
2919         if(sendMessage==true) this.sendMessage("",MessageType.BYE,"","",function(){},function(){});
2920         this.dataBroker.removeDataChannel(this.identity);
2921         this.RTCPeerConnection.close();
2922     }
2923 }
2924 
2925 
2926 /**
2927  * Returns the current status of this Participant
2928  * 
2929  * @returns ParticipantStatus ... gets the status attribute for a participant
2930  * 
2931  */
2932 Participant.prototype.getStatus = function() {
2933     return this.status;
2934 }
2935 
2936 /**@ignore
2937  * setConversation
2938  */
2939 
2940 Participant.prototype.setConversation = function(conversation) {
2941 
2942     if (!conversation)
2943         return;
2944     else
2945         this.conversation = conversation;
2946 }
2947 
2948 
2949 /**
2950  * Adds a Resource to this participant including all the signaling and logical actions required.
2951  * 
2952  * @param {ResourceConstraints} resourceConstraints - Array of constraints for the initial resources of the remote participant (CURRENT IMPLEMENTATION WILL TAKE THE FIRST ONE).
2953  * @param {Message} [message] - In case an UPDATE message is received, it should be passed to this function as a parameter to process it and send the UPDATED.
2954  * @param {callback} callback - Callback function fired when the resource was added succesfully.
2955  * @param {errorCallback} errorCallback -  Callback function fired when an error happens.
2956  *
2957  */
2958 Participant.prototype.addResource = function (resourceConstraints, message, callback, errorCallback) {
2959 
2960     if (this == this.me) {
2961         // Create the resource and the media if the direction is not "in"
2962 
2963         var doGetUserMedia = false;
2964         var doDataChannel = false;
2965         var conversationResource = false;
2966         var constraints = new Object();
2967         constraints.audio = false;
2968         constraints.video = false;
2969 
2970         // make the conditions to update the stream if we had already Webcam or microphone
2971         var micAlready = (this.getResources("",ResourceType.AUDIO_MIC).length!=0);
2972         var camAlready = (this.getResources("",ResourceType.VIDEO_CAM).length!=0);
2973         var micCamAlready = (this.getResources("",ResourceType.AUDIO_VIDEO).length!=0);
2974         // TODO: CHECK FOR DATACHANNEL TYPES AND CONTROL THE CREATION OF A NEW DATACHANNEL, CREATING IT ONLY IF NECCESARY
2975 
2976         // TODO: Solve the problem where for many resourceConstraints, we would have a loop with callbacks inside.
2977         resourceConstraints = resourceConstraints[0]; // <dirtyFix>
2978 
2979         switch (resourceConstraints.type) {
2980 
2981         case ResourceType.AUDIO_MIC:
2982             if(!micAlready & !micCamAlready){
2983                 constraints.audio = true;
2984                 doGetUserMedia = true;
2985             }
2986             break;
2987         case ResourceType.VIDEO_CAM:
2988             if(!camAlready & !micCamAlready){
2989                 constraints.video = true;
2990                 doGetUserMedia = true;
2991             }
2992             break;
2993         case ResourceType.AUDIO_VIDEO:
2994             if(!camAlready) constraints.video = true;
2995             if(!micAlready) constraints.audio = true;
2996             if(!micCamAlready) doGetUserMedia = true;
2997             break;
2998         case ResourceType.SCREEN:
2999             constraints.audio = false;
3000             constraints.video = {
3001                 mandatory: {
3002                         chromeMediaSource: 'screen',
3003                         maxWidth: 1280,
3004                         maxHeight: 720
3005                 },
3006                 optional: []
3007                 };
3008             doGetUserMedia = true;
3009             break;
3010         case ResourceType.FILE:
3011             doDataChannel = true;
3012             break;
3013         case ResourceType.CHAT:
3014             doDataChannel = true;
3015             break;
3016         }
3017 
3018 
3019         console.log(constraints);
3020 
3021         if (doGetUserMedia === true && resourceConstraints.direction != "in") {
3022             var thisParticipant = this;
3023             getUserMedia(constraints, function (stream) {
3024                 var evt = new Object();
3025                 evt.stream = stream;
3026                 thisParticipant.rtcEvtHandler('onaddlocalstream', evt);
3027 
3028                 if(!micAlready & !camAlready){
3029                     thisParticipant.RTCPeerConnection.addStream(stream);
3030                     var resource = new Resource(resourceConstraints);
3031                     resource.id = stream.id;
3032                     resource.constraint.constraints= {id: stream.id};
3033                     resource.connections.push(thisParticipant.RTCPeerConnection);
3034                     thisParticipant.resources.push(resource);
3035                 }
3036                 else{
3037                     if(micAlready){
3038                         var resource = thisParticipant.me.getResources("",ResourceType.AUDIO_MIC)[0];
3039                         var stream2 = thisParticipant.RTCPeerConnection.getStreamById(resource.id);
3040                         stream2.addTrack(stream.getVideoTracks()[0]);
3041                     }
3042                     if(camAlready){
3043                         var resource = thisParticipant.me.getResources("",ResourceType.VIDEO_CAM)[0];
3044                         var stream2 = thisParticipant.RTCPeerConnection.getStreamById(resource.id);
3045                         stream2.addTrack(stream.getAudioTracks()[0]); 
3046                     }
3047                     resource.type=ResourceType.AUDIO_VIDEO;   
3048                     resource.constraint.type=ResourceType.AUDIO_VIDEO;
3049                 }
3050 
3051                 callback();
3052             }, errorCallback);
3053             return;
3054         }
3055         if (doDataChannel === true && resourceConstraints.direction != "in") {
3056             var codec = new Codec(resourceConstraints.type);
3057             if(resourceConstraints.constraints.id) codec.id = resourceConstraints.constraints.id;
3058             var resource = new Resource(resourceConstraints, codec);
3059             resource.connections.push(thisParticipant.RTCPeerConnection);
3060             resource.owner = this.identity;
3061             this.resources.push(resource);
3062             var evt = new Object();
3063             evt.codec = codec;
3064             this.onRTCEvt('onResourceParticipantAddedEvt', evt);
3065         }
3066         callback();
3067     } else {
3068         if (resourceConstraints.direction != "out") {
3069             if (doGetUserMedia === true) {
3070                 var resource = new Resource(resourceConstraints);
3071                 resource.id = resource.constraint.constraints.id;
3072                 resource.owner = this.identity;
3073                 resource.connections.push(pc);
3074                 thisParticipant.resources.push(resource);
3075             }
3076             if (doDataChannel === true) {
3077                 var resource = new Resource(resourceConstraints);
3078                 //var codec = new Codec(resourceConstraints.type);
3079                 //resource.codec = codec;
3080                 resource.owner = this.identity;
3081                 resource.connections.push(pc);
3082                 thisParticipant.resources.push(resource);
3083             }
3084         }
3085         if (resourceConstraints.direction != "in") {
3086             // Get the resource from the me participant and add it to the peerConnection
3087             
3088             //TODO: IMPLEMENT AND CHECK!!!!!!!!!!!!!!
3089             var channel;
3090             var thisParticipant = this;
3091 
3092             if (resourceConstraints.length > 0) {
3093                 // TODO: Solve the problem where for many resourceConstraints, we would have a loop with callbacks inside.
3094                 constraints = resourceConstraints[0]; // <dirtyFix>
3095                 var media = false;
3096                 var data = false;
3097                 switch (constraints.type) {
3098                         
3099                 case ResourceType.AUDIO_MIC:
3100                     media = true;
3101                     break;
3102                 case ResourceType.VIDEO_CAM:
3103                     media = true;
3104                     break;
3105                 case ResourceType.AUDIO_VIDEO:
3106                     media = true;
3107                     break;
3108                 case ResourceType.FILE:
3109                     data = true;
3110                     break;
3111                 case ResourceType.CHAT:
3112                     data = true;
3113                     break;
3114                 }
3115             }
3116 
3117             // needed to be implemented the resources types
3118             //wiuth the resources types see what do we want to have
3119             //if chat -> codec for chat | if filesharing -> codec for filesharing
3120 
3121             // create data channel and setup chat        
3122             if (data && constraints.direction != "in") {
3123                 console.log("Creating remote peer with local constraints: ", constraints);
3124                 channel = thisParticipant.RTCPeerConnection.createDataChannel("dataChannel"); // TODO: CREATE DATACHANNEL ONLY IF THERE IS NOT DATARESOURCE YET.
3125                 thisParticipant.setDataBroker(constraints.constraints.dataBroker);
3126                 thisParticipant.dataBroker.addDataChannel(channel,thisParticipant.identity);
3127 
3128                 var resourceData = this.me.getResources(constraints)[0];
3129                 
3130                 resourceData.connections.push(thisParticipant.RTCPeerConnection);
3131 
3132 
3133                 channel.onopen = function () {
3134                     thisParticipant.dataBroker.onDataChannelEvt();
3135                     //thisParticipant.onRTCEvt("onResourceParticipantAddedEvt", resourceData);
3136                 }
3137 
3138                 // setup chat on incoming data channel
3139                 pc.ondatachannel = function (evt) {
3140                     channel = evt.channel;
3141                 };
3142             }
3143             if (media && constraints.direction != "in") {
3144                 var resourceMedia = this.me.getResources(constraints);
3145                                 
3146                 //If it doesnt find the constraints means that the audio and video were merged into AudioVideo
3147                 if(resourceMedia.length==0){
3148                     constraints.type=ResourceType.AUDIO_VIDEO;
3149                     resourceMedia = this.me.getResources(constraints)[0];
3150                 }
3151                 
3152                 var stream = this.me.RTCPeerConnection.getStreamById(resourceMedia[0].id);
3153                 thisParticipant.RTCPeerConnection.addStream(stream);
3154                 resourceMedia[0].connections.push(thisParticipant.RTCPeerConnection);
3155             }
3156         }
3157         
3158         if (!message) {
3159             // setlocal description, send update
3160             var messageBody = new Object();
3161             messageBody.newConstraints=resourceConstraints;
3162             this.sendMessage(messageBody, MessageType.UPDATE, constraints, callback, errorCallback);
3163             
3164         } else {
3165             // set remotedescription, set localdescription send updated 
3166             console.log("ADD_RESOURCE <- Participant");
3167             console.log("message: ", message);
3168             var description = new RTCSessionDescription(message.body.newConnectionDescription);
3169             this.RTCPeerConnection.setRemoteDescription(description,
3170                     onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
3171             console.log("Remote Description set: ", description);
3172             console.log("Participant: ", this);
3173             
3174             var messageBody = new Object();
3175             messageBody.newConstraints=resourceConstraints;
3176             this.sendMessage(messageBody, MessageType.UPDATED, constraints, callback, errorCallback);
3177         }
3178         callback();
3179     }
3180 }
3181 
3182 /**
3183  * Searches and retrieves Resources.
3184  *
3185  * @param {ResourceConstraints} [resourceConstraints] - Searches the Resources by constraints.
3186  * @param {ResourceType} [resourceType] - Searches the Resources by type.
3187  * @param {string} id - Searches the Resources by ID.
3188  *
3189  */
3190 Participant.prototype.getResources = function (resourceConstraints, resourceType, id) {
3191     
3192     var resources = new Array();
3193     
3194     if (resourceConstraints) {
3195         this.resources.forEach(function (element, index, array) {
3196             if (element.constraint.type == resourceConstraints.type && element.constraint.direction == resourceConstraints.direction) resources.push(array[index]);
3197         });
3198         return resources;
3199     }
3200 
3201     if (resourceType) {
3202         this.resources.forEach(function (element, index, array) {
3203             if (element.type === resourceType) resources.push(array[index]);
3204         });
3205         return resources;
3206     }
3207     if (id) {
3208         this.resources.forEach(function (element, index, array) {
3209             if (element.id === id) resources.push(array[index]);
3210         });
3211         return resources;
3212     }
3213 
3214     // If no filter, return all the resources.
3215     return this.resources;
3216 }
3217 
3218 /**@ignore
3219  * getStreams
3220  * 
3221  * @returns Stream [] ... gets the resources array attribute
3222  * 
3223  */
3224 Participant.prototype.getStreams = function() {
3225     if (this.me.identity.rtcIdentity == this.identity.rtcIdentity)
3226         return this.RTCPeerConnection.getLocalStreams();
3227     else
3228         return this.RTCPeerConnection.getRemoteStreams();
3229 }
3230 
3231 /**
3232  * Sets the DataBroker to a Participant
3233  * @param {DataBroker} databroker - DataBroker to set.
3234  * 
3235  */
3236 Participant.prototype.setDataBroker = function( databroker ) {
3237     this.dataBroker = databroker;
3238 }
3239 // -------------------------------
3240 // Conversation.js
3241 // -------------------------------
3242 
3243 /**
3244  * @fileOverview WebRTC Framework to facilitate the development of Applications that seamless interoperate between each other
3245  * @author Paulo Chainho <paulo-g-chainho@ptinovacao.pt>
3246  * @author Steffen Druesedow <Steffen.Druesedow@telekom.de>
3247  * @author Miguel Seijo Simo <Miguel.Seijo@telekom.de>
3248  * @author Vasco Amaral <vasco-m-amaral@ptinovacao.pt>
3249  * @author Kay Haensge <Kay.Haensge@telekom.de>
3250  * @author Luis Oliveira <luis-f-oliveira@ptinovacao.pt>
3251  */
3252 
3253 
3254 /**
3255  * Conversation class
3256  * @class
3257  * @param participants list of {@link Participant} involved in the conversation 
3258  * @param id Unique Conversation identification
3259  * @param owner the {@link Participant} organizing the conversation 
3260  * @param hosting the {@link Identity} that is providing the signalling message server 
3261  * @param rtcEvtHandler Event handler implemented by the Application to receive and process RTC events triggered by WebRTC Media Engine
3262  * @param msgHandler {@link Message} handler implemented by the Application to receive and process Messages from the {@link MessagingStub}
3263  *
3264  */
3265 function Conversation(myIdentity, rtcEvtHandler, msgHandler, iceServers, callback, errorCallback) {
3266      /**
3267      * The list of {@link Participant} in the Conversation.
3268      * @private
3269      * @type Participant[]
3270      */
3271     this.participants = [];
3272     
3273      /**
3274      * Unique Conversation identification.
3275      * @private
3276      * @type string
3277      */
3278     this.id;
3279     
3280      /**
3281      * The {@link Participant} that manages the Conversation having additional features 
3282      * Eg, add and remove participants, mute/unmute/share resources, etc. 
3283      * It may change in the middle of the conversation by sending a "OfferRole" {@link Message}.
3284      * @private
3285      * @type Participant
3286      */
3287     this.owner;
3288     this.rtcEvtHandler = rtcEvtHandler;
3289     this.msgHandler = msgHandler;
3290     
3291     this.dataBroker = new DataBroker();
3292     
3293      /**
3294      * The {@link Identity} that is providing the conversation signalling messaging server.
3295      * @private
3296      * @type Identity
3297      */
3298     this.hosting;
3299     
3300      /**
3301      * ICE servers setup data.
3302      * @private
3303      * @type String
3304      */
3305     this.iceServers=iceServers;
3306     
3307     /*
3308      * TODO: 
3309      * - hosting could be empty, in this case the communication is purely P2P
3310      * - if hosting is NOT empty, then the Conversation has to add a listener to the MessagingStub
3311      *   of the hosting-identity
3312      * - if hosting is empty, then the participants are invoking addListener of 
3313      */
3314     this.resources = [];
3315     //this.status;
3316     //this.recording;
3317     //this.subject;
3318     //this.agenda = [];
3319     //this.startingTime;
3320     //this.duration;
3321 
3322     //this.eventHandler;
3323     this.myParticipant = new Participant();
3324     this.myParticipant.identity = myIdentity;
3325     thisConversation = this;
3326 }
3327 
3328 
3329 
3330 /**
3331  * A Conversation is opened for invited participants. 
3332  * Creates the remote participant, resolves and gets the stub, 
3333  * creates the peer connection, connects to the stub and sends invitation
3334  * 
3335  * @param {string[]} rtcIdentity list of users to be invited
3336  * @param {string} [invitation] body to be attached to INVITATION {@link MESSAGE}
3337  * @@callback callback to handle responses triggered by this operation
3338  */
3339 Conversation.prototype.open = function (rtcIdentity, resourceConstraints, invitationBody, callback, errorCallback) {
3340 	/* TODO: 
3341 	In the following code from the clients doCall() method it seems that the peers and invitation params hold 
3342 	redundant information - just the peers.
3343 	...
3344 	var peers = document.getElementById('callTo').value.split(";");
3345 	conversation = new Conversation(myIdentity, this.onRTCEvt.bind(this),
3346 	this.onMessage.bind(this), iceServers);
3347 	var invitation = new Object();
3348 	invitation.peers = peers;
3349 	conversation.open(peers, constraints, invitation);	
3350 	...
3351 	*/
3352     var that = this;
3353 
3354     this.myParticipant.createMyself(this.myParticipant.identity, resourceConstraints, this.onRTCEvt.bind(this), this.onMessage.bind(this), function () {
3355 
3356         that.id = "context-" + guid();
3357         that.owner = that.myParticipant;
3358         that.owner.contextId = that.id;
3359 
3360         that.myParticipant.contextId = that.id;
3361         if (that.myParticipant.hosting === "")
3362             that.myParticipant.hosting = that.myParticipant.identity;
3363 
3364         var localParticipant = that.myParticipant;
3365         var localIDP = localParticipant.identity.idp;
3366         var toIdentity;
3367         //add a verification if rtcIdentity is already an identity or if is a rtcIdentity only
3368         //
3369         localIDP.createIdentities(rtcIdentity, function (identity) {
3370             console.log("rtcIdentity: ", identity);
3371             if (identity instanceof Array) {
3372                 identity.forEach(function (element, index, array) {
3373                     var participant = new Participant();
3374                     console.log("owner: ", this.owner);
3375                     toIdentity = element;
3376 
3377                     participant.hosting = that.myParticipant.hosting;
3378 
3379                     console.log("Calling to Identity: ", toIdentity);
3380 
3381                     console.log("Created remote participant: ", participant);
3382                     /*var constraints = new Array();
3383                     that.resources.every(function (element, index, array) {
3384                         constraints.push(element.constraint);
3385                     });
3386                     that.myParticipant.resources.every(function (element, index, array) {
3387                         constraints.push(element.constraint);
3388                     });*/
3389 
3390                     participant.setDataBroker(that.dataBroker);
3391                     participant.createRemotePeer(toIdentity, localParticipant, that.id, resourceConstraints, that.onRTCEvt.bind(that), that.onMessage.bind(that), that.iceServers);
3392                     that.addParticipant(participant, invitationBody, resourceConstraints);
3393                 });
3394             }
3395         });
3396 
3397 
3398 
3399 
3400 
3401     }, errorCallback);
3402 };
3403 
3404 /**
3405  * Opens a conversation by accepting an incoming invitation.
3406  * Sends the message to the addressed participant (the one who sent the invitation)
3407  * Sets the Conversation status to OPENED.
3408  * @param {Message} invitation the invitation message received for the accepted conversation
3409  * @param {string} answer the answer to be sent with the accepted message
3410  * @param {string} [constraints] any constraint on how the invitation was accepted e.g. only audio but not video  
3411  * @callback callback the callback that handles events triggered by this function 
3412  */
3413 Conversation.prototype.acceptInvitation = function(recvInvitation, answerBody, callback, errorCallback) {
3414 
3415     // Swap direction because we are receiving
3416     var direction = "in_out";
3417     /* if(recvInvitation.body.constraints[0].direction=="in") direction="out";
3418      if(recvInvitation.body.constraints[0].direction=="out") direction="in";
3419      recvInvitation.body.constraints[0].direction=direction;
3420      onsole.log("CONVERSATION..> acceptInvitation: ", recvInvitation);*/
3421     if (!this.setStatus(ConversationStatus.OPENED)) {
3422         // TODO: ERROR, Status cant be changed
3423         return;
3424     }
3425 
3426     var that = this;
3427     var chatID = new Object();
3428     var videoID = new Object();
3429     var fileID = new Object();
3430     var iteration;
3431     var i;
3432     for(i=0;i<recvInvitation.body.constraints.length;i++){
3433         if(recvInvitation.body.constraints[i].type == "file"){
3434             fileID.id = recvInvitation.body.constraints[i].id;
3435             fileID.index = i;
3436         }else{
3437             if(recvInvitation.body.constraints[i].type == "chat"){
3438                 chatID.id = recvInvitation.body.constraints[i].id;
3439                 chatID.index = i;
3440             }else{
3441                 if(recvInvitation.body.constraints[i].type == "audioVideo"){
3442                     videoID.id = recvInvitation.body.constraints[i].id;
3443                     videoID.index = i;
3444                 }
3445             }
3446         }
3447     }
3448     this.myParticipant.createMyself(this.myParticipant.identity, recvInvitation.body.constraints, this.onRTCEvt.bind(this), this.onMessage.bind(this), function () {
3449         //TODO: THINK OF MULTIPARTY CASE, YOU RECEIVE A CALL BUT THE INVITE IS FOR MANY PEOPLE
3450         that.id = recvInvitation.contextId;
3451         //this.owner.contextId=this.id;
3452         that.myParticipant.contextId=that.id;
3453 
3454         //var participant = new Participant();
3455         //var localParticipant = this.owner;
3456         console.log("CONVERSATION > SS1: ", that.myParticipant.hosting);
3457         if(that.myParticipant.hosting == null){
3458             that.myParticipant.hosting = recvInvitation.from;
3459             console.log("CONVERSATION > SS: ", that.myParticipant.hosting);
3460             console.log("CONVERSATION > SS: ", recvInvitation.from);
3461         }
3462 
3463 
3464         var localParticipant = that.myParticipant;
3465         //console.log("CONSTRAINTS AFTER!!!!:" + recvInvitation.body.constraints[0].constraints.id);
3466 
3467 
3468         var localIDP = localParticipant.identity.idp;
3469         var toIdentity;
3470 
3471         var constraints = recvInvitation.body.constraints; // <dirtyFix>
3472 
3473         console.log("recvInvitation.body.constraints: ", recvInvitation.body.constraints);
3474         for(iteration=0;iteration<constraints.length;iteration++){
3475             if(constraints[iteration].type==ResourceType.CHAT || constraints[iteration].type==ResourceType.FILE){
3476                 //beginof: create a codec with the data received
3477                 var codec=new Codec(constraints[iteration].constraints.type,constraints[iteration].constraints.CodecLibUrl);
3478                 console.log("constraints.type==ResourceType.CHAT: ", that.myParticipant.resources);
3479                 that.myParticipant.resources[iteration].codec.id=constraints[iteration].constraints.id;
3480                 var resource = new Resource(constraints[iteration], codec);
3481                 resource.codec.setDataBroker(that.dataBroker);
3482                 //endof: create a codec with the data received
3483             }
3484         }
3485         //constraints = new Array(constraints); // </dirtyFix>
3486 
3487         //Create an array to all peers that I want to connect
3488         //recvInvitation.body.peers[i] is defined when the clients are selected in the application
3489         var peers = new Array();
3490         peers.push(recvInvitation.from.rtcIdentity);
3491         console.log("recv: ", recvInvitation);
3492         for(var i = 0; i < recvInvitation.body.peers.length; i++){
3493             if(recvInvitation.body.peers[i] !== that.myParticipant.identity.rtcIdentity)
3494                 peers.push(recvInvitation.body.peers[i]);
3495         }
3496 
3497         //now should be createIdentities because of multiparty
3498         localIDP.createIdentities(peers, function(identity){
3499 
3500             if(identity instanceof Array){
3501                 console.log("Identity: ", identity);
3502 
3503                 identity.forEach(function(element, index, array){
3504                         var participant = new Participant();
3505 
3506                         toIdentity = element;
3507                         that.hosting = recvInvitation.body.hosting;
3508                         console.log("THIS.HOSTING", recvInvitation.body.hosting);
3509                         if(typeof that.owner === 'undefined'){
3510                             that.owner = participant;
3511                         }
3512                         participant.hosting = that.owner;
3513                         if(that.hosting == recvInvitation.from.rtcIdentity){
3514                             console.log("THIS.HOSTING", that.hosting);
3515                             console.log("THIS.OWNER", that.owner);
3516                             toIdentity.messagingStub = recvInvitation.from.messagingStub;
3517                         }
3518                         else{
3519                             toIdentity.messagingStub = that.myParticipant.identity.messagingStub;
3520                         }
3521 
3522 
3523                         participant.setDataBroker(that.dataBroker);
3524                         if(chatID.id != null){
3525                             constraints[chatID.index].id = {id: chatID.id};
3526                         }
3527                         if(videoID.id != null){
3528                             constraints[videoID.index].id = {id: videoID.id};
3529                         }
3530                         if(fileID.id != null){
3531                             constraints[fileID.index].id = {id: fileID.id};
3532                         }
3533 
3534                         participant.createRemotePeer(toIdentity, localParticipant, that.id, constraints,that.onRTCEvt.bind(that), that.onMessage.bind(that), that.iceServers);
3535                         that.participants.push(participant);
3536                         if(recvInvitation.from.rtcIdentity === toIdentity.rtcIdentity){
3537                             //Only do the RTCPeerConnection to the identity that is inviting
3538                             //for the other identities only creates the participants
3539                             console.log("ENTREI > acceptInvitation: ", recvInvitation);
3540                             var description = new RTCSessionDescription(recvInvitation.body.connectionDescription);
3541                             participant.RTCPeerConnection.setRemoteDescription(description, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
3542                         }
3543                         participant.connectStub(function() {
3544                             if(recvInvitation.from.rtcIdentity === toIdentity.rtcIdentity){
3545                                 participant.sendMessage(answerBody, MessageType.ACCEPTED, constraints, callback, errorCallback);
3546                             }
3547 
3548                         });
3549                     },
3550                     function(error){
3551                         console.log(error);
3552                     });
3553             }
3554         });
3555 
3556     }, errorCallback);
3557 };
3558 
3559 
3560 /**
3561  * If to-field of the message is empty, then send message to all participants, send only to specified participants
3562  * if to-field is filled.
3563  * (Message.to-field is a list of identities.)
3564  * @param {MESSAGE} message the {@link Message} to be sent to the specified Identities or or ALL participants
3565  */
3566 Conversation.prototype.sendMessage = function(message) {
3567     if (!message)
3568         return;
3569 
3570     /*
3571      *  TODO:
3572      *  if this.hosting is set, then ALL Messages are send via the MessagingStub
3573      *  of the hosting identity.
3574      *  Only if this.hosting is NOT SET we iterate through the participants
3575      */
3576 
3577     if (this.hosting) {
3578         // seems that there is a special Identity assigned to "host" this conversation
3579         // in this case ALL Messages must be sent via the messagingStub of this identity
3580         // TODO: Check following ASSUMPTION:
3581         // Only one message is sent to the Messaging stub of the hosting-identity. The MessagingServer is responsible
3582         // to forward individual messages to all attached participants of this conversation.
3583         var p = this.getParticipant(this.hosting);
3584         if (p)
3585             p.sendMessage(message);
3586     }
3587     else {
3588         // send to all participants via their own messaging stubs, if to is not set or empty
3589         if (!message.to) {
3590             for (var p in this.participants)
3591                 this.participants[p].sendMessage(message);
3592         }
3593         else {
3594             // send to all participants that we have for the given identities
3595             for (var i in message.to) {
3596                 // check for participant matching this identity
3597                 var p = this.getParticipant(message.to[i]);
3598                 if (p)
3599                     p.sendMessage(message);
3600             }
3601         }
3602     }
3603 };
3604 
3605 /** @ignore
3606  * Set the status of this conversation. 
3607  * TODO: This method should be private and only be changed by internal state-changes 
3608  * @param status ConversationStatus ... the new status of the conversation
3609  */
3610 Conversation.prototype.setStatus = function(status) {
3611     // DONE: implement the state machine checks here !!!
3612     // TODO: PAUSED AND STOPPED are not in the Enums !!!
3613     // TODO: Change the UML so returns true or false if the state change is not allowed.
3614     // (@Vasco) the previous state machine verifications it was not working in a correct way 
3615     // Changed verify below
3616     console.log("In setStatus");
3617     switch (this.status) {
3618         case ConversationStatus.CREATED:
3619             if (status != ConversationStatus.OPENED) {
3620                 console.log("transition is not permited " + status + " actual state: " + this.status);
3621                 return false;
3622             } else {
3623                 this.status = status;
3624                 return true;
3625             }
3626         case ConversationStatus.OPENED:
3627             if (status != ConversationStatus.ACTIVE && status != ConversationStatus.INACTIVE && status != ConversationStatus.FAILED && status != ConversationStatus.PLAYING && status != ConversationStatus.CLOSED) {
3628                 console.log("transition is not permited " + status + " actual state: " + this.status);
3629                 return false;
3630             } else {
3631                 this.status = status;
3632                 return true;
3633             }
3634         case ConversationStatus.INACTIVE:
3635             if (status != ConversationStatus.ACTIVE) {
3636                 console.log("transition is not permited " + status + " actual state: " + this.status);
3637                 return false;
3638             } else {
3639                 this.status = status;
3640                 return true;
3641             }
3642         case ConversationStatus.FAILED:
3643             console.log("transition is not permited");
3644             return false;
3645         case ConversationStatus.ACTIVE:
3646             if (status != ConversationStatus.INACTIVE && status != ConversationStatus.CLOSED && status != ConversationStatus.FAILED && status != ConversationStatus.RECORDING && status != ConversationStatus.PLAYING) {
3647                 return false;
3648                 console.log("transition is not permited " + status + " actual state: " + this.status);
3649             } else {
3650                 this.status = status;
3651                 return true;
3652             }
3653         case ConversationStatus.CLOSED:
3654             console.log("transition is not permited " + status + " actual state: " + this.status);
3655             return false;
3656         case ConversationStatus.RECORDING:
3657             if (status != ConversationStatus.FAILED && status != ConversationStatus.INACTIVE && status != ConversationStatus.CLOSED && status != ConversationStatus.PLAYING) {
3658                 console.log("transition is not permited " + status + " actual state: " + this.status);
3659                 return false;
3660             } else {
3661                 this.status = status;
3662                 return true;
3663             }
3664         case ConversationStatus.PLAYING:
3665             if (status != ConversationStatus.PAUSED && status != ConversationStatus.STOPPED && status != ConversationStatus.INACTIVE && status != ConversationStatus.ACTIVE) {
3666                 console.log("transition is not permited " + status + " actual state: " + this.status);
3667                 return false;
3668             } else {
3669                 this.status = status;
3670                 return true;
3671             }
3672         case ConversationStatus.PAUSED:
3673             if (status != ConversationStatus.PLAYING && status != ConversationStatus.STOPPED && status != ConversationStatus.INACTIVE && status != ConversationStatus.ACTIVE) {
3674                 console.log("transition is not permited " + status + " actual state: " + this.status);
3675                 return false;
3676             } else {
3677                 this.status = status;
3678                 return true;
3679             }
3680         case ConversationStatus.STOPPED:
3681             console.log("transition is not permited " + status + " actual state: " + this.status);
3682             return false;
3683         default:
3684             if (!this.status) {
3685                 this.status = status;
3686                 return true;
3687             }
3688             return false;
3689     }
3690 };
3691 
3692 /** 
3693  * Returns the status of this conversation
3694  */
3695 Conversation.prototype.getStatus = function() {
3696     return(this.status);
3697 };
3698 
3699 
3700 /**
3701  * Close the conversation with the given message.
3702  * Sends this message to ALL participants and sets the conversation status to CLOSED
3703  * @param {Message} message the final message to be sent to ALL participants of this conversation
3704  * @return {boolean} True if successful, false if the participant is not the owner.
3705  */
3706 Conversation.prototype.close = function() {
3707     if(this.owner==this.myParticipant)
3708     {
3709         this.participants.forEach(function(element,index,array){
3710             element.status=ParticipantStatus.PARTICIPATED;
3711             element.identity.messagingStub.removeListener("",element.identity.rtcIdentity,"");
3712             element.sendMessage("",MessageType.REMOVE_PARTICIPANT,"","",function(){},function(){});
3713             element.RTCPeerConnection.close();
3714         });
3715         this.myParticipant.leave(false);
3716         this.setStatus(ConversationStatus.CLOSED);
3717         return true;
3718     }
3719     return false;
3720 }
3721 
3722 
3723 /**
3724  * The current user invokes bye, if he wants to leave an ongoing conversation.
3725  * Other participants might stay in this conversation, in case that it was a multi-party call with 
3726  * more participants. </br>
3727  * Sends a REMOVE_PARTICIPANT message to ALL participants and sets the conversation status to CLOSED.
3728  */
3729 Conversation.prototype.bye = function() {
3730     this.participants.forEach(function(element,index,array){
3731                                 element.leave(true);   
3732                                 delete array[index];
3733     });
3734     this.myParticipant.leave(false);
3735     this.setStatus(ConversationStatus.CLOSED);
3736 };
3737 
3738 
3739 /**
3740  * Adds a participant to the conversation.
3741  * @param {Participant} participant the {@link Participant} to add to the conversation
3742  * @param {String} [invitation] the invitation to be attached to the {@link Message} body
3743  */
3744 Conversation.prototype.addParticipant = function(participant, invitationBody, constraints, callback, callbackError) {
3745     this.participants.push(participant);
3746     participant.connectStub(function() { // @pchainho: why do we need this?
3747         participant.sendMessage(invitationBody, MessageType.INVITATION, constraints, callback, callbackError)
3748     });
3749 };
3750 
3751 /*** Returns the participants of the conversation as an Array.
3752  * @returns {Participants[]}
3753  */
3754 Conversation.prototype.getParticipants = function() {
3755     return(this.participants);
3756 };
3757 
3758 
3759 /**@ignore
3760  * Callback for events from the Participants (received via the MessagingStub)
3761  * @param message : Message
3762  */
3763 Conversation.prototype.onMessage = function(message) {
3764     // TODO: implement eventHandling
3765     switch (message.type) {
3766 
3767         case MessageType.ACCEPTED:
3768             console.log("MESSAGEACCEPTEDCONVERSATION: ", message);
3769             // TODO: change state of the conversation and forward to app-layer
3770             break;
3771         case MessageType.CONNECTIVITY_CANDIDATE:
3772 
3773             // put candidate to PC
3774             break;
3775         case MessageType.NOT_ACCEPTED:
3776             this.participants.forEach(function(element, index, array){
3777                 if(element.status==ParticipantStatus.PARTICIPATED){
3778                     array.splice(index, 1);
3779                 }
3780             });
3781             if(this.participants.length==0) this.bye();
3782             break;
3783         case MessageType.CANCEL:
3784             break;
3785         case MessageType.ADD_RESOURCE:
3786             break;
3787         case MessageType.UPDATE:
3788             break;
3789         case MessageType.UPDATED:
3790             break;
3791         case MessageType.REDIRECT:
3792             break;
3793         case MessageType.BYE:
3794             this.participants.forEach(function(element, index, array){
3795                 if(element.status==ParticipantStatus.PARTICIPATED){
3796                     array.splice(index, 1);
3797                 }
3798             });
3799             if(this.participants.length==0) this.bye();
3800             break;
3801         case MessageType.OFFER_ROLE: // set new moderator of the conversatoin
3802             break;
3803         case MessageType.INVITATION:
3804             // IF RECEIVED, SOMETHING IS WRONG
3805            /*INVITATION
3806                 Participant should only receive invitations in multiparty conversation. In this case it will be automatically accepted, the peerconnection is set and the Accepted message sent.*/
3807 
3808             break;
3809         case MessageType.RESOURCE_REMOVED:
3810             break;
3811         case MessageType.REMOVE_PARTICIPANT:
3812             // Remove everyone without sending BYE ("silently")
3813             this.participants.forEach(function (element, index, array) {
3814                 element.leave(false);
3815                 delete array[index];
3816             });
3817             this.myParticipant.leave(false);
3818             this.setStatus(ConversationStatus.CLOSED);
3819             break;
3820         case MessageType.SHARE_RESOURCE:
3821             break;
3822         default:
3823             // forward to application level
3824             break;
3825     }
3826     this.msgHandler(message);
3827 };
3828 
3829 /** @ignore */
3830 Conversation.prototype.onRTCEvt = function(event, evt) {
3831     // TODO To implement and pass the events up
3832     switch (event) {
3833 
3834         case 'onnegotiationneeded':
3835             this.rtcEvtHandler(event, evt);
3836             break;
3837         case 'onicecandidate':
3838             this.rtcEvtHandler(event, evt);
3839             break;
3840         case 'onsignalingstatechange':
3841             this.rtcEvtHandler(event, evt);
3842             break;
3843         case 'onaddstream':
3844             this.rtcEvtHandler(event, evt);
3845             break;
3846         case 'onaddlocalstream':
3847             this.rtcEvtHandler(event,evt);
3848             break;
3849         case 'onremovestream':
3850             this.rtcEvtHandler(event, evt);
3851             break;
3852         case 'oniceconnectionstatechange':
3853             this.rtcEvtHandler(event, evt);
3854             break;
3855         case 'ondatachannel':
3856             this.rtcEvtHandler(event, evt);
3857             break;
3858         default:
3859             this.rtcEvtHandler(event, evt);
3860             break;
3861 
3862     }
3863 };
3864 
3865 /**@ignore
3866  * Records a conversation.
3867  */
3868 Conversation.prototype.record = function() {
3869     // TODO: to be re-fined and implemented
3870 };
3871 
3872 /**@ignore
3873  * Playback a (part of a) conversation.
3874  * @param timing : ??? ... time index information
3875  * @param resources : Resource[1..*] ... the resource to be played back
3876  */
3877 Conversation.prototype.play = function(timing, resources) {
3878     // TODO: to be re-fined and implemented
3879 };
3880 
3881 /**@ignore
3882  * Pause playback of a conversation.
3883  */
3884 Conversation.prototype.pause = function() {
3885     // TODO: to be re-fined and implemented
3886 };
3887 
3888 
3889 /**@ignore
3890  * Helper function to get the matching Particpant for a given Identity.
3891  * @param identity : Identity ... the identity to search for
3892  * @returns the Particpant of the current conversation that matches the given Identity
3893  */
3894 Conversation.prototype.getParticipant = function(identity) {
3895     if (!identity)
3896         return;
3897     var match;
3898     for (var p in this.participants)
3899         if (this.participants[p].identity.rtcIdentity == identity.rtcIdentity)
3900             match = this.participants[p];
3901     return match;
3902 };
3903 
3904 /**
3905  * ConversationAddResource
3906  */
3907 Conversation.prototype.addResource = function(resourceConstraints, message, onSuccessCallback, onErrorCallback) {
3908 
3909     //see what's in the resource (resourceConstraints)
3910     var thisConversation = this;
3911     // If it comes with a message, means we add a resource from an incoming petition to the corresponding participant.
3912     if(!message){
3913     var count=0;
3914     var internalSuccessCallback = function(){
3915             if(count<thisConversation.participants.length){ 
3916                 count++;
3917                 console.log("Adding the resource for the participant number: " + count);
3918                 thisConversation.participants[count-1].addResource(resourceConstraints,"",internalSuccessCallback);
3919             }
3920             else{
3921                 onSuccessCallback();
3922             }
3923 
3924     }
3925     
3926             thisConversation.myParticipant.addResource(resourceConstraints,"",internalSuccessCallback);
3927     }
3928     else{
3929         // Swap direction because we are receiving
3930         var direction = "in_out";
3931         if(message.body.newConstraints[0].direction=="in") direction="out";
3932         if(message.body.newConstraints[0].direction=="out") direction="in";
3933         message.body.newConstraints[0].direction=direction;
3934         
3935         thisConversation.myParticipant.addResource(resourceConstraints,"",function() {
3936             thisConversation.getParticipant(message.from).addResource(resourceConstraints,message,onSuccessCallback,onErrorCallback);
3937         });
3938         
3939     }
3940  };
3941  
3942 Conversation.reject = function(message){
3943     message.from.resolve(function(stub){stub.sendMessage(MessageFactory.createNotAccepted(message))});
3944 
3945 }