diff --git a/bower.json b/bower.json index f56c15a1d..a9bfdf860 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "twilio-conversations", - "version": "0.13.0-dev", + "version": "0.13.0", "description": "Twilio Conversations JavaScript library", "license": "BSD", "authors": [ diff --git a/dist/docs/AudioTrack.html b/dist/docs/AudioTrack.html new file mode 100644 index 000000000..0bd271de0 --- /dev/null +++ b/dist/docs/AudioTrack.html @@ -0,0 +1,1620 @@ + + + + + JSDoc: Class: AudioTrack + + + + + + + + + + +
+ +

Class: AudioTrack

+ + + + + + +
+ +
+ +

AudioTrack

+ +

An AudioTrack is a Track representing audio.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
attachments + + +Set.<HTMLElement> + + + +

The <audio> elements this + AudioTrack is currently attached to (managed by + AudioTrack#attach)

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the AudioTrack to a newly created <audio> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteAudioEl = audioTrack.attach();
+document.getElementById('div#remote-audio-container').appendChild(remoteAudioEl);
+ + + + + + + + +

attach(audio) → {HTMLElement}

+ + + + + +
+

Attach the AudioTrack to an existing <audio> element.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
audio + + +HTMLElement + + + +

The <audio> element to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteAudioEl = document.getElementById('remote-audio');
+audioTrack.attach(remoteAudioEl);
+ + + + + + + + +

attach(selector) → {HTMLElement}

+ + + + + +
+

Attach the AudioTrack to a <audio> element selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the <audio> element to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteAudioEl = audioTrack.attach('audio#remote-audio');
+ + + + + + + + +

detach(selector) → {HTMLElement}

+ + + + + +
+

Detach the AudioTrack from a previously attached <audio> element selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the <audio> element to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var detachedAudioEl = media.detach('div#remote-audio');
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the AudioTrack from any and all previously attached <audio> elements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedAudioEls = audioTrack.detach();
+ + + + + + + + +

detach(audio) → {HTMLElement}

+ + + + + +
+

Detach the AudioTrack from a previously attached <audio> element.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
audio + + +HTMLElement + + + +

The <audio> element to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteAudioEl = document.getElementById('remote-audio');
+audioTrack.detach(remoteAudioEl);
+ + + + + + + + + +

Events

+ + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/Client.html b/dist/docs/Client.html new file mode 100644 index 000000000..f8dff953a --- /dev/null +++ b/dist/docs/Client.html @@ -0,0 +1,1502 @@ + + + + + JSDoc: Class: Client + + + + + + + + + + +
+ +

Class: Client

+ + + + + + +
+ +
+ +

Client

+ +

Construct a Client to start creating and participating + in Conversations with other Participants.

+ + +
+ +
+
+ + + + +

Constructor

+ + +

new Twilio.Conversations.Client(managerOrToken, optionsopt)

+ + + + + +
+

Constructs a new Client with an AccessManager. Alternatively, you +can pass an Access Token string and the Client will construct an +AccessManager for you. AccessManager is provided by twilio-common.js, which +must be included alongside twilio-conversations.js.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
managerOrToken + + +AccessManager +| + +string + + + + + + + + + +

The Client's AccessManager or an Access Token string to use when constructing an AccessManager

options + + +Client.ConstructorOptions + + + + + + <optional>
+ + + + + +

Options to override the + constructor's default behavior

+ + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
accessManager + + +AccessManager + + + +

The Client's AccessManager

identity + + +string + + + +

The Client's identity

conversations + + +Map.<Conversation.SID, Conversation> + + + +

The Conversations this + Client is participating in

isListening + + +bool + + + +

Whether the Client is listening for + IncomingInvites to Conversations

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + + + + +

inviteToConversation(participants, optionsopt) → {OutgoingInvite}

+ + + + + +
+

Invite remote Clients to join a Conversation. +

+ By default, this will attempt to setup an AudioTrack and + VideoTrack between local and remote Clients. You can + override this by specifying options.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
participants + + +Array.<string> +| + +string + + + + + + + + + + + +

Participant identities to invite to the Conversation

options + + +Client.InviteToConversationOptions + + + + + + <optional>
+ + + + + +
+ + {localStreamConstraints:{audio:true,video:true}} + +

Options to override + Client#inviteToConversation's default behavior

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +OutgoingInvite + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+client.inviteToConversation(['bob', 'charlie']).then(function(conversation) {
+  conversation.on('participantConnected', function(participant) {
+    console.log(participant.identity + ' has connected');
+  });
+});
+ + + + + + + + +

listen() → {Promise.<this>}

+ + + + + +
+

Causes this Client to start listening for IncomingInvites to + Conversations.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<this> + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var alice = new Twilio.Conversations.Client(manager);
+
+alice.listen().then(function() {
+  console.log('Alice is listening');
+}, function(error) {
+  console.error(error);
+});
+ + + + + + + + +

unlisten() → {this}

+ + + + + +
+

Causes this Client to stop listening for IncomingInvites to + Conversations until Client#listen is called again.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + +

Type Definitions

+ + + +

ConstructorOptions

+ + + + +
+

You may pass these options to Client's constructor to override +its default behavior.

+
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
logLevel + + +string + + + + + + <optional>
+ + + +
+ + 'warn' + +

Set the verbosity of logging to console. + Valid values: ['off', 'error', 'warn', 'info', 'debug']

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

InviteToConversationOptions

+ + + + +
+

You may pass these options to Client#inviteToConversation to +override the default behavior.

+
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
localMedia + + +LocalMedia + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + LocalMedia object when creating the Conversation

localStream + + +MediaStream + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + MediaStream when creating the Conversation

localStreamConstraints + + +object + + + + + + <optional>
+ + + + <nullable>
+ +
+ + {audio:true,video:true} + +

Set to + override the parameters passed to getUserMedia when neither + localMedia nor localStream are provided

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

error

+ + + + + +
+

Your Client has run into an error.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +Error + + + +

The Error

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+client.on('error', function(error) {
+ console.error(error);
+});
+ + + + + + + + +

invite

+ + + + + +
+

Your Client has received an IncomingInvite to participant in a +Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +IncomingInvite + + + +

the IncomingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+client.on('invite', function(invite) {
+  console.log('Received an IncomingInvite to join a Conversation from ' + invite.from);
+});
+ + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/Conversation.html b/dist/docs/Conversation.html new file mode 100644 index 000000000..8ecfa5f10 --- /dev/null +++ b/dist/docs/Conversation.html @@ -0,0 +1,2520 @@ + + + + + JSDoc: Class: Conversation + + + + + + + + + + +
+ +

Class: Conversation

+ + + + + + +
+ +
+ +

Conversation

+ +

A Conversation represents communication between your + Client and one or more Participants sharing + AudioTracks and VideoTracks. +

+ You can join a Conversation by first creating an + OutgoingInvite with Client#inviteToConversation or by + accepting an IncomingInvite with IncomingInvite#accept.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
localMedia + + +LocalMedia + + + +

Your Client's LocalMedia in the Conversation

participants + + +Map.<Participant.SID, Participant> + + + +

The Participants + participating in this Conversation

sid + + +Conversation.SID + + + +

The Conversation's SID

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + + + + +

disconnect() → {this}

+ + + + + +
+

Disconnect from the Conversation.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

invite(identity) → {this}

+ + + + + +
+

Add a Participant to the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + +

The identity of the Participant to add

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + +
Throws:
+ + + +
+
+
+

INVALID_ARGUMENT

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+ client.inviteToConversation('alice').then(function(conversation) {
+   conversation.invite('bob');
+
+   conversation.on('participantConnected', function(participant) {
+     if (participant.identity === 'bob') {
+       console.log('Bob has connected');
+     }
+   });
+ });
+ + + + + + + + +

invite(identities) → {this}

+ + + + + +
+

Add Participants to the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identities + + +Array.<string> + + + +

The identities of the Participants to add

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + +
Throws:
+ + + +
+
+
+

INVALID_ARGUMENT

+
+
+
+
+
+
+ Type +
+
+ +Error + + +
+
+
+
+
+ + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+ client.inviteToConversation('alice').then(function(conversation) {
+   conversation.invite(['bob', 'charlie']);
+
+   conversation.on('participantConnected', function() {
+     if (participant.identity === 'bob') {
+       console.log('Bob has connected');
+     } else if (participant.identity === 'charlie') {
+       console.log('Charlie has connected');
+     }
+   });
+ });
+ + + + + + + +

Type Definitions

+ + + +

SID

+ + + + +
+

A Conversation.SID is a 34-character string starting with "CV" +that uniquely identifies a Conversation.

+
+ + + +
Type:
+
    +
  • + +string + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

disconnected

+ + + + + +
+

Your Client was disconnected from the Conversation and all +other Participants.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
conversation + + +Conversation + + + +

The Conversation your + Client was disconnected from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
myConversation.on('disconnected', function() {
+  myConversation.localMedia.detach();
+});
+ + + + + + + + +

participantConnected

+ + + + + +
+

A Participant joined the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
participant + + +Participant + + + +

The Participant who joined

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
myConversation.on('participantConnected', function(participant) {
+  console.log(participant.identity + ' joined the Conversation');
+
+  // Get the participant's Media,
+  var participantMedia = participant.media;
+
+  // And attach it to your application's view.
+  var participantView = document.getElementById('participant-view');
+  participantMedia.attach(participantView);
+  participantVideos.appendChild(participantView);
+});
+ + + + + + + + +

participantDisconnected

+ + + + + +
+

A Participant left the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
participant + + +Participant + + + +

The Participant who left

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
myConversation.on('participantDisconnected', function(participant) {
+  console.log(participant.identity + ' left the Conversation');
+});
+ + + + + + + + +

participantFailed

+ + + + + +
+

A Participant failed to join Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
participant + + +Participant + + + +

The Participant that failed to join

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Example
+ +
myConversation.on('participantFailed', function(participant) {
+  console.log(participant.identity + ' failed to join the Conversation');
+});
+ + + + + + + + +

trackAdded

+ + + + + +
+

A Track was added by a Participant in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was added

participant + + +Participant + + + +

The Participant who added the + Track

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDimensionsChanged

+ + + + + +
+

One of the Participant's VideoTrack's dimensions changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

participant + + +Participant + + + +

The Participant whose VideoTrack's + dimensions changed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDisabled

+ + + + + +
+

A Track was disabled by a Participant in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

participant + + +Participant + + + +

The Participant who disabled the + Track

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnabled

+ + + + + +
+

A Track was enabled by a Participant in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

participant + + +Participant + + + +

The Participant who enabled the + Track

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnded

+ + + + + +
+

One of a Participant's Tracks in the Conversation ended.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

participant + + +Participant + + + +

The Participant whose Track ended

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackRemoved

+ + + + + +
+

A Track was removed by a Participant in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was removed

participant + + +Participant + + + +

The Participant who removed the + Track

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackStarted

+ + + + + +
+

One of a Participant's Tracks in the Conversation started.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

participant + + +Participant + + + +

The Participant whose Track started

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/IncomingInvite.html b/dist/docs/IncomingInvite.html new file mode 100644 index 000000000..8ccadbf3d --- /dev/null +++ b/dist/docs/IncomingInvite.html @@ -0,0 +1,1378 @@ + + + + + JSDoc: Class: IncomingInvite + + + + + + + + + + +
+ +

Class: IncomingInvite

+ + + + + + +
+ +
+ +

IncomingInvite

+ +

An IncomingInvite to a Conversation can be accepted or +rejected. +

+IncomingInvites are returned by Client#event:invite.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
conversationSid + + +Conversation.SID + + + +

The SID of the Conversation + this IncomingInvite invites to

from + + +string + + + +

The identity of the Participant that sent this + IncomingInvite

participants + + +Array.<string> + + + +

The identities of the Participants currently in the Conversation

status + + +string + + + +

The status of this IncomingInvite, either + "accepting", "accepted", "rejected", "canceled", "failed", or "pending"

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + + + + +

accept(optionsopt) → {Promise.<Conversation>}

+ + + + + +
+

Accept the IncomingInvite and join the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
options + + +IncomingInvite.AcceptOptions + + + + + + <optional>
+ + + + + +
+ + {localStreamConstraints:{audio:true,video:true}} + +

Options to override + IncomingInvite#accept's default behavior

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<Conversation> + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+client.on('invite', function(invite) {
+  console.log('Received IncomingInvite to join a Conversation with ' + invite.from);
+
+  // By default, accept will request the microphone and camera for you.
+  invite.accept();
+});
+ + + + + + + + +

reject() → {this}

+ + + + + +
+

Reject the IncomingInvite to a Conversation.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + +
Example
+ +
var initialToken = getAccessToken();
+var manager = new Twilio.AccessManager(initialToken);
+var client = new Twilio.Conversations.Client(manager);
+
+client.on('invite', function(invite) {
+  console.log('Rejecting IncomingInvite to join a Conversation with ' + invite.from);
+  invite.reject();
+});
+ + + + + + + +

Type Definitions

+ + + +

AcceptOptions

+ + + + +
+

You may pass these options to IncomingInvite#accept to +override the default behavior.

+
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
localMedia + + +LocalMedia + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + LocalMedia object when accepting an IncomingInvite

localStream + + +MediaStream + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + MediaStream when accepting an IncomingInvite

localStreamConstraints + + +object + + + + + + <optional>
+ + + + <nullable>
+ +
+ + {audio:true,video:true} + +

Set to + override the parameters passed to getUserMedia when neither + localMedia nor localStream are provided

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

accepted

+ + + + + +
+

The IncomingInvite was accepted, and the Client is now +participating in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +IncomingInvite + + + +

The IncomingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

canceled

+ + + + + +
+

The IncomingInvite was canceled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +IncomingInvite + + + +

The IncomingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

failed

+ + + + + +
+

The IncomingInvite failed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +IncomingInvite + + + +

The IncomingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

rejected

+ + + + + +
+

The IncomingInvite was rejected.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +IncomingInvite + + + +

The IncomingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/LocalAudioTrack.html b/dist/docs/LocalAudioTrack.html new file mode 100644 index 000000000..14a7ca777 --- /dev/null +++ b/dist/docs/LocalAudioTrack.html @@ -0,0 +1,1463 @@ + + + + + JSDoc: Class: LocalAudioTrack + + + + + + + + + + +
+ +

Class: LocalAudioTrack

+ + + + + + +
+ +
+ +

LocalAudioTrack

+ +

A LocalAudioTrack is an AudioTrack representing +audio that your Client sends to a Conversation.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the AudioTrack to a newly created <audio> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteAudioEl = audioTrack.attach();
+document.getElementById('div#remote-audio-container').appendChild(remoteAudioEl);
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the AudioTrack from any and all previously attached <audio> elements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedAudioEls = audioTrack.detach();
+ + + + + + + + +

disable() → {this}

+ + + + + +
+

Disable the LocalAudioTrack. This is effectively "mute".

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable(enabledopt) → {this}

+ + + + + +
+

Enable or disable the LocalAudioTrack. This is effectively "unmute" or +"mute".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
enabled + + +boolean + + + + + + <optional>
+ + + + + +

Specify false to mute the LocalAudioTrack

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable() → {this}

+ + + + + +
+

Enable the LocalAudioTrack. This is effectively "unmute".

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

stop() → {this}

+ + + + + +
+

Stop sending this LocalTrack.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + + +

Events

+ + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/LocalMedia.html b/dist/docs/LocalMedia.html new file mode 100644 index 000000000..41df97c8b --- /dev/null +++ b/dist/docs/LocalMedia.html @@ -0,0 +1,3892 @@ + + + + + JSDoc: Class: LocalMedia + + + + + + + + + + +
+ +

Class: LocalMedia

+ + + + + + +
+ +
+ +

LocalMedia

+ +

A LocalMedia object is a Media object representing + LocalAudioTracks and LocalVideoTracks that your Client may + share in a Conversation.

+ + +
+ +
+
+ + + + +

Constructor

+ + +

new Twilio.Conversations.LocalMedia()

+ + + + + +
+

Construct a LocalMedia object.

+
+ + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
audioTracks + + +Map.<Track.ID, LocalAudioTrack> + + + +

The LocalAudioTracks on + this Media object

tracks + + +Map.<Track.ID, LocalTrack> + + + +

The LocalAudioTracks and + LocalVideoTracks on this Media object

videoTracks + + +Map.<Track.ID, LocalVideoTrack> + + + +

The LocalVideoTracks on + this Media object

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

(static) getLocalMedia(optionsopt, nullable) → {Promise.<LocalMedia>}

+ + + + + +
+

Get LocalMedia. By default, this requests a +LocalAudioTrack and a LocalVideoTrack representing a microphone and +camera. +

+This method calls getUserMedia internally. Pass in +options to override the default behavior.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
options + + +LocalMedia.GetLocalMediaOptions + + + + + + <optional>
+ + + + <nullable>
+ + + +
+ + {localStreamConstraints:{audio:true,video:true}} + +

Options to override + LocalMedia.getLocalMedia's default behavior

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<LocalMedia> + + +
+
+ + + + + + + + + + +

addCamera() → {Promise.<LocalVideoTrack>}

+ + + + + +
+

Adds a LocalVideoTrack representing your browser's camera to the +LocalMedia object, if not already added. +

+Internally, this calls getUserMedia({ video: true }).

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<LocalVideoTrack> + + +
+
+ + + + + + + + + + +

addMicrophone() → {Promise.<LocalAudioTrack>}

+ + + + + +
+

Adds a LocalAudioTrack representing your browser's microphone to the +LocalMedia object, if not already added. +

+Internally, this calls getUserMedia({ audio: true }).

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<LocalAudioTrack> + + +
+
+ + + + + + + + + + +

addStream(mediaStream) → {this}

+ + + + + +
+

Add a MediaStream to the LocalMedia object, constructing +LocalTracks as necessary for each MediaStreamTrack contained +within.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
mediaStream + + +MediaStream + + + +

The MediaStream to add

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

addTrack(track) → {this}

+ + + + + +
+

Adds a LocalTrack to the LocalMedia object, if not already added.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +LocalTrack + + + +

The LocalTrack to add

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the Media to a newly created <div> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteMediaEl = media.attach();
+document.getElementById('div#remote-media-container').appendChild(remoteMediaEl);
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the Media from any and all previously attached HTMLElements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedMediaEls = media.detach();
+ + + + + + + + +

mute(enablednullable) → {this}

+ + + + + +
+

Disable or enable every LocalAudioTrack on this LocalMedia object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
enabled + + +boolean + + + + + + + + <nullable>
+ + + +

Specify false to enable the LocalAudioTracks

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

mute() → {this}

+ + + + + +
+

Disable every LocalAudioTrack on this LocalMedia object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

pause(enablednullable) → {this}

+ + + + + +
+

Disable or enable every LocalVideoTrack on this LocalMedia object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
enabled + + +boolean + + + + + + + + <nullable>
+ + + +

Specify false to enable the LocalVideoTracks

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

pause() → {this}

+ + + + + +
+

Disable every LocalVideoTrack on this LocalMedia object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

removeCamera() → (nullable) {LocalVideoTrack}

+ + + + + +
+

Removes the LocalVideoTrack representing your browser's camera, if it +has been added.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +LocalVideoTrack + + +
+
+ + + + + + + + + + +

removeMicrophone() → (nullable) {LocalAudioTrack}

+ + + + + +
+

Removes the LocalAudioTrack representing your browser's microphone, if it +has been added.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +LocalAudioTrack + + +
+
+ + + + + + + + + + +

removeStream(mediaStream, stopopt, nullable) → {this}

+ + + + + +
+

Remove a MediaStream from the LocalMedia object. This +will remove any LocalTracks corresponding to +MediaStreamTracks contained within the MediaStream.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
mediaStream + + +MediaStream + + + + + + + + + + + +

The MediaStream to remove

stop + + +boolean + + + + + + <optional>
+ + + + <nullable>
+ + + +
+ + true + +

Whether or not to call + LocalTrack#stop on the corresponding LocalTracks

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

removeTrack(track, stopopt, nullable) → {this}

+ + + + + +
+

Removes a LocalTrack from the LocalMedia object, if it was added.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
track + + +LocalTrack + + + + + + + + + + + +

The LocalTrack to remove

stop + + +boolean + + + + + + <optional>
+ + + + <nullable>
+ + + +
+ + true + +

Whether or not to call + LocalTrack#stop

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

stop() → {this}

+ + + + + +
+

Stop all LocalAudioTracks and LocalVideoTracks on this LocalMedia object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

unmute() → {this}

+ + + + + +
+

Enable every LocalAudioTrack on this LocalMedia object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

unpause() → {this}

+ + + + + +
+

Enable every LocalVideoTrack on this LocalMedia object.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + +

Type Definitions

+ + + +

GetLocalMediaOptions

+ + + + +
+

You may pass these options to LocalMedia.getLocalMedia to +override the default behavior.

+
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
localMedia + + +LocalMedia + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + LocalMedia object

localStream + + +MediaStream + + + + + + <optional>
+ + + + <nullable>
+ +
+ + null + +

Set to reuse an existing + MediaStream

localStreamConstraints + + +object + + + + + + <optional>
+ + + + <nullable>
+ +
+ + {audio:true,video:true} + +

Set to + override the parameters passed to getUserMedia when neither + localMedia nor localStream are provided

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

trackAdded

+ + + + + +
+

A Track was added to this Media object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was added

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDimensionsChanged

+ + + + + +
+

The dimensions of a VideoTrack on this Media object changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDisabled

+ + + + + +
+

A Track on this Media object was disabled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnabled

+ + + + + +
+

A Track on this Media object was enabled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnded

+ + + + + +
+

A Track on this Media object ended.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackRemoved

+ + + + + +
+

A Track was removed from this Media object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was removed

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackStarted

+ + + + + +
+

A Track on this Media object was started.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was started

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/LocalTrack.html b/dist/docs/LocalTrack.html new file mode 100644 index 000000000..beed5f050 --- /dev/null +++ b/dist/docs/LocalTrack.html @@ -0,0 +1,1214 @@ + + + + + JSDoc: Class: LocalTrack + + + + + + + + + + +
+ +

Class: LocalTrack

+ + + + + + +
+ +
+ +

LocalTrack

+ +

A LocalTrack represents audio or video that your +Client is sending to a Conversation. As such, it can be +enabled and disabled with LocalTrack#enable and +LocalTrack#disable or stopped completely with +LocalTrack#stop.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

disable() → {this}

+ + + + + +
+

Disable the LocalTrack.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable(enabledopt) → {this}

+ + + + + +
+

Enable or disable the LocalTrack.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
enabled + + +boolean + + + + + + <optional>
+ + + + + +

Specify false to disable the LocalTrack

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable() → {this}

+ + + + + +
+

Enable the LocalTrack.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

stop() → {this}

+ + + + + +
+

Stop sending this LocalTrack.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + + +

Events

+ + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/LocalVideoTrack.html b/dist/docs/LocalVideoTrack.html new file mode 100644 index 000000000..1309bc723 --- /dev/null +++ b/dist/docs/LocalVideoTrack.html @@ -0,0 +1,1775 @@ + + + + + JSDoc: Class: LocalVideoTrack + + + + + + + + + + +
+ +

Class: LocalVideoTrack

+ + + + + + +
+ +
+ +

LocalVideoTrack

+ +

A LocalVideoTrack is a VideoTrack representing +audio that your Client sends to a Conversation.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the VideoTrack to a newly created <video> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteVideoEl = videoTrack.attach();
+document.getElementById('div#remote-video-container').appendChild(remoteVideoEl);
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the VideoTrack from any and all previously attached <video> elements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedVideoEls = videoTrack.detach();
+ + + + + + + + +

disable() → {this}

+ + + + + +
+

Disable the LocalVideoTrack. This is effectively "pause".

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable() → {this}

+ + + + + +
+

Enable the LocalVideoTrack. This is effectively "unpause".

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

enable(enabledopt) → {this}

+ + + + + +
+

Enable or disable the LocalVideoTrack. This is effectively "unpause" or +"pause".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
enabled + + +boolean + + + + + + <optional>
+ + + + + +

Specify false to pause the LocalVideoTrack

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + +

stop() → {this}

+ + + + + +
+

Stop sending this LocalTrack.

+
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + +

Type Definitions

+ + + +

Dimensions

+ + + + +
+

A VideoTrack's width and height.

+
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
width + + +number + + + + + + + + <nullable>
+ +

The VideoTrack's width or null if the + VideoTrack has not yet started

height + + +number + + + + + + + + <nullable>
+ +

The VideoTrack's height or null if the + VideoTrack has not yet started

+ + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

dimensionsChanged

+ + + + + +
+

The VideoTrack's dimensions changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/Media.html b/dist/docs/Media.html new file mode 100644 index 000000000..6004a95cd --- /dev/null +++ b/dist/docs/Media.html @@ -0,0 +1,2137 @@ + + + + + JSDoc: Class: Media + + + + + + + + + + +
+ +

Class: Media

+ + + + + + +
+ +
+ +

Media

+ +

A Media object contains a number of AudioTracks + and VideoTracks. You can call Media#attach with a + <div> to automatically update your application's user interface with + <audio> and <video> elements as Tracks are added and + removed.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
attachments + + +Map.<HTMLElement, Map.<Track, HTMLElement>> + + + +

A Map + from <div> elements to a Map from Tracks to their attached + HTMLElements (managed by Media#attach and Media#detach)

audioTracks + + +Map.<Track.ID, AudioTrack> + + + +

The AudioTracks on + this Media object

isMuted + + +boolean + + + +

True if every AudioTrack on this + Media object is disabled

isPaused + + +boolean + + + +

True if every VideoTrack on this + Media object is disabled

mediaStreams + + +Set.<MediaStream> + + + +

The MediaStreams associated with + the Tracks on this Media object

tracks + + +Map.<Track.ID, Track> + + + +

The AudioTracks and + VideoTracks on this Media object

videoTracks + + +Map.<Track.ID, VideoTrack> + + + +

The VideoTracks on + this Media object

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + + + + +

attach(el) → {HTMLElement}

+ + + + + +
+

Attach the Media to an existing HTMLElement.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
el + + +HTMLElement + + + +

The HTMLElement to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteMediaEl = document.getElementById('remote-media');
+media.attach(remoteMediaEl);
+ + + + + + + + +

attach(selector) → {HTMLElement}

+ + + + + +
+

Attach the Media to an HTMLElement selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the HTMLElement to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteMediaEl = media.attach('div#remote-media');
+ + + + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the Media to a newly created <div> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteMediaEl = media.attach();
+document.getElementById('div#remote-media-container').appendChild(remoteMediaEl);
+ + + + + + + + +

detach(selector) → {HTMLElement}

+ + + + + +
+

Detach the Media from a previously attached HTMLElement selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the HTMLElement to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var detachedMediaEl = media.detach('div#remote-media');
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the Media from any and all previously attached HTMLElements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedMediaEls = media.detach();
+ + + + + + + + +

detach(el) → {HTMLElement}

+ + + + + +
+

Detach the Media from a previously attached HTMLElement.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
el + + +HTMLElement + + + +

The HTMLElement to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteMediaEl = document.getElementById('remote-media');
+media.detach(remoteMediaEl);
+ + + + + + + + + +

Events

+ + + + + + +

trackAdded

+ + + + + +
+

A Track was added to this Media object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was added

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDimensionsChanged

+ + + + + +
+

The dimensions of a VideoTrack on this Media object changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDisabled

+ + + + + +
+

A Track on this Media object was disabled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnabled

+ + + + + +
+

A Track on this Media object was enabled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnded

+ + + + + +
+

A Track on this Media object ended.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackRemoved

+ + + + + +
+

A Track was removed from this Media object.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was removed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackStarted

+ + + + + +
+

A Track on this Media object was started.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was started

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/OutgoingInvite.html b/dist/docs/OutgoingInvite.html new file mode 100644 index 000000000..e7f86224a --- /dev/null +++ b/dist/docs/OutgoingInvite.html @@ -0,0 +1,905 @@ + + + + + JSDoc: Class: OutgoingInvite + + + + + + + + + + +
+ +

Class: OutgoingInvite

+ + + + + + +
+ +
+ +

OutgoingInvite

+ +

An OutgoingInvite is a Promise that eventually resolves + to a Conversation if one or more Participants accept the + corresponding IncomingInvite. An OutgoingInvite may be + canceled up until a Participant has accepted it. +

+ OutgoingInvites are returned by Client#inviteToConversation.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
status + + +string + + + +

The status of this OutgoingInvite, either + "accepted", "rejected", "canceled", "failed", or "pending"

to + + +Array.<string> + + + +

The Participant identities + invited by this OutgoingInvite

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

cancel() → {this}

+ + + + + +
+

Attempt to cancel the OutgoingInvite.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +this + + +
+
+ + + + + + + + + + + +

Events

+ + + + + + +

accepted

+ + + + + +
+

The OutgoingInvite was accepted, and the Client is now +participating in the Conversation.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +OutgoingInvite + + + +

The OutgoingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

canceled

+ + + + + +
+

The OutgoingInvite was canceled.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +OutgoingInvite + + + +

The OutgoingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

failed

+ + + + + +
+

The OutgoingInvite failed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +OutgoingInvite + + + +

The OutgoingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

rejected

+ + + + + +
+

The OutgoingInvite was rejected.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
invite + + +OutgoingInvite + + + +

The OutgoingInvite

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/Participant.html b/dist/docs/Participant.html new file mode 100644 index 000000000..11d3f5abc --- /dev/null +++ b/dist/docs/Participant.html @@ -0,0 +1,1282 @@ + + + + + JSDoc: Class: Participant + + + + + + + + + + +
+ +

Class: Participant

+ + + + + + +
+ +
+ +

Participant

+ +

A Participant represents a remote Client in a +Conversation.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + +

The identity of the Participant

media + + +Media + + + +

The Media this Participant is sharing, if any

sid + + +Participant.SID + + + +

The Participant's SID

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Type Definitions

+ + + +

SID

+ + + + +
+

A Participant.SID is a 34-character string starting with "PA" +that uniquely identifies a Participant.

+
+ + + +
Type:
+
    +
  • + +string + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

trackAdded

+ + + + + +
+

A Track was added by the Participant.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was added

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDimensionsChanged

+ + + + + +
+

One of the Participant's VideoTrack's dimensions changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackDisabled

+ + + + + +
+

A Track was disabled by the Participant.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnabled

+ + + + + +
+

A Track was enabled by the Participant.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackEnded

+ + + + + +
+

One of the Participant's Tracks ended.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackRemoved

+ + + + + +
+

A Track was removed by the Participant.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was removed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

trackStarted

+ + + + + +
+

One of the Participant's Tracks started.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/Track.html b/dist/docs/Track.html new file mode 100644 index 000000000..1bcec242b --- /dev/null +++ b/dist/docs/Track.html @@ -0,0 +1,982 @@ + + + + + JSDoc: Class: Track + + + + + + + + + + +
+ +

Class: Track

+ + + + + + +
+ +
+ +

Track

+ +

A Track represents audio or video that can be sent to or +received from a Conversation. Tracks abstract away the notion +of MediaStream and MediaStreamTrack.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id + + +Track.ID + + + +

This Track's ID

isEnded + + +boolean + + + +

Whether or not the Track has ended

isStarted + + +boolean + + + +

Whether or not the Track has started

isEnabled + + +boolean + + + +

Whether or not the Track is enabled + (i.e., whether it is paused or muted)

kind + + +string + + + +

The kind of the underlying + MediaStreamTrack; e.g. "audio" or "video"

mediaStream + + +MediaStream + + + +

The underlying MediaStream

mediaStreamTrack + + +MediaStreamTrack + + + +

The underlying + MediaStreamTrack

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +

Type Definitions

+ + + +

ID

+ + + + +
+

The Track ID is a string identifier for the Track.

+
+ + + +
Type:
+
    +
  • + +string + + +
  • +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/VideoTrack.html b/dist/docs/VideoTrack.html new file mode 100644 index 000000000..e3e7d1da5 --- /dev/null +++ b/dist/docs/VideoTrack.html @@ -0,0 +1,1950 @@ + + + + + JSDoc: Class: VideoTrack + + + + + + + + + + +
+ +

Class: VideoTrack

+ + + + + + +
+ +
+ +

VideoTrack

+ +

A VideoTrack is a Track representing video.

+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
attachments + + +Set.<HTMLElement> + + + +

The <video> elements this + VideoTrack is currently attached to (managed by + VideoTrack#attach)

dimensions + + +VideoTrack#Dimensions + + + +

The VideoTrack's VideoTrack#Dimensions

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
Fires:
+ + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + +

attach(video) → {HTMLElement}

+ + + + + +
+

Attach the VideoTrack to an existing <video> element.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
video + + +HTMLElement + + + +

The <video> element to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteVideoEl = document.getElementById('remote-video');
+videoTrack.attach(remoteVideoEl);
+ + + + + + + + +

attach() → {HTMLElement}

+ + + + + +
+

Attach the VideoTrack to a newly created <video> element.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteVideoEl = videoTrack.attach();
+document.getElementById('div#remote-video-container').appendChild(remoteVideoEl);
+ + + + + + + + +

attach(selector) → {HTMLElement}

+ + + + + +
+

Attach the VideoTrack to a <video> element selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the <video> element to attach to

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteVideoEl = videoTrack.attach('video#remote-video');
+ + + + + + + + +

detach(selector) → {HTMLElement}

+ + + + + +
+

Detach the VideoTrack from a previously attached <video> element selected by +document.querySelector.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selector + + +string + + + +

A query selector for the <video> element to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var detachedVideoEl = media.detach('div#remote-video');
+ + + + + + + + +

detach(video) → {HTMLElement}

+ + + + + +
+

Detach the VideoTrack from a previously attached <video> element.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
video + + +HTMLElement + + + +

The <video> element to detach from

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +HTMLElement + + +
+
+ + + + +
Example
+ +
var remoteVideoEl = document.getElementById('remote-video');
+videoTrack.detach(remoteVideoEl);
+ + + + + + + + +

detach() → {Array.<HTMLElement>}

+ + + + + +
+

Detach the VideoTrack from any and all previously attached <video> elements.

+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Array.<HTMLElement> + + +
+
+ + + + +
Example
+ +
var detachedVideoEls = videoTrack.detach();
+ + + + + + + +

Type Definitions

+ + + +

Dimensions

+ + + + +
+

A VideoTrack's width and height.

+
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
width + + +number + + + + + + + + <nullable>
+ +

The VideoTrack's width or null if the + VideoTrack has not yet started

height + + +number + + + + + + + + <nullable>
+ +

The VideoTrack's height or null if the + VideoTrack has not yet started

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Events

+ + + + + + +

dimensionsChanged

+ + + + + +
+

The VideoTrack's dimensions changed.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +VideoTrack + + + +

The VideoTrack whose dimensions changed

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

disabled

+ + + + + +
+

The Track was disabled. For AudioTracks this means +"muted", and for VideoTracks this means "paused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was disabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

enabled

+ + + + + +
+

The Track was enabled. For AudioTracks this means +"unmuted", and for VideoTracks this means "unpaused".

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that was enabled

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

ended

+ + + + + +
+

The Track ended. This means that the Track will no longer +playback audio or video.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that ended

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +

started

+ + + + + +
+

The Track started. This means that the Track contains +enough audio or video to begin playback.

+
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
track + + +Track + + + +

The Track that started

+ + + + + + +
+ + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/client.js.html b/dist/docs/client.js.html new file mode 100644 index 000000000..f7b6fcfb7 --- /dev/null +++ b/dist/docs/client.js.html @@ -0,0 +1,479 @@ + + + + + JSDoc: Source: client.js + + + + + + + + + + +
+ +

Source: client.js

+ + + + + + +
+
+
'use strict';
+
+var inherits = require('util').inherits;
+
+var AccessManager = require('twilio-common').AccessManager;
+var constants = require('./util/constants');
+var E = constants.twilioErrors;
+var EventEmitter = require('events').EventEmitter;
+var IncomingInvite = require('./incominginvite');
+var Log = require('./util/log');
+var OutgoingInvite = require('./outgoinginvite');
+var SIPJSUserAgent = require('./signaling/sipjsuseragent');
+var StatsReporter = require('./statsreporter');
+var util = require('./util');
+
+/**
+ * Constructs a new {@link Client} with an AccessManager. Alternatively, you
+ * can pass an Access Token string and the {@link Client} will construct an
+ * AccessManager for you. AccessManager is provided by twilio-common.js, which
+ * must be included alongside twilio-conversations.js.
+ * @class
+ * @classdesc Construct a {@link Client} to start creating and participating
+ *   in {@link Conversation}s with other {@link Participant}s.
+ * @param {AccessManager|string} managerOrToken - The {@link Client}'s AccessManager or an Access Token string to use when constructing an AccessManager
+ * @param {Client.ConstructorOptions} [options] - Options to override the
+ *   constructor's default behavior
+ * @property {AccessManager} accessManager - The {@link Client}'s AccessManager
+ * @property {string} identity - The {@link Client}'s identity
+ * @property {Map<Conversation.SID, Conversation>} conversations - The {@link Conversation}s this
+ *   {@link Client} is participating in
+ * @property {bool} isListening - Whether the {@link Client} is listening for
+ *   {@link IncomingInvite}s to {@link Conversation}s
+ * @fires Client#invite
+ * @fires Client#error
+ */
+function Client(accessManager, options) {
+  if (!(this instanceof Client)) {
+    return new Client(accessManager, options);
+  }
+
+  if (typeof accessManager === 'string') {
+    accessManager = new AccessManager(accessManager);
+  }
+
+  var self = this;
+  EventEmitter.call(this);
+
+  options = util.withDefaults(options, {
+    eventGateway: constants.EVENT_GATEWAY,
+    logLevel: constants.DEFAULT_LOG_LEVEL,
+    useConversationEvents: true,
+    userAgent: SIPJSUserAgent
+  });
+
+  var logLevel = options.logLevel;
+
+  if (options.debug === true) {
+    logLevel = 'debug';
+    console.warn('Warning: ClientOptions.debug is deprecated and will be removed in a future release. Please see ClientOptions.logLevel.');
+  }
+
+  var log = new Log('Client', logLevel);
+
+  var conversations = new Map();
+  var eventGateway = options.eventGateway;
+  var incomingInvites = new Map();
+  var rejectedIncomingInvites = new Map();
+  var isListening = false;
+  var outgoingInvites = new Map();
+  var canceledOutgoingInvites = new Map();
+
+  var UserAgent = options.userAgent;
+  var userAgent = new UserAgent(accessManager, options);
+
+  userAgent.on('invite', function onInviteServerTransaction(inviteServerTransaction) {
+    var cookie = inviteServerTransaction.cookie;
+    var outgoingInvite = outgoingInvites.get(cookie) || canceledOutgoingInvites.get(cookie);
+    if (outgoingInvite) {
+      return outgoingInvite._onInviteServerTransaction(inviteServerTransaction);
+    }
+
+    var key = inviteServerTransaction.key;
+    var incomingInvite = incomingInvites.get(key) || rejectedIncomingInvites.get(key);
+    if (incomingInvite) {
+      return incomingInvite._onInviteServerTransaction(inviteServerTransaction);
+    }
+
+    var conversation = conversations.get(inviteServerTransaction.conversationSid);
+    if (conversation) {
+      return conversation._onInviteServerTransaction(inviteServerTransaction);
+    }
+
+    self._onInviteServerTransaction(inviteServerTransaction);
+  });
+
+  userAgent.on('dialogCreated', function(dialog) {
+    new StatsReporter(eventGateway, dialog, logLevel);
+  });
+
+  userAgent.on('keepAliveTimeout', function() {
+    self.emit('error', E.GATEWAY_DISCONNECTED);
+  });
+
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    _canceledOutgoingInvites: {
+      value: canceledOutgoingInvites
+    },
+    _incomingInvites: {
+      value: incomingInvites
+    },
+    _isListening: {
+      set: function(_isListening) {
+        isListening = _isListening;
+      }
+    },
+    _isRegistered: {
+      enumerable: true,
+      get: function() {
+        return this._userAgent.isRegistered;
+      }
+    },
+    _log: {
+      value: log
+    },
+    _logLevel: {
+      value: logLevel
+    },
+    _needsRegistration: {
+      get: function() {
+        return isListening
+          || outgoingInvites.size
+          || incomingInvites.size
+          || conversations.size;
+      }
+    },
+    _options: {
+      value: options
+    },
+    _outgoingInvites: {
+      value: outgoingInvites
+    },
+    _rejectedIncomingInvites: {
+      value: rejectedIncomingInvites
+    },
+    _userAgent: {
+      value: userAgent
+    },
+    accessManager: {
+      enumerable: true,
+      value: accessManager
+    },
+    conversations: {
+      enumerable: true,
+      value: conversations
+    },
+    identity: {
+      enumerable: true,
+      get: function() {
+        return accessManager.identity;
+      }
+    },
+    isListening: {
+      enumerable: true,
+      get: function() {
+        return isListening;
+      }
+    }
+  });
+
+  accessManager.on('tokenUpdated', this._register.bind(this, true));
+
+  return this;
+}
+
+inherits(Client, EventEmitter);
+
+Client.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) {
+  // If not listening, do nothing. Do not even reject, as that would cause
+  // other listening Clients to be unable to accept the
+  // InviteServerTransaction.
+  if (!this.isListening) {
+    return;
+  }
+
+  var incomingInvite = new IncomingInvite(inviteServerTransaction, this._options);
+  var key = inviteServerTransaction.key;
+  this._incomingInvites.set(key, incomingInvite);
+
+  var self = this;
+
+  // If accepted, the Client has joined the Conversation and will pass any
+  // InviteServerTransactions with matching Conversation SID to the
+  // Conversation in order to be accepted or rejected.
+  incomingInvite.once('accepted', function incomingInviteAccepted() {
+    self._incomingInvites.delete(key);
+
+    var conversation = incomingInvite._conversation;
+    self.conversations.set(conversation.sid, conversation);
+
+    conversation.once('disconnected', function() {
+      self.conversations.delete(conversation.sid);
+      self._unregisterIfNotNeeded();
+    });
+  });
+
+  incomingInvite.once('canceled', function incomingInviteCanceled() {
+    self._incomingInvites.delete(key);
+    self._unregisterIfNotNeeded();
+  });
+
+  // If rejected, the Client remembers the IncomingInvite until all the
+  // InviteServerTransactions with matching Conversation SID and Participant
+  // SID would have been received and passes them to the IncomingInvite in
+  // order to be rejected.
+  incomingInvite.once('rejected', function incomingInviteRejected() {
+    self._incomingInvites.delete(key);
+    self._rejectedIncomingInvites.set(key, incomingInvite);
+
+    setTimeout(function deleteRejectedIncomingInvite() {
+      self._rejectedIncomingInvites.delete(key);
+    }, 3 * constants.DEFAULT_CALL_TIMEOUT);
+
+    self._unregisterIfNotNeeded();
+  });
+
+  this.emit('invite', incomingInvite);
+};
+
+/**
+ * Causes this {@link Client} to stop listening for {@link IncomingInvite}s to
+ *   {@link Conversation}s until {@link Client#listen} is called again.
+ * @returns {this}
+ */
+Client.prototype.unlisten = function unlisten() {
+  this._isListening = false;
+  this._unregisterIfNotNeeded();
+  return this;
+};
+
+Client.prototype._unregister = function _unregister() {
+  if (!this._isRegistered) {
+    return Promise.resolve(this);
+  }
+  var self = this;
+  return this._userAgent.unregister().then(function() {
+    self._isListening = false;
+    return self;
+  });
+};
+
+/**
+ * Causes this {@link Client} to start listening for {@link IncomingInvite}s to
+ *   {@link Conversation}s.
+ * @returns {Promise<this>}
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var alice = new Twilio.Conversations.Client(manager);
+ *
+ * alice.listen().then(function() {
+ *   console.log('Alice is listening');
+ * }, function(error) {
+ *   console.error(error);
+ * });
+ */
+Client.prototype.listen = function listen() {
+  var self = this;
+  return this._register().then(function() {
+    self._isListening = true;
+    return self;
+  });
+};
+
+Client.prototype._register = function _register(reregister) {
+  var self = this;
+  if (!reregister && this._isRegistered) {
+    return Promise.resolve(this);
+  }
+  return this._userAgent.register().then(function onRegistered() {
+    return self;
+  }, function onRegisterFailed(response) {
+    var gatewayMessage = util.getOrNull(response, 'headers.X-Twilio-Error.0.raw');
+    var sipMessage = response.cause;
+
+    if (sipMessage) {
+      self._log.throw(E.LISTEN_FAILED, 'Received SIP error: ' + sipMessage);
+    } else if (gatewayMessage) {
+      self._log.throw(E.LISTEN_FAILED, 'Gateway responded with: ' + gatewayMessage);
+    } else {
+      self._log.throw(E.LISTEN_FAILED, response.message || response);
+    }
+  });
+};
+
+/**
+ * Invite remote {@link Client}s to join a {@link Conversation}.
+ *   <br><br>
+ *   By default, this will attempt to setup an {@link AudioTrack} and
+ *   {@link VideoTrack} between local and remote {@link Client}s. You can
+ *   override this by specifying <code>options</code>.
+ * @param {Array<string>|string} participants - {@link Participant} identities to invite to the {@link Conversation}
+ * @param {Client.InviteToConversationOptions}
+ *   [options={localStreamConstraints:{audio:true,video:true}}] - Options to override
+ *   {@link Client#inviteToConversation}'s default behavior
+ * @returns {OutgoingInvite}
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ * client.inviteToConversation(['bob', 'charlie']).then(function(conversation) {
+ *   conversation.on('participantConnected', function(participant) {
+ *     console.log(participant.identity + ' has connected');
+ *   });
+ * });
+ */
+Client.prototype.inviteToConversation = function inviteToConversation(participants, options) {
+  options = util.withDefaults({ }, options, this._options);
+
+  if (!participants) {
+    this._log.throw(E.INVALID_ARGUMENT, 'No Participant identities were provided');
+  }
+
+  participants = participants.forEach ? participants : [participants];
+  util.validateAddresses(this.accessManager._tokenPayload.sub, participants);
+
+  // Save a reference to the OutgoingInvite; the Client will pass any
+  // InviteServerTransactions with matching cookie to the OutgoingInvite
+  // in order to be accepted or rejected.
+  var outgoingInvite = new OutgoingInvite(this._userAgent, participants, options);
+  var cookie = outgoingInvite._cookie;
+  this._outgoingInvites.set(cookie, outgoingInvite);
+
+  var self = this;
+
+  // If accepted, the Client has joined the Conversation and will pass any
+  // InviteServerTransactions with matching Conversation SID to the
+  // Conversation in order to be accepted or rejected.
+  outgoingInvite.once('accepted', function() {
+    self._outgoingInvites.delete(cookie);
+
+    var conversation = outgoingInvite._conversation;
+    self.conversations.set(conversation.sid, conversation);
+
+    conversation.once('disconnected', function() {
+      self.conversations.delete(conversation.sid);
+      self._unregisterIfNotNeeded();
+    });
+  });
+
+  // If canceled, the Client remembers the OutgoingInvite until all the
+  // InviteServerTransactions with matching cookies would have been received
+  // and passes them to the OutgoingInvite in order to be rejected.
+  outgoingInvite.once('canceled', function outgoingInviteCanceled() {
+    self._outgoingInvites.delete(cookie);
+    self._canceledOutgoingInvites.set(cookie, outgoingInvite);
+
+    setTimeout(function deleteCanceledOutgoingInvite() {
+      self._canceledOutgoingInvites.delete(cookie);
+    }, constants.DEFAULT_CALL_TIMEOUT * 3);
+
+    self._unregisterIfNotNeeded();
+  });
+
+  outgoingInvite.once('rejected', function() {
+    self._outgoingInvites.delete(outgoingInvite._cookie);
+    self._unregisterIfNotNeeded();
+  });
+
+  return outgoingInvite;
+};
+
+Client.prototype._unregisterIfNotNeeded = function _unregisterIfNotNeeded() {
+  return this._needsRegistration ? Promise.resolve(this) : this._unregister();
+};
+
+Object.freeze(Client.prototype);
+
+/**
+ * Your {@link Client} has run into an error.
+ * @param {Error} error - The Error
+ * @event Client#error
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ * client.on('error', function(error) {
+ *  console.error(error);
+ * });
+ */
+
+/**
+ * Your {@link Client} has received an {@link IncomingInvite} to participant in a
+ * {@link Conversation}.
+ * @param {IncomingInvite} invite - the {@link IncomingInvite}
+ * @event Client#invite
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ * client.on('invite', function(invite) {
+ *   console.log('Received an IncomingInvite to join a Conversation from ' + invite.from);
+ * });
+ */
+
+/**
+ * You may pass these options to {@link Client}'s constructor to override
+ * its default behavior.
+ * @typedef {object} Client.ConstructorOptions
+ * @property {string} [logLevel='warn'] - Set the verbosity of logging to console.
+ *   Valid values: ['off', 'error', 'warn', 'info', 'debug']
+ */
+
+/**
+ * You may pass these options to {@link Client#inviteToConversation} to
+ * override the default behavior.
+ * @typedef {object} Client.InviteToConversationOptions
+ * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing
+ *   {@link LocalMedia} object when creating the {@link Conversation}
+ * @property {?MediaStream} [localStream=null] - Set to reuse an existing
+ *   <code>MediaStream</code> when creating the {@link Conversation}
+ * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to
+ *   override the parameters passed to <code>getUserMedia</code> when neither
+ *   <code>localMedia</code> nor <code>localStream</code> are provided
+ */
+
+module.exports = Client;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/conversation.js.html b/dist/docs/conversation.js.html new file mode 100644 index 000000000..914ac2204 --- /dev/null +++ b/dist/docs/conversation.js.html @@ -0,0 +1,749 @@ + + + + + JSDoc: Source: conversation.js + + + + + + + + + + +
+ +

Source: conversation.js

+ + + + + + +
+
+
'use strict';
+
+var constants = require('./util/constants');
+var ConversationInfo = require('./signaling/conversation-info');
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+var Media = require('./media');
+var Participant = require('./participant');
+var util = require('./util');
+
+var Log = require('./util/log');
+var E = constants.twilioErrors;
+
+/**
+ * Construct a {@link Conversation}.
+ * @class
+ * @classdesc A {@link Conversation} represents communication between your
+ *   {@link Client} and one or more {@link Participant}s sharing
+ *   {@link AudioTrack}s and {@link VideoTrack}s.
+ *   <br><br>
+ *   You can join a {@link Conversation} by first creating an
+ *   {@link OutgoingInvite} with {@link Client#inviteToConversation} or by
+ *   accepting an {@link IncomingInvite} with {@link IncomingInvite#accept}.
+ * @param {Object} [options] - Options to override the constructor's default
+ *   behavior.
+ * @property {LocalMedia} localMedia - Your {@link Client}'s {@link LocalMedia} in the {@link Conversation}
+ * @property {Map<Participant.SID, Participant>} participants - The {@link Participant}s
+ *   participating in this {@link Conversation}
+ * @property {Conversation.SID} sid - The {@link Conversation}'s SID
+ * @fires Conversation#disconnected
+ * @fires Conversation#participantConnected
+ * @fires Conversation#participantDisconnected
+ * @fires Conversation#participantFailed
+ * @fires Conversation#trackAdded
+ * @fires Conversation#trackDimensionsChanged
+ * @fires Conversation#trackDisabled
+ * @fires Conversation#trackEnabled
+ * @fires Conversation#trackEnded
+ * @fires Conversation#trackRemoved
+ * @fires Conversation#trackStarted
+ */
+function Conversation(options) {
+  if (!(this instanceof Conversation)) {
+    return new Conversation(options);
+  }
+  EventEmitter.call(this);
+
+  options = util.withDefaults({ }, options, {
+    logLevel: constants.DEFAULT_LOG_LEVEL
+  });
+
+  var localMedia = options.localMedia;
+  var participantSid = null;
+  var shouldStopLocalMediaOnDisconnect = options.shouldStopLocalMediaOnDisconnect;
+  var sid;
+
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    _dialogs: {
+      value: new Set()
+    },
+    _localMedia: {
+      set: function(_localMedia) {
+        localMedia = _localMedia;
+      }
+    },
+    _log: {
+      value: new Log('Conversation', options.logLevel)
+    },
+    _options: {
+      value: options
+    },
+    _participantSid: {
+      get: function() {
+        return participantSid;
+      },
+      set: function(_participantSid) {
+        participantSid = _participantSid;
+      }
+    },
+    _shouldStopLocalMediaOnDisconnect: {
+      get: function() {
+        return shouldStopLocalMediaOnDisconnect;
+      },
+      set: function(_shouldStopLocalMediaOnDisconnect) {
+        shouldStopLocalMediaOnDisconnect = _shouldStopLocalMediaOnDisconnect;
+      }
+    },
+    _sid: {
+      set: function(_sid) {
+        sid = _sid;
+      }
+    },
+    _trackIdToParticipants: {
+      value: new Map()
+    },
+    localMedia: {
+      enumerable: true,
+      get: function() {
+        return localMedia;
+      }
+    },
+    participants: {
+      enumerable: true,
+      value: new Map()
+    },
+    sid: {
+      enumerable: true,
+      get: function() {
+        return sid;
+      }
+    }
+  });
+
+  return this;
+}
+
+var TRACK_ADDED = Conversation.TRACK_ADDED = Participant.TRACK_ADDED;
+var TRACK_DIMENSIONS_CHANGED = Conversation.TRACK_DIMENSIONS_CHANGED = Participant.TRACK_DIMENSIONS_CHANGED;
+var TRACK_DISABLED = Conversation.TRACK_DISABLED = Participant.TRACK_DISABLED;
+var TRACK_ENABLED = Conversation.TRACK_ENABLED = Participant.TRACK_ENABLED;
+var TRACK_ENDED = Conversation.TRACK_ENDED = Participant.TRACK_ENDED;
+var TRACK_REMOVED = Conversation.TRACK_REMOVED = Participant.TRACK_REMOVED;
+var TRACK_STARTED = Conversation.TRACK_STARTED = Participant.TRACK_STARTED;
+
+inherits(Conversation, EventEmitter);
+
+/**
+ * Add a {@link Dialog} to the {@link Conversation}.
+ * @private
+ * @param {Dialog} dialog - The {@link Dialog}
+ * @returns {this}
+ */
+Conversation.prototype._onDialog = function _onDialog(dialog) {
+  if (this._dialogs.has(dialog)) {
+    return this;
+  }
+
+  this._sid = this.sid || dialog.conversationSid;
+  this._localMedia = this.localMedia || dialog.localMedia;
+  this._participantSid = this._participantSid || dialog.participantSid;
+  this._dialogs.add(dialog);
+
+  dialog.once('ended', this._removeDialog.bind(this));
+
+  dialog.on('notification', this._onNotification.bind(this, dialog));
+  dialog.dequeue('notification');
+  handleDialogTrackEvents(dialog, this._trackIdToParticipants);
+
+  // NOTE(mroberts): Simulate Conversation Events if disabled. Once we are
+  // confident in the Conversation Events implementation we will completely
+  // remove this path.
+  if (!this._options.useConversationEvents) {
+    var participantSid = util.makeUUID();
+    var notification = ConversationInfo
+      .simulateParticipantConnectedEvent(dialog, participantSid);
+    this._onNotification(dialog, notification);
+    var participant = this.participants.get(participantSid);
+    handleDialogTrackEventsMesh(dialog, participant);
+  }
+
+  return this;
+};
+
+Conversation.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) {
+  this._options.localMedia = this.localMedia;
+  return inviteServerTransaction.accept(this._options).then(this._onDialog.bind(this));
+};
+
+/**
+ * Handle {@link Dialog} {@link Track} events using a Map from {@link Track} IDs
+ * to {@link Participant}s. This technique relies on Conversation Events to
+ * construct the Map. It is topology-independent.
+ * @private
+ * @param {Dialog} dialog - The {@link Dialog}
+ * @param {Map<string, Set>} trackIdToParticipants - The Map from
+ *   {@link Track} IDs to {@link Participant}s
+ */
+function handleDialogTrackEvents(dialog, trackIdToParticipants) {
+  // Add the Track to any Participant associated with the Track ID.
+  function addTrack(track) {
+    var participants = trackIdToParticipants.get(track.id) || new Set();
+    participants.forEach(function(participant) {
+      participant.media._addTrack(track);
+    });
+  }
+
+  // Remove the Track from any Participant associated with the Track ID, and
+  // remove the Track from the Map.
+  function removeTrack(track) {
+    var participants = trackIdToParticipants.get(track.id) || new Set();
+    participants.forEach(function(participant) {
+      participant.media._removeTrack(track);
+    });
+    trackIdToParticipants.delete(track.id);
+  }
+
+  var dialogMedia = dialog.remoteMedia;
+  dialogMedia.tracks.forEach(addTrack);
+  dialogMedia.on(Media.TRACK_ADDED, addTrack);
+  dialogMedia.on(Media.TRACK_REMOVED, removeTrack);
+  dialog.once('ended', function() {
+    dialogMedia.removeListener(Media.TRACK_ADDED, addTrack);
+    dialogMedia.removeListener(Media.TRACK_REMOVED, removeTrack);
+  });
+}
+
+/**
+ * Handle {@link Dialog} {@link Track} events using a one-to-one association
+ * with a {@link Participant}. This technique only works in mesh topologies.
+ * @private
+ * @param {Dialog} dialog - The {@link Dialog}
+ * @param {Participant} participant - The {@link Participant}
+ */
+function handleDialogTrackEventsMesh(dialog, participant) {
+  var dialogMedia = dialog.remoteMedia;
+  var participantMedia = participant.media;
+  dialogMedia.tracks.forEach(participantMedia._addTrack, participantMedia);
+  dialogMedia.on(Media.TRACK_ADDED, participantMedia._addTrack.bind(participantMedia));
+  dialogMedia.on(Media.TRACK_REMOVED, participantMedia._removeTrack.bind(participantMedia));
+}
+
+/**
+ * Connect a {@link Participant} to the {@link Conversation}.
+ * @private
+ * @param {Participant} participant - The {@link Participant}
+ * @returns {this}
+ */
+Conversation.prototype._connectParticipant = function _connectParticipant(participant) {
+  if (this.participants.has(participant.sid)) {
+    return this;
+  }
+
+  this.participants.set(participant.sid, participant);
+
+  var self = this;
+  participant.on(Participant.TRACK_ADDED, function trackAdded(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_ADDED, trackAdded);
+    }
+    self.emit(TRACK_ADDED, participant, track);
+  });
+  participant.on(Participant.TRACK_DIMENSIONS_CHANGED, function trackDimensionsChanged(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_DIMENSIONS_CHANGED, trackDimensionsChanged);
+    }
+    self.emit(TRACK_DIMENSIONS_CHANGED, participant, track);
+  });
+  participant.on(Participant.TRACK_DISABLED, function trackDisabled(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_DISABLED, trackDisabled);
+    }
+    self.emit(TRACK_DISABLED, participant, track);
+  });
+  participant.on(Participant.TRACK_ENABLED, function trackEnabled(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_ENABLED, trackEnabled);
+    }
+    self.emit(TRACK_ENABLED, participant, track);
+  });
+  participant.on(Participant.TRACK_ENDED, function trackEnded(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_ENDED, trackEnded);
+    }
+    self.emit(TRACK_ENDED, participant, track);
+  });
+  participant.on(Participant.TRACK_REMOVED, function trackRemoved(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_REMOVED, trackRemoved);
+    }
+    self.emit(TRACK_REMOVED, participant, track);
+  });
+  participant.on(Participant.TRACK_STARTED, function trackStarted(track) {
+    if (!self.participants.has(participant.sid)) {
+      return participant.removeListener(Participant.TRACK_STARTED, trackStarted);
+    }
+    self.emit(TRACK_STARTED, participant, track);
+  });
+
+  // Emit these events on the next tick so the customer has
+  // a chance to listen for them.
+  setTimeout(function() {
+    self.emit('participantConnected', participant);
+
+    // Re-emit the "trackAdded" event for each of the Participant's Tracks.
+    participant.media.tracks.forEach(participant.emit.bind(participant, Participant.TRACK_ADDED));
+  });
+
+  return this;
+};
+
+Conversation.prototype._removeDialog = function _removeDialog(dialog) {
+  this._dialogs.delete(dialog);
+
+  // NOTE(mroberts): Simulate Conversation Events if disabled. Once we are
+  // confident in the Conversation Events implementation we will completely
+  // remove this path.
+  if (!this._options.useConversationEvents) {
+    var notification = ConversationInfo
+      .simulateParticipantDisconnectedEvents(this.participants, dialog);
+    this._onNotification(dialog, notification);
+  }
+
+  if (!this._dialogs.size) {
+    if (this._shouldStopLocalMediaOnDisconnect) {
+      this.localMedia.stop();
+    }
+
+    this.emit('disconnected', this);
+
+    // NOTE(mroberts): Regardless of topology, zero dialogs implies we are
+    // disconnected from the Conversation; so disconnect any remaining
+    // Participants (hopefully they have already been disconnected).
+    this.participants.forEach(this._disconnectParticipant, this);
+  }
+
+  return this;
+};
+
+/**
+ * Associate a {@link Track} ID to a {@link Participant}.
+ * @private
+ * @param {Participant} participant - The {@link Participant}
+ * @param {{id: string}} track - An object containing the {@link Track} ID
+ * @returns {this}
+ */
+Conversation.prototype._associateParticipantToTrackId = function _associateParticipantToTrackId(participant, track) {
+  util.map.addToMapOfSets(this._trackIdToParticipants, track.id, participant);
+  return this;
+};
+
+/**
+ * Associate {@link Track} IDs to a {@link Participant}.
+ * @private
+ * @param {Participant} participant - The {@link Participant}
+ * @param {Array<{id: string}>} tracks - Objects containing the {@link Track} IDs
+ * @returns {this}
+ */
+Conversation.prototype._associateParticipantToTrackIds = function _associateParticipantToTrackIds(participant, tracks) {
+  tracks.forEach(this._associateParticipantToTrackId.bind(this, participant));
+  return this;
+};
+
+/**
+ * Disassociate a {@link Participant} from a {@link Track} ID.
+ * @private
+ * @param {Participant} participant - The {@link Participant}
+ * @param {{id: string}} track - An object containing the {@link Track} ID
+ * @returns {this}
+ */
+Conversation.prototype._disassociateParticipantFromTrackId = function _disassociateParticipantFromTrackId(participant, _track) {
+  var id = _track.id;
+  util.map.deleteFromMapOfSets(this._trackIdToParticipants, id, participant);
+  var track = participant.media.tracks.get(id);
+  if (track) {
+    participant.media._removeTrack(track);
+  }
+  return this;
+};
+
+/**
+ * Associate {@link Track} IDs to a {@link Participant}.
+ * @private
+ * @param {Participant} participant - The {@link Participant}
+ * @param {Array<{id: string}>} tracks - Objects containing the {@link Track} IDs
+ * @returns {this}
+ */
+Conversation.prototype._disassociateParticipantFromTrackIds = function _disassociateParticipantFromTrackIds(participant, tracks) {
+  tracks.forEach(this._disassociateParticipantFromTrackId.bind(this, participant));
+  return this;
+};
+
+/**
+ * Disconnect a {@link Participant} from the {@link Conversation}.
+ * @private
+ * @param {Participant} - The {@link Participant}
+ * @returns {this}
+ */
+Conversation.prototype._disconnectParticipant = function _disconnectParticipant(participant) {
+  participant.media.tracks.forEach(function(track) {
+    this._disassociateParticipantFromTrackId(participant, track.id);
+    participant.media._removeTrack(track);
+  }, this);
+  this.participants.delete(participant.sid);
+  this.emit('participantDisconnected', participant);
+  return this;
+};
+
+/**
+ * Update the {@link Conversation} upon receipt of a {@link Notification}.
+ * @private
+ * @param {Dialog} dialog - the {@link Dialog} that received the
+ *   {@link PartialNotification}
+ * @param {PartialNotification} notification
+ * @returns {this}
+ */
+Conversation.prototype._onNotification = function _onNotification(dialog, notification) {
+  var conversationState = notification.conversation_state;
+  if (conversationState) {
+    if (this.sid !== conversationState.sid) {
+      return this;
+    }
+    return this._onFullNotification(dialog, notification);
+  }
+  return this._onPartialNotification(dialog, notification);
+};
+
+Conversation.prototype._onFullNotification = function _onFullNotification(dialog, notification) {
+  notification.conversation_state.participants.forEach(this._onParticipantConnected, this);
+  return this;
+};
+
+Conversation.prototype._onPartialNotification = function _onPartialNotification(dialog, notification) {
+  notification.event_list.forEach(function(event) {
+    var eventType = event.event.toLowerCase();
+    switch (eventType) {
+      case 'participant_connected':
+        this._onParticipantConnected(event);
+        return;
+      case 'participant_disconnected':
+        this._onParticipantDisconnected(event);
+        return;
+      case 'participant_failed':
+        this._onParticipantFailed(event);
+        return;
+    }
+    var participant = this.participants.get(event.participant_sid);
+    if (participant) {
+      switch (eventType) {
+        case 'track_added':
+          this._associateParticipantToTrackIds(participant, event.tracks);
+          return;
+        case 'track_removed':
+          this._disassociateParticipantFromTrackIds(participant, event.tracks);
+          return;
+      }
+      participant._onConversationEvent(event);
+    }
+  }, this);
+  return this;
+};
+
+/**
+ * Handle a "participant_connected" Conversation Event.
+ * @private
+ * @param {Notification} event
+ * @returns {this}
+ */
+Conversation.prototype._onParticipantConnected = function _onParticipantConnected(event) {
+  if (this._participantSid === event.participant_sid) {
+    return this;
+  }
+
+  var participant = this.participants.get(event.participant_sid);
+  var connectParticipant = false;
+
+  if (!participant) {
+    participant = new Participant(event.participant_sid, util.getUser(event.address));
+    connectParticipant = true;
+  }
+
+  this._associateParticipantToTrackIds(participant, event.tracks);
+
+  if (connectParticipant) {
+    this._connectParticipant(participant);
+  }
+
+  return this;
+};
+
+Conversation.prototype._onParticipantFailed = function _onParticipantFailed(event) {
+  if (this._participantSid !== event.participant_sid) {
+    return this;
+  }
+
+  var participant = this.participants.get(event.participant_sid) ||
+    new Participant(event.participant_sid, util.getUser(event.address));
+
+  this.emit('participantFailed', participant);
+
+  return this;
+};
+
+/**
+ * Handle a "participant_disconnected" Conversation Event.
+ * @private
+ * @param {Notification} event
+ * @returns {this}
+ */
+Conversation.prototype._onParticipantDisconnected = function _onParticipantDisconnected(event) {
+  if (this._participantSid === event.participant_sid) {
+    return this;
+  }
+
+  var participant = this.participants.get(event.participant_sid);
+
+  if (participant) {
+    this._disconnectParticipant(participant);
+  }
+
+  return this;
+};
+
+Conversation.prototype.getStats = function getStats() {
+  var promises = [];
+  this._dialogs.forEach(function(dialog) {
+    promises.push(dialog.getStats());
+  });
+
+  return Promise.all(promises);
+};
+
+/**
+ * Disconnect from the {@link Conversation}.
+ * @returns {this}
+ */
+Conversation.prototype.disconnect = function disconnect() {
+  this._dialogs.forEach(function(dialog) {
+    dialog.end();
+  });
+  return this;
+};
+
+/**
+ * Add a {@link Participant} to the {@link Conversation}.
+ * @param {string} identity - The identity of the {@link Participant} to add
+ * @returns {this}
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ *  client.inviteToConversation('alice').then(function(conversation) {
+ *    conversation.invite('bob');
+ *
+ *    conversation.on('participantConnected', function(participant) {
+ *      if (participant.identity === 'bob') {
+ *        console.log('Bob has connected');
+ *      }
+ *    });
+ *  });
+ * @throws {Error} INVALID_ARGUMENT
+ *//**
+ * Add {@link Participant}s to the {@link Conversation}.
+ * @param {Array<string>} identities - The identities of the {@link Participant}s to add
+ * @returns {this}
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ *  client.inviteToConversation('alice').then(function(conversation) {
+ *    conversation.invite(['bob', 'charlie']);
+ *
+ *    conversation.on('participantConnected', function() {
+ *      if (participant.identity === 'bob') {
+ *        console.log('Bob has connected');
+ *      } else if (participant.identity === 'charlie') {
+ *        console.log('Charlie has connected');
+ *      }
+ *    });
+ *  });
+ * @throws {Error} INVALID_ARGUMENT
+ */
+Conversation.prototype.invite = function invite(identity) {
+  if (!identity) {
+    this._log.throw(E.INVALID_ARGUMENT, 'No Participant identities were provided');
+  }
+
+  // there maybe several dialogs within the conversation
+  // we just pick the first dialog to send the REFER to conversation service
+  var dialog;
+  this._dialogs.forEach(function(_dialog) {
+    dialog = dialog || _dialog;
+  });
+
+  var identities = identity.forEach ? identity : [identity];
+
+  var accessManager = dialog.userAgent.accessManager;
+  util.validateAddresses(accessManager._tokenPayload.sub, identities);
+
+  identities.forEach(dialog.refer, dialog);
+
+  return this;
+};
+
+Object.freeze(Conversation.prototype);
+
+/**
+ * A {@link Conversation.SID} is a 34-character string starting with "CV"
+ * that uniquely identifies a {@link Conversation}.
+ * @type string
+ * @typedef Conversation.SID
+ */
+
+/**
+ * Your {@link Client} was disconnected from the {@link Conversation} and all
+ * other {@link Participant}s.
+ * @param {Conversation} conversation - The {@link Conversation} your
+ *   {@link Client} was disconnected from
+ * @event Conversation#disconnected
+ * @example
+ * myConversation.on('disconnected', function() {
+ *   myConversation.localMedia.detach();
+ * });
+ */
+
+/**
+ * A {@link Participant} joined the {@link Conversation}.
+ * @param {Participant} participant - The {@link Participant} who joined
+ * @event Conversation#participantConnected
+ * @example
+ * myConversation.on('participantConnected', function(participant) {
+ *   console.log(participant.identity + ' joined the Conversation');
+ *
+ *   // Get the participant's Media,
+ *   var participantMedia = participant.media;
+ *
+ *   // And attach it to your application's view.
+ *   var participantView = document.getElementById('participant-view');
+ *   participantMedia.attach(participantView);
+ *   participantVideos.appendChild(participantView);
+ * });
+ */
+
+/**
+ * A {@link Participant} left the {@link Conversation}.
+ * @param {Participant} participant - The {@link Participant} who left
+ * @event Conversation#participantDisconnected
+ * @example
+ * myConversation.on('participantDisconnected', function(participant) {
+ *   console.log(participant.identity + ' left the Conversation');
+ * });
+ */
+
+/**
+ * A {@link Participant} failed to join {@link Conversation}.
+ * @param {Participant} participant - The {@link Participant} that failed to join
+ * @event Conversation#participantFailed
+ * @example
+ * myConversation.on('participantFailed', function(participant) {
+ *   console.log(participant.identity + ' failed to join the Conversation');
+ * });
+ */
+
+/**
+ * A {@link Track} was added by a {@link Participant} in the {@link Conversation}.
+ * @param {Track} track - The {@link Track} that was added
+ * @param {Participant} participant - The {@link Participant} who added the
+ *   {@link Track}
+ * @event Conversation#trackAdded
+ */
+
+/**
+ * One of the {@link Participant}'s {@link VideoTrack}'s dimensions changed.
+ * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
+ * @param {Participant} participant - The {@link Participant} whose {@link VideoTrack}'s
+ *   dimensions changed
+ * @event Conversation#trackDimensionsChanged
+ */
+
+/**
+ * A {@link Track} was disabled by a {@link Participant} in the {@link Conversation}.
+ * @param {Track} track - The {@link Track} that was disabled
+ * @param {Participant} participant - The {@link Participant} who disabled the
+ *   {@link Track}
+ * @event Conversation#trackDisabled
+ */
+
+/**
+ * A {@link Track} was enabled by a {@link Participant} in the {@link Conversation}.
+ * @param {Track} track - The {@link Track} that was enabled
+ * @param {Participant} participant - The {@link Participant} who enabled the
+ *   {@link Track}
+ * @event Conversation#trackEnabled
+ */
+
+/**
+ * One of a {@link Participant}'s {@link Track}s in the {@link Conversation} ended.
+ * @param {Track} track - The {@link Track} that ended
+ * @param {Participant} participant - The {@link Participant} whose {@link Track} ended
+ * @event Conversation#trackEnded
+ */
+
+/**
+ * A {@link Track} was removed by a {@link Participant} in the {@link Conversation}.
+ * @param {Track} track - The {@link Track} that was removed
+ * @param {Participant} participant - The {@link Participant} who removed the
+ *   {@link Track}
+ * @event Conversation#trackRemoved
+ */
+
+/**
+ * One of a {@link Participant}'s {@link Track}s in the {@link Conversation} started.
+ * @param {Track} track - The {@link Track} that started
+ * @param {Participant} participant - The {@link Participant} whose {@link Track} started
+ * @event Conversation#trackStarted
+ */
+
+module.exports = Conversation;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/fonts/OpenSans-Bold-webfont.eot b/dist/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 000000000..5d20d9163 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Bold-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-Bold-webfont.svg b/dist/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 000000000..3ed7be4bc --- /dev/null +++ b/dist/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-Bold-webfont.woff b/dist/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 000000000..1205787b0 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Bold-webfont.woff differ diff --git a/dist/docs/fonts/OpenSans-BoldItalic-webfont.eot b/dist/docs/fonts/OpenSans-BoldItalic-webfont.eot new file mode 100644 index 000000000..1f639a15f Binary files /dev/null and b/dist/docs/fonts/OpenSans-BoldItalic-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-BoldItalic-webfont.svg b/dist/docs/fonts/OpenSans-BoldItalic-webfont.svg new file mode 100644 index 000000000..6a2607b9d --- /dev/null +++ b/dist/docs/fonts/OpenSans-BoldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-BoldItalic-webfont.woff b/dist/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 000000000..ed760c062 Binary files /dev/null and b/dist/docs/fonts/OpenSans-BoldItalic-webfont.woff differ diff --git a/dist/docs/fonts/OpenSans-Italic-webfont.eot b/dist/docs/fonts/OpenSans-Italic-webfont.eot new file mode 100644 index 000000000..0c8a0ae06 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Italic-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-Italic-webfont.svg b/dist/docs/fonts/OpenSans-Italic-webfont.svg new file mode 100644 index 000000000..e1075dcc2 --- /dev/null +++ b/dist/docs/fonts/OpenSans-Italic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-Italic-webfont.woff b/dist/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 000000000..ff652e643 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Italic-webfont.woff differ diff --git a/dist/docs/fonts/OpenSans-Light-webfont.eot b/dist/docs/fonts/OpenSans-Light-webfont.eot new file mode 100644 index 000000000..14868406a Binary files /dev/null and b/dist/docs/fonts/OpenSans-Light-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-Light-webfont.svg b/dist/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 000000000..11a472ca8 --- /dev/null +++ b/dist/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-Light-webfont.woff b/dist/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 000000000..e78607481 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Light-webfont.woff differ diff --git a/dist/docs/fonts/OpenSans-LightItalic-webfont.eot b/dist/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 000000000..8f445929f Binary files /dev/null and b/dist/docs/fonts/OpenSans-LightItalic-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-LightItalic-webfont.svg b/dist/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 000000000..431d7e354 --- /dev/null +++ b/dist/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-LightItalic-webfont.woff b/dist/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 000000000..43e8b9e6c Binary files /dev/null and b/dist/docs/fonts/OpenSans-LightItalic-webfont.woff differ diff --git a/dist/docs/fonts/OpenSans-Regular-webfont.eot b/dist/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 000000000..6bbc3cf58 Binary files /dev/null and b/dist/docs/fonts/OpenSans-Regular-webfont.eot differ diff --git a/dist/docs/fonts/OpenSans-Regular-webfont.svg b/dist/docs/fonts/OpenSans-Regular-webfont.svg new file mode 100644 index 000000000..25a395234 --- /dev/null +++ b/dist/docs/fonts/OpenSans-Regular-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/docs/fonts/OpenSans-Regular-webfont.woff b/dist/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 000000000..e231183dc Binary files /dev/null and b/dist/docs/fonts/OpenSans-Regular-webfont.woff differ diff --git a/dist/docs/incominginvite.js.html b/dist/docs/incominginvite.js.html new file mode 100644 index 000000000..6adbf41ff --- /dev/null +++ b/dist/docs/incominginvite.js.html @@ -0,0 +1,373 @@ + + + + + JSDoc: Source: incominginvite.js + + + + + + + + + + +
+ +

Source: incominginvite.js

+ + + + + + +
+
+
'use strict';
+
+var C = require('./util/constants');
+var Conversation = require('./conversation');
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+var util = require('./util');
+
+var LocalMedia = require('./media/localmedia');
+
+/**
+ * Construct an {@link IncomingInvite}.
+ * @class
+ * @classdesc An {@link IncomingInvite} to a {@link Conversation} can be accepted or
+ * rejected.
+ * <br><br>
+ * {@link IncomingInvite}s are returned by {@link Client#event:invite}.
+ * @param {InviteServerTransaction} inviteServerTransaction - The
+ *   {@link InviteServerTransaction} that this {@link IncomingInvite} wraps
+ * @param {Object} [options] - Options to override the constructor's
+ *   default behavior.
+ * @property {Conversation.SID} conversationSid - The SID of the {@link Conversation}
+ *   this {@link IncomingInvite} invites to
+ * @property {string} from - The identity of the {@link Participant} that sent this
+ *   {@link IncomingInvite}
+ * @property {Array<string>} participants - The identities of the {@link Participant}s currently in the {@link Conversation}
+ * @property {string} status - The status of this {@link IncomingInvite}, either
+ *   "accepting", "accepted", "rejected", "canceled", "failed", or "pending"
+ * @fires IncomingInvite#accepted
+ * @fires IncomingInvite#canceled
+ * @fires IncomingInvite#failed
+ * @fires IncomingInvite#rejected
+ */
+function IncomingInvite(inviteServerTransaction, options) {
+  if (!(this instanceof IncomingInvite)) {
+    return new IncomingInvite(inviteServerTransaction, options);
+  }
+
+  options = util.withDefaults({ }, options, {
+    logLevel: C.DEFAULT_LOG_LEVEL
+  });
+
+  var self = this;
+  EventEmitter.call(this);
+
+  var conversation = null;
+  var from = util.getUser(inviteServerTransaction.from);
+  var localMedia = null;
+  var participants = [from];
+  var participantSid = inviteServerTransaction.participantSid;
+  var pending = 0;
+  var shouldStopLocalMediaOnFailure = false;
+  var status = 'pending';
+
+  var deferred = util.defer();
+  var inviteServerTransactions = new Set();
+  inviteServerTransactions.add(inviteServerTransaction);
+
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    _conversation: {
+      set: function(_conversation) {
+        conversation = _conversation;
+      },
+      get: function() {
+        return conversation;
+      }
+    },
+    _deferred: {
+      value: deferred
+    },
+    _inviteServerTransaction: {
+      value: inviteServerTransaction
+    },
+    _inviteServerTransactions: {
+      value: inviteServerTransactions
+    },
+    _localMedia: {
+      get: function() {
+        return localMedia;
+      },
+      set: function(_localMedia) {
+        localMedia = _localMedia;
+      }
+    },
+    _logLevel: {
+      value: options.logLevel
+    },
+    _options: {
+      get: function() {
+        return options;
+      },
+      set: function(_options) {
+        options = _options;
+      }
+    },
+    _pending: {
+      get: function() {
+        return pending;
+      },
+      set: function(_pending) {
+        pending = _pending;
+      }
+    },
+    _promise: {
+      value: deferred.promise
+    },
+    _shouldStopLocalMediaOnFailure: {
+      get: function() {
+        return shouldStopLocalMediaOnFailure;
+      },
+      set: function(_shouldStopLocalMediaOnFailure) {
+        shouldStopLocalMediaOnFailure = _shouldStopLocalMediaOnFailure;
+      }
+    },
+    _status: {
+      get: function() {
+        return status;
+      },
+      set: function(_status) {
+        status = _status;
+      }
+    },
+    conversationSid: {
+      enumerable: true,
+      value: inviteServerTransaction.conversationSid
+    },
+    from: {
+      enumerable: true,
+      value: from
+    },
+    participants: {
+      enumerable: true,
+      value: participants
+    },
+    participantSid: {
+      enumerable: true,
+      value: participantSid
+    },
+    status: {
+      enumerable: true,
+      get: function() {
+        return status;
+      }
+    }
+  });
+
+  inviteServerTransaction.once('canceled', function() {
+    self.emit('canceled', self);
+  });
+
+  return this;
+}
+
+inherits(IncomingInvite, EventEmitter);
+
+IncomingInvite.prototype._onAcceptFailure = function _onAcceptFailure(reason) {
+  this._pending--;
+
+  if (this.status === 'accepting' && !this._pending) {
+    this._status = 'failed';
+    if (this._shouldStopLocalMediaOnFailure && this._localMedia) {
+      this._localMedia.stop();
+    }
+    this._deferred.reject(reason);
+    this.emit('failed', this);
+  }
+
+  return this;
+};
+
+IncomingInvite.prototype._onDialog = function _onDialog(dialog) {
+  this._pending--;
+
+  var conversation
+    = this._conversation
+    = this._conversation || new Conversation(this._options);
+
+  conversation._onDialog(dialog);
+
+  if (this.status === 'accepting') {
+    this._status = 'accepted';
+    this._deferred.resolve(conversation);
+    this.emit('accepted', this);
+  }
+
+  return conversation;
+};
+
+IncomingInvite.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) {
+  switch (this.status) {
+    case 'canceled':
+    case 'rejected':
+      inviteServerTransaction.reject();
+      return;
+    case 'accepting':
+      if (!this._inviteServerTransactions.has(inviteServerTransaction)) {
+        this.participants.push(util.getUser(inviteServerTransaction.from));
+        this._inviteServerTransactions.add(inviteServerTransaction);
+      }
+      this._pending++;
+      inviteServerTransaction.accept(this._options)
+        .then(this._onDialog.bind(this), this._onAcceptFailure.bind(this));
+      return;
+    case 'accepted':
+      this._conversation._onInviteServerTransaction(inviteServerTransaction);
+      return;
+    default:
+      if (!this._inviteServerTransactions.has(inviteServerTransaction)) {
+        this.participants.push(util.getUser(inviteServerTransaction.from));
+        this._inviteServerTransactions.add(inviteServerTransaction);
+      }
+  }
+};
+
+/**
+ * Accept the {@link IncomingInvite} and join the {@link Conversation}.
+ * @param {IncomingInvite.AcceptOptions}
+ *   [options={localStreamConstraints:{audio:true,video:true}}] - Options to override
+ *   {@link IncomingInvite#accept}'s default behavior
+ * @fires IncomingInvite#accepted
+ * @fires IncomingInvite#failed
+ * @returns {Promise<Conversation>}
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ * client.on('invite', function(invite) {
+ *   console.log('Received IncomingInvite to join a Conversation with ' + invite.from);
+ *
+ *   // By default, accept will request the microphone and camera for you.
+ *   invite.accept();
+ * });
+ */
+IncomingInvite.prototype.accept = function accept(options) {
+  if (this.status === 'accepted') {
+    return this._promise;
+  }
+
+  options = this._options = util.withDefaults({ }, options, this._options);
+  this._status = 'accepting';
+
+  var self = this;
+
+  function getLocalMedia() {
+    if (!options.localMedia && !options.localStream) {
+      self._shouldStopLocalMediaOnFailure = true;
+      self._options.shouldStopLocalMediaOnDisconnect = true;
+    }
+    return LocalMedia.getLocalMedia(options);
+  }
+
+  getLocalMedia().then(function(localMedia) {
+    self._localMedia = localMedia;
+    options.localMedia = localMedia;
+    self._inviteServerTransactions.forEach(self._onInviteServerTransaction, self);
+  });
+
+  return this._promise;
+};
+
+/**
+ * Reject the {@link IncomingInvite} to a {@link Conversation}.
+ * @fires IncomingInvite#rejected
+ * @example
+ * var initialToken = getAccessToken();
+ * var manager = new Twilio.AccessManager(initialToken);
+ * var client = new Twilio.Conversations.Client(manager);
+ *
+ * client.on('invite', function(invite) {
+ *   console.log('Rejecting IncomingInvite to join a Conversation with ' + invite.from);
+ *   invite.reject();
+ * });
+ *
+ * @returns {this}
+ */
+IncomingInvite.prototype.reject = function reject() {
+  this._inviteServerTransaction.reject();
+  this.emit('rejected');
+  return this;
+};
+
+Object.freeze(IncomingInvite.prototype);
+
+/**
+ * The {@link IncomingInvite} was accepted, and the {@link Client} is now
+ * participating in the {@link Conversation}.
+ * @param {IncomingInvite} invite - The {@link IncomingInvite}
+ * @event IncomingInvite#accepted
+ */
+
+/**
+ * The {@link IncomingInvite} was rejected.
+ * @param {IncomingInvite} invite - The {@link IncomingInvite}
+ * @event IncomingInvite#rejected
+ */
+
+/**
+ * The {@link IncomingInvite} was canceled.
+ * @param {IncomingInvite} invite - The {@link IncomingInvite}
+ * @event IncomingInvite#canceled
+ */
+
+/**
+ * The {@link IncomingInvite} failed.
+ * @param {IncomingInvite} invite - The {@link IncomingInvite}
+ * @event IncomingInvite#failed
+ */
+
+/**
+ * You may pass these options to {@link IncomingInvite#accept} to
+ * override the default behavior.
+ * @typedef {object} IncomingInvite.AcceptOptions
+ * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing
+ *   {@link LocalMedia} object when accepting an {@link IncomingInvite}
+ * @property {?MediaStream} [localStream=null] - Set to reuse an existing
+ *   MediaStream when accepting an {@link IncomingInvite}
+ * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to
+ *   override the parameters passed to <code>getUserMedia</code> when neither
+ *   <code>localMedia</code> nor <code>localStream</code> are provided
+ */
+
+module.exports = IncomingInvite;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/index.html b/dist/docs/index.html new file mode 100644 index 000000000..beb2c186c --- /dev/null +++ b/dist/docs/index.html @@ -0,0 +1,63 @@ + + + + + JSDoc: Home + + + + + + + + + + +
+ +

Home

+ + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/dist/docs/media_index.js.html b/dist/docs/media_index.js.html new file mode 100644 index 000000000..d67e35b8f --- /dev/null +++ b/dist/docs/media_index.js.html @@ -0,0 +1,457 @@ + + + + + JSDoc: Source: media/index.js + + + + + + + + + + +
+ +

Source: media/index.js

+ + + + + + +
+
+
'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+
+var Track = require('./track');
+var AudioTrack = require('./track/audiotrack');
+var VideoTrack = require('./track/videotrack');
+
+/**
+ * Construct a {@link Media} object.
+ * @class
+ * @classdesc A {@link Media} object contains a number of {@link AudioTrack}s
+ *   and {@link VideoTrack}s. You can call {@link Media#attach} with a
+ *   &lt;div&gt; to automatically update your application's user interface with
+ *   &lt;audio&gt; and &lt;video&gt; elements as {@link Track}s are added and
+ *   removed.
+ * @property {Map<HTMLElement, Map<Track, HTMLElement>>} attachments - A Map
+ *   from &lt;div&gt; elements to a Map from {@link Track}s to their attached
+ *   HTMLElements (managed by {@link Media#attach} and {@link Media#detach})
+ * @property {Map<Track.ID, AudioTrack>} audioTracks - The {@link AudioTrack}s on
+ *   this {@link Media} object
+ * @property {boolean} isMuted - True if every {@link AudioTrack} on this
+ *   {@link Media} object is disabled
+ * @property {boolean} isPaused - True if every {@link VideoTrack} on this
+ *   {@link Media} object is disabled
+ * @property {Set<MediaStream>} mediaStreams - The MediaStreams associated with
+ *   the {@link Track}s on this {@link Media} object
+ * @property {Map<Track.ID, Track>} tracks - The {@link AudioTrack}s and
+ *   {@link VideoTrack}s on this {@link Media} object
+ * @property {Map<Track.ID, VideoTrack>} videoTracks - The {@link VideoTrack}s on
+ *   this {@link Media} object
+ * @fires Media#trackAdded
+ * @fires Media#trackDimensionsChanged
+ * @fires Media#trackDisabled
+ * @fires Media#trackEnabled
+ * @fires Media#trackEnded
+ * @fires Media#trackRemoved
+ * @fires Media#trackStarted
+ */
+function Media() {
+  EventEmitter.call(this);
+
+  var attachments = new Map();
+  var audioTracks = new Map();
+  var mediaStreams = new Set();
+  var tracks = new Map();
+  var videoTracks = new Map();
+
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    attachments: {
+      enumerable: true,
+      value: attachments
+    },
+    audioTracks: {
+      enumerable: true,
+      value: audioTracks
+    },
+    isMuted: {
+      enumerable: true,
+      get: function() {
+        var isMuted = true;
+        audioTracks.forEach(function(track) {
+          isMuted = isMuted && !track.isEnabled;
+        });
+        return isMuted;
+      }
+    },
+    isPaused: {
+      enumerable: true,
+      get: function() {
+        var isPaused = true;
+        videoTracks.forEach(function(track) {
+          isPaused = isPaused && !track.isEnabled;
+        });
+        return isPaused;
+      }
+    },
+    mediaStreams: {
+      enumerable: true,
+      value: mediaStreams
+    },
+    tracks: {
+      enumerable: true,
+      value: tracks
+    },
+    videoTracks: {
+      enumerable: true,
+      value: videoTracks
+    }
+  });
+  return this;
+}
+
+var TRACK_ADDED = Media.TRACK_ADDED = 'trackAdded';
+var TRACK_DIMENSIONS_CHANGED = Media.TRACK_DIMENSIONS_CHANGED = 'trackDimensionsChanged';
+var TRACK_DISABLED = Media.TRACK_DISABLED = 'trackDisabled';
+var TRACK_ENABLED = Media.TRACK_ENABLED = 'trackEnabled';
+var TRACK_ENDED = Media.TRACK_ENDED = 'trackEnded';
+var TRACK_REMOVED = Media.TRACK_REMOVED = 'trackRemoved';
+var TRACK_STARTED = Media.TRACK_STARTED = 'trackStarted';
+
+inherits(Media, EventEmitter);
+
+Media.prototype._addRemoteStream = function _addRemoteStream(mediaStream) {
+  mediaStream.getAudioTracks().forEach(function(mediaStreamTrack) {
+    var audioTrack = new AudioTrack(mediaStream, mediaStreamTrack);
+    this._addTrack(audioTrack);
+  }, this);
+  mediaStream.getVideoTracks().forEach(function(mediaStreamTrack) {
+    var videoTrack = new VideoTrack(mediaStream, mediaStreamTrack);
+    this._addTrack(videoTrack);
+  }, this);
+  return this;
+};
+
+Media.prototype._updateMediaStreams = function _updateMediaStreams() {
+  this.mediaStreams.clear();
+  this.tracks.forEach(function(track) {
+    this.mediaStreams.add(track.mediaStream);
+  }, this);
+  return this.mediaStreams;
+};
+
+Media.prototype._addTrack = function _addTrack(track) {
+  if (this.tracks.has(track.id)) {
+    return this;
+  }
+
+  var self = this;
+  this.mediaStreams.add(track.mediaStream);
+
+  this.tracks.set(track.id, track);
+  this._reemitTrackEvent(track, VideoTrack.DIMENSIONS_CHANGED, TRACK_DIMENSIONS_CHANGED);
+  this._reemitTrackEvent(track, Track.DISABLED, TRACK_DISABLED);
+  this._reemitTrackEvent(track, Track.ENABLED, TRACK_ENABLED);
+  this._reemitTrackEvent(track, Track.ENDED, TRACK_ENDED);
+  this._reemitTrackEvent(track, Track.STARTED, TRACK_STARTED);
+  if (track.kind === 'audio') {
+    this._addAudioTrack(track);
+  } else {
+    this._addVideoTrack(track);
+  }
+  this._updateMediaStreams();
+
+  track.once(Track.ENDED, function ended() {
+    self._removeTrack(track);
+  });
+
+  this.emit(TRACK_ADDED, track);
+
+  return this;
+};
+
+Media.prototype._addAudioTrack = function _addAudioTrack(track) {
+  this.audioTracks.set(track.id, track);
+  return this;
+};
+
+Media.prototype._addVideoTrack = function _addVideoTrack(track) {
+  this.videoTracks.set(track.id, track);
+  return this;
+};
+
+Media.prototype._reemitTrackEvent = function _reemitTrackEvent(track, trackEvent, event) {
+  var self = this;
+  var trackSet = track.kind === 'audio' ? this.audioTracks : this.videoTracks;
+  track.on(trackEvent, function onTrackEvent() {
+    // FIXME(mroberts): Lazily remove the event handler, but what happens
+    // if we add the Track twice? We only want to emit the event once.
+    if (!trackSet.has(track.id)) {
+      return track.removeListener(trackEvent, onTrackEvent);
+    }
+    self.emit(event, track);
+  });
+  return this;
+};
+
+/**
+ * Add any new {@link Track}s that did not trigger the onaddtrack event. WebRTC
+ * does not always call this callback, so we have to check ourselves.
+ * @private
+ * @returns {Media}
+ */
+Media.prototype._refreshTracks = function _refreshTracks() {
+  this.mediaStreams.forEach(this._addRemoteStream, this);
+  return this;
+};
+
+Media.prototype._attachTrack = function _attachTrack(el, attachments, track) {
+  var self = this;
+  var trackEl = track.attach();
+  // NOTE(mroberts): We want to mute local audio, otherwise we get feedback.
+  if (this.constructor !== Media && track instanceof AudioTrack) {
+    trackEl.muted = true;
+  }
+  el.appendChild(trackEl);
+  attachments.set(track, trackEl);
+  track.once('ended', function() {
+    self._detachTrack(el, attachments, track);
+  });
+  return this;
+};
+
+Media.prototype._detachTrack = function _detachTrack(el, attachments, track) {
+  var trackEl = attachments.get(track);
+  if (!trackEl) {
+    return this;
+  }
+  track.detach(trackEl);
+  if (trackEl.parentNode) {
+    trackEl.parentNode.removeChild(trackEl);
+  }
+  attachments.delete(track);
+  return this;
+};
+
+Media.prototype._removeTrack = function _removeTrack(track) {
+  if (!this.tracks.has(track.id)) {
+    return this;
+  }
+  this.tracks.delete(track.id);
+  (track.kind === 'audio' ? this.audioTracks : this.videoTracks).delete(track.id);
+  this._updateMediaStreams();
+  this.emit(TRACK_REMOVED, track);
+  return this;
+};
+
+/**
+ * Attach the {@link Media} to a newly created &lt;div&gt; element.
+ * @returns {HTMLElement}
+ * @example
+ * var remoteMediaEl = media.attach();
+ * document.getElementById('div#remote-media-container').appendChild(remoteMediaEl);
+*//**
+ * Attach the {@link Media} to an existing HTMLElement.
+ * @param {HTMLElement} el - The HTMLElement to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteMediaEl = document.getElementById('remote-media');
+ * media.attach(remoteMediaEl);
+*//**
+ * Attach the {@link Media} to an HTMLElement selected by
+ * <code>document.querySelector</code>.
+ * @param {string} selector - A query selector for the HTMLElement to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteMediaEl = media.attach('div#remote-media');
+ */
+Media.prototype.attach = function attach(el) {
+  if (!el) {
+    return createDivAndAttach(this);
+  } else if (typeof el === 'string') {
+    return selectElementAndAttach(this, el);
+  }
+  return attachToElement(this, el);
+};
+
+function attachToElement(media, el) {
+  if (media.attachments.has(el)) {
+    return el;
+  }
+  var attachments = new Map();
+  // Attach existing audio and video tracks to the element,
+  media.tracks.forEach(function(track) {
+    media._attachTrack(el, attachments, track);
+  }, media);
+  // And update the element as tracks are added,
+  media.on(TRACK_ADDED, function trackAdded(track) {
+    // But stop updating the element if we've been detached.
+    if (!media.attachments.has(el)) {
+      return media.removeListener(TRACK_ADDED, trackAdded);
+    }
+    media._attachTrack(el, attachments, track);
+  });
+  media.on(TRACK_REMOVED, function trackRemoved(track) {
+    if (!media.attachments.has(el)) {
+      return media.removeListener(TRACK_REMOVED, trackRemoved);
+    }
+    media._detachTrack(el, attachments, track);
+  });
+  media.attachments.set(el, attachments);
+  return el;
+}
+
+function selectElementAndAttach(media, selector) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var el = document.querySelector(selector);
+  if (!el) {
+    throw new Error('document.querySelector returned nothing');
+  }
+  return media.attach(el);
+}
+
+function createDivAndAttach(media) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  return media.attach(document.createElement('div'));
+}
+
+/**
+ * Detach the {@link Media} from any and all previously attached HTMLElements.
+ * @returns {Array<HTMLElement>}
+ * @example
+ * var detachedMediaEls = media.detach();
+*//**
+ * Detach the {@link Media} from a previously attached HTMLElement.
+ * @param {HTMLElement} el - The HTMLElement to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var remoteMediaEl = document.getElementById('remote-media');
+ * media.detach(remoteMediaEl);
+*//**
+ * Detach the {@link Media} from a previously attached HTMLElement selected by
+ * <code>document.querySelector</code>.
+ * @param {string} selector - A query selector for the HTMLElement to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var detachedMediaEl = media.detach('div#remote-media');
+ */
+Media.prototype.detach = function detach(el) {
+  if (!el) {
+    return detachFromAllElements(this);
+  } else if (typeof el === 'string') {
+    return selectElementAndDetach(this, el);
+  }
+  return detachFromElement(this, el);
+};
+
+function detachFromElement(media, el) {
+  if (!media.attachments.has(el)) {
+    return el;
+  }
+  var attachments = media.attachments.get(el);
+  media.attachments.delete(el);
+  attachments.forEach(function(trackEl, track) {
+    media._detachTrack(el, attachments, track);
+  });
+  return el;
+}
+
+function selectElementAndDetach(media, selector) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var el = document.querySelector(selector);
+  if (!el) {
+    throw new Error('document.querySelector returned nothing');
+  }
+  return detachFromElement(media, el);
+}
+
+function detachFromAllElements(media) {
+  var els = [];
+  media.attachments.forEach(function(attachments, el) {
+    els.push(el);
+    detachFromElement(media, el);
+  });
+  return els;
+}
+
+/**
+ * A {@link Track} was added to this {@link Media} object.
+ * @param {Track} track - The {@link Track} that was added
+ * @event Media#trackAdded
+ */
+
+/**
+ * The dimensions of a {@link VideoTrack} on this {@link Media} object changed.
+ * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
+ * @event Media#trackDimensionsChanged
+ */
+
+/**
+ * A {@link Track} on this {@link Media} object was disabled.
+ * @param {Track} track - The {@link Track} that was disabled
+ * @event Media#trackDisabled
+ */
+
+/**
+ * A {@link Track} on this {@link Media} object was enabled.
+ * @param {Track} track - The {@link Track} that was enabled
+ * @event Media#trackEnabled
+ */
+
+/**
+ * A {@link Track} on this {@link Media} object ended.
+ * @param {Track} track - The {@link Track} that ended
+ * @event Media#trackEnded
+ */
+
+/**
+ * A {@link Track} was removed from this {@link Media} object.
+ * @param {Track} track - The {@link Track} that was removed
+ * @event Media#trackRemoved
+ */
+
+/**
+ * A {@link Track} on this {@link Media} object was started.
+ * @param {Track} track - The {@link Track} that was started
+ * @event Media#trackStarted
+ */
+
+module.exports = Media;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_localmedia.js.html b/dist/docs/media_localmedia.js.html new file mode 100644 index 000000000..9cef986c2 --- /dev/null +++ b/dist/docs/media_localmedia.js.html @@ -0,0 +1,352 @@ + + + + + JSDoc: Source: media/localmedia.js + + + + + + + + + + +
+ +

Source: media/localmedia.js

+ + + + + + +
+
+
'use strict';
+
+var getUserMedia = require('../webrtc/getusermedia');
+var inherits = require('util').inherits;
+var LocalAudioTrack = require('./track/localaudiotrack');
+var LocalVideoTrack = require('./track/localvideotrack');
+var Media = require('./');
+
+/**
+ * Construct a {@link LocalMedia} object.
+ * @class
+ * @classdesc A {@link LocalMedia} object is a {@link Media} object representing
+ *   {@link LocalAudioTrack}s and {@link LocalVideoTrack}s that your {@link Client} may
+ *   share in a {@link Conversation}.
+ * @extends Media
+ * @property {Map<Track.ID, LocalAudioTrack>} audioTracks - The {@link LocalAudioTrack}s on
+ *   this {@link Media} object
+ * @property {Map<Track.ID, LocalTrack>} tracks - The {@link LocalAudioTrack}s and
+ *   {@link LocalVideoTrack}s on this {@link Media} object
+ * @property {Map<Track.ID, LocalVideoTrack>} videoTracks - The {@link LocalVideoTrack}s on
+ *   this {@link Media} object
+ */
+function LocalMedia() {
+  if (!(this instanceof LocalMedia)) {
+    return new LocalMedia();
+  }
+  Media.call(this);
+  return this;
+}
+
+/**
+ * Get {@link LocalMedia}. By default, this requests a
+ * {@link LocalAudioTrack} and a {@link LocalVideoTrack} representing a microphone and
+ * camera.
+ * <br><br>
+ * This method calls <code>getUserMedia</code> internally. Pass in
+ * <code>options</code> to override the default behavior.
+ * @param {?LocalMedia.GetLocalMediaOptions}
+ *   [options={localStreamConstraints:{audio:true,video:true}}] - Options to override
+ *   {@link LocalMedia.getLocalMedia}'s default behavior
+ * @returns {Promise<LocalMedia>}
+ */
+LocalMedia.getLocalMedia = function getLocalMedia(options) {
+  options = options || {};
+  if (options.localMedia) {
+    return Promise.resolve(options.localMedia);
+  }
+  var localMedia = new LocalMedia();
+  if (options.localStream) {
+    return Promise.resolve(localMedia.addStream(options.localStream));
+  }
+  return getUserMedia(options.localStreamConstraints)
+    .then(function(mediaStream) {
+      return localMedia.addStream(mediaStream);
+    });
+};
+
+inherits(LocalMedia, Media);
+
+/**
+ * Adds a {@link LocalTrack} to the {@link LocalMedia} object, if not already added.
+ * @method
+ * @param {LocalTrack} track - The {@link LocalTrack} to add
+ * @returns {this}
+ * @fires Media#trackAdded
+ */
+LocalMedia.prototype.addTrack = Media.prototype._addTrack;
+
+/**
+ * Removes a {@link LocalTrack} from the {@link LocalMedia} object, if it was added.
+ * @method
+ * @param {LocalTrack} track - The {@link LocalTrack} to remove
+ * @param {?boolean} [stop=true] - Whether or not to call
+ *   {@link LocalTrack#stop}
+ * @returns {this}
+ * @fires Media#trackRemoved
+ */
+LocalMedia.prototype.removeTrack = Media.prototype._removeTrack;
+
+/**
+ * Adds a {@link LocalAudioTrack} representing your browser's microphone to the
+ * {@link LocalMedia} object, if not already added.
+ * <br><br>
+ * Internally, this calls <code>getUserMedia({ audio: true })</code>.
+ * @returns {Promise<LocalAudioTrack>}
+ * @fires Media#trackAdded
+ */
+LocalMedia.prototype.addMicrophone = function addMicrophone() {
+  var self = this;
+  var microphone = null;
+  this.audioTracks.forEach(function(audioTrack) {
+    microphone = microphone || audioTrack;
+  });
+  if (microphone) {
+    return Promise.resolve(microphone);
+  }
+  return getUserMedia({ audio: true, video: false })
+    .then(function gotMicrophone(mediaStream) {
+      var audioTracks = mediaStream.getAudioTracks();
+      var mediaStreamTrack = audioTracks[0];
+      var audioTrack = new LocalAudioTrack(mediaStream, mediaStreamTrack);
+      self._addTrack(audioTrack);
+      return audioTrack;
+    });
+};
+
+/**
+ * Removes the {@link LocalAudioTrack} representing your browser's microphone, if it
+ * has been added.
+ * @returns {?LocalAudioTrack}
+ * @fires Media#trackRemoved
+ */
+LocalMedia.prototype.removeMicrophone = function removeMicrophone() {
+  var microphone = null;
+  this.audioTracks.forEach(function(audioTrack) {
+    microphone = microphone || audioTrack;
+  });
+  if (microphone) {
+    return this._removeTrack(microphone);
+  }
+  return null;
+};
+
+/**
+ * Adds a {@link LocalVideoTrack} representing your browser's camera to the
+ * {@link LocalMedia} object, if not already added.
+ * <br><br>
+ * Internally, this calls <code>getUserMedia({ video: true })</code>.
+ * @returns {Promise<LocalVideoTrack>}
+ * @fires Media#trackAdded
+ */
+LocalMedia.prototype.addCamera = function addCamera() {
+  var self = this;
+  var camera = null;
+  this.videoTracks.forEach(function(videoTrack) {
+    camera = camera || videoTrack;
+  });
+  if (camera) {
+    return Promise.resolve(camera);
+  }
+  return getUserMedia({ audio: false, video: true })
+    .then(function gotCamera(mediaStream) {
+      var videoTracks = mediaStream.getVideoTracks();
+      var mediaStreamTrack = videoTracks[0];
+      var videoTrack = new LocalVideoTrack(mediaStream, mediaStreamTrack);
+      self._addTrack(videoTrack);
+      return videoTrack;
+    });
+};
+
+/**
+ * Removes the {@link LocalVideoTrack} representing your browser's camera, if it
+ * has been added.
+ * @returns {?LocalVideoTrack}
+ * @fires Media#trackRemoved
+ */
+LocalMedia.prototype.removeCamera = function removeCamera() {
+  var camera = null;
+  this.videoTracks.forEach(function(videoTrack) {
+    camera = camera || videoTrack;
+  });
+  if (camera) {
+    return this._removeTrack(camera);
+  }
+  return null;
+};
+
+/**
+ * Add a <code>MediaStream</code> to the {@link LocalMedia} object, constructing
+ * {@link LocalTrack}s as necessary for each <code>MediaStreamTrack</code> contained
+ * within.
+ * @param {MediaStream} mediaStream - The <code>MediaStream</code> to add
+ * @returns {this}
+ * @fires Media#trackAdded
+ */
+LocalMedia.prototype.addStream = function addStream(mediaStream) {
+  mediaStream.getAudioTracks().forEach(function(mediaStreamTrack) {
+    var audioTrack = new LocalAudioTrack(mediaStream, mediaStreamTrack);
+    this._addTrack(audioTrack);
+  }, this);
+  mediaStream.getVideoTracks().forEach(function(mediaStreamTrack) {
+    var videoTrack = new LocalVideoTrack(mediaStream, mediaStreamTrack);
+    this._addTrack(videoTrack);
+  }, this);
+  return this;
+};
+
+/**
+ * Remove a <code>MediaStream</code> from the {@link LocalMedia} object. This
+ * will remove any {@link LocalTrack}s corresponding to
+ * <code>MediaStreamTrack</code>s contained within the <code>MediaStream</code>.
+ * @param {MediaStream} mediaStream - The <code>MediaStream</code> to remove
+ * @param {?boolean} [stop=true] - Whether or not to call
+ *   {@link LocalTrack#stop} on the corresponding {@link LocalTrack}s
+ * @returns {this}
+ * @fires Media#trackRemoved
+ */
+LocalMedia.prototype.removeStream = function removeStream(mediaStream, stop) {
+  mediaStream.getTracks().forEach(function(mediaStreamTrack) {
+    var track = this.tracks.get(mediaStreamTrack.id);
+    if (track) {
+      this.removeTrack(track, stop);
+    }
+  }, this);
+  return this;
+};
+
+LocalMedia.prototype._removeTrack = function _removeTrack(track, stop) {
+  if (typeof stop === 'boolean' ? stop : true) {
+    track.stop();
+  }
+  try {
+    track.mediaStream.removeTrack(track.mediaStreamTrack);
+  } catch (error) {
+    // Firefox doesn't support removeStream/removeTrack, so we can't yet truly
+    // remove and renegotiate media.
+  }
+  Media.prototype._removeTrack.call(this, track);
+  return track;
+};
+
+/**
+ * Disable every {@link LocalAudioTrack} on this {@link LocalMedia} object.
+ * @returns {this}
+ * @fires Media#trackDisabled
+*//**
+ * Disable or enable every {@link LocalAudioTrack} on this {@link LocalMedia} object.
+ * @param {?boolean} enabled - Specify false to enable the {@link LocalAudioTrack}s
+ * @returns {this}
+ * @fires Media#trackDisabled
+ * @fires Media#trackEnabled
+ */
+LocalMedia.prototype.mute = function mute(muted) {
+  muted = typeof muted === 'boolean' ? muted : true;
+  this.audioTracks.forEach(function(track) {
+    track.enable(!muted);
+  });
+  return this;
+};
+
+/**
+ * Disable every {@link LocalVideoTrack} on this {@link LocalMedia} object.
+ * @returns {this}
+ * @fires Media#trackDisabled
+*//**
+ * Disable or enable every {@link LocalVideoTrack} on this {@link LocalMedia} object.
+ * @param {?boolean} enabled - Specify false to enable the {@link LocalVideoTrack}s
+ * @returns {this}
+ * @fires Media#trackDisabled
+ * @fires Media#trackEnabled
+ */
+LocalMedia.prototype.pause = function pause(paused) {
+  paused = typeof paused === 'boolean' ? paused : true;
+  this.videoTracks.forEach(function(track) {
+    track.enable(!paused);
+  });
+  return this;
+};
+
+/**
+ * Stop all {@link LocalAudioTrack}s and {@link LocalVideoTrack}s on this {@link LocalMedia} object.
+ * @returns {this}
+ * @fires Media#trackEnded
+ */
+LocalMedia.prototype.stop = function stop() {
+  this.tracks.forEach(function(track) {
+    track.stop();
+  });
+  return this;
+};
+
+/**
+ * Enable every {@link LocalAudioTrack} on this {@link LocalMedia} object.
+ * @returns {this}
+ * @fires Media#trackEnabled
+ */
+LocalMedia.prototype.unmute = function unmute() {
+  return this.mute(false);
+};
+
+/**
+ * Enable every {@link LocalVideoTrack} on this {@link LocalMedia} object.
+ * @returns {this}
+ * @fires Media#trackEnabled
+ */
+LocalMedia.prototype.unpause = function unpause() {
+  return this.pause(false);
+};
+
+/**
+ * You may pass these options to {@link LocalMedia.getLocalMedia} to
+ * override the default behavior.
+ * @typedef {object} LocalMedia.GetLocalMediaOptions
+ * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing
+ *   {@link LocalMedia} object
+ * @property {?MediaStream} [localStream=null] - Set to reuse an existing
+ *   <code>MediaStream</code>
+ * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to
+ *   override the parameters passed to <code>getUserMedia</code> when neither
+ *   <code>localMedia</code> nor <code>localStream</code> are provided
+ */
+
+module.exports = LocalMedia;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_audiotrack.js.html b/dist/docs/media_track_audiotrack.js.html new file mode 100644 index 000000000..e651e4b67 --- /dev/null +++ b/dist/docs/media_track_audiotrack.js.html @@ -0,0 +1,123 @@ + + + + + JSDoc: Source: media/track/audiotrack.js + + + + + + + + + + +
+ +

Source: media/track/audiotrack.js

+ + + + + + +
+
+
'use strict';
+
+var inherits = require('util').inherits;
+var Track = require('./');
+
+/**
+ * Construct an {@link AudioTrack} from MediaStream and MediaStreamTrack.
+ * @class
+ * @classdesc An {@link AudioTrack} is a {@link Track} representing audio.
+ * @extends Track
+ * @param {MediaStream} mediaStream
+ * @param {MediaStreamTrack} mediaStreamTrack
+ * @property {Set<HTMLElement>} attachments - The &lt;audio&gt; elements this
+ *   {@link AudioTrack} is currently attached to (managed by
+ *   {@link AudioTrack#attach})
+ */
+function AudioTrack(mediaStream, mediaStreamTrack) {
+  Track.call(this, mediaStream, mediaStreamTrack);
+}
+
+inherits(AudioTrack, Track);
+
+/**
+ * Attach the {@link AudioTrack} to a newly created &lt;audio&gt; element.
+ * @method
+ * @returns {HTMLElement}
+ * @example
+ * var remoteAudioEl = audioTrack.attach();
+ * document.getElementById('div#remote-audio-container').appendChild(remoteAudioEl);
+*//**
+ * Attach the {@link AudioTrack} to an existing &lt;audio&gt; element.
+ * @method
+ * @param {HTMLElement} audio - The &lt;audio&gt; element to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteAudioEl = document.getElementById('remote-audio');
+ * audioTrack.attach(remoteAudioEl);
+*//**
+ * Attach the {@link AudioTrack} to a &lt;audio&gt; element selected by
+ * <code>document.querySelector</code>.
+ * @method
+ * @param {string} selector - A query selector for the &lt;audio&gt; element to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteAudioEl = audioTrack.attach('audio#remote-audio');
+ */
+AudioTrack.prototype.attach = Track.prototype.attach;
+
+/**
+ * Detach the {@link AudioTrack} from any and all previously attached &lt;audio&gt; elements.
+ * @method
+ * @returns {Array<HTMLElement>}
+ * @example
+ * var detachedAudioEls = audioTrack.detach();
+*//**
+ * Detach the {@link AudioTrack} from a previously attached &lt;audio&gt; element.
+ * @method
+ * @param {HTMLElement} audio - The &lt;audio&gt; element to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var remoteAudioEl = document.getElementById('remote-audio');
+ * audioTrack.detach(remoteAudioEl);
+*//**
+ * Detach the {@link AudioTrack} from a previously attached &lt;audio&gt; element selected by
+ * <code>document.querySelector</code>.
+ * @method
+ * @param {string} selector - A query selector for the &lt;audio&gt; element to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var detachedAudioEl = media.detach('div#remote-audio');
+ */
+AudioTrack.prototype.detach = Track.prototype.detach;
+
+module.exports = AudioTrack;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_index.js.html b/dist/docs/media_track_index.js.html new file mode 100644 index 000000000..09460e3e5 --- /dev/null +++ b/dist/docs/media_track_index.js.html @@ -0,0 +1,347 @@ + + + + + JSDoc: Source: media/track/index.js + + + + + + + + + + +
+ +

Source: media/track/index.js

+ + + + + + +
+
+
'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+
+/**
+ * Construct a {@link Track} from a MediaStream and MediaStreamTrack.
+ * @class
+ * @classdesc A {@link Track} represents audio or video that can be sent to or
+ * received from a {@link Conversation}. {@link Track}s abstract away the notion
+ * of MediaStream and MediaStreamTrack.
+ * @param {MediaStream} mediaStream
+ * @param {MediaStreamTrack} mediaStreamTrack
+ * @property {Track.ID} id - This {@link Track}'s ID
+ * @property {boolean} isEnded - Whether or not the {@link Track} has ended
+ * @property {boolean} isStarted - Whether or not the {@link Track} has started
+ * @property {boolean} isEnabled - Whether or not the {@link Track} is enabled
+ *  (i.e., whether it is paused or muted)
+ * @property {string} kind - The kind of the underlying
+ *   {@link MediaStreamTrack}; e.g. "audio" or "video"
+ * @property {MediaStream} mediaStream - The underlying MediaStream
+ * @property {MediaStreamTrack} mediaStreamTrack - The underlying
+ *   MediaStreamTrack
+ * @fires Track#disabled
+ * @fires Track#enabled
+ * @fires Track#ended
+ * @fires Track#started
+ */
+function Track(mediaStream, mediaStreamTrack) {
+  EventEmitter.call(this);
+  var isEnabled = true;
+  var isEnded = false;
+  var isStarted = false;
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    _isEnabled: {
+      get: function() {
+        return isEnabled;
+      },
+      set: function(_isEnabled) {
+        isEnabled = _isEnabled;
+      }
+    },
+    _isEnded: {
+      get: function() {
+        return isEnded;
+      },
+      set: function(_isEnded) {
+        isEnded = _isEnded;
+      }
+    },
+    _isStarted: {
+      get: function() {
+        return isStarted;
+      },
+      set: function(_isStarted) {
+        isStarted = _isStarted;
+      }
+    },
+    attachments: {
+      value: new Set()
+    },
+    id: {
+      enumerable: true,
+      value: mediaStreamTrack.id
+    },
+    isEnabled: {
+      get: function() {
+        return isEnabled;
+      }
+    },
+    isEnded: {
+      get: function() {
+        return isEnded;
+      }
+    },
+    isStarted: {
+      get: function() {
+        return isStarted;
+      }
+    },
+    kind: {
+      enumerable: true,
+      value: mediaStreamTrack.kind
+    },
+    mediaStream: {
+      enumerable: true,
+      value: mediaStream
+    },
+    mediaStreamTrack: {
+      enumerable: true,
+      value: mediaStreamTrack
+    }
+  });
+  var self = this;
+  mediaStreamTrack.onended = function onended() {
+    /* eslint no-use-before-define:0 */
+    self.emit(ENDED, self);
+  };
+  emitStartedEvent(this);
+}
+
+var DISABLED = Track.DISABLED = 'disabled';
+var ENABLED = Track.ENABLED = 'enabled';
+var ENDED = Track.ENDED = 'ended';
+var STARTED = Track.STARTED = 'started';
+
+function emitStartedEvent(track) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var elem = document.createElement(track.kind);
+  elem.muted = true;
+  elem.oncanplay = function oncanplay() {
+    track.detach(elem);
+    track._isStarted = true;
+    if (track.kind === 'video') {
+      track.dimensions.width = elem.videoWidth;
+      track.dimensions.height = elem.videoHeight;
+    }
+    track.emit(STARTED, track);
+    elem.oncanplay = null;
+  };
+  track.once(ENDED, function() {
+    track.detach(elem);
+    elem.oncanplay = null;
+  });
+  return track.attach(elem);
+}
+
+function attachAudio(audio, mediaStream) {
+  if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
+    if (typeof navigator.webkitGetUserMedia === 'function') {
+      var vendorURL = window.URL || window.webkitURL;
+      audio.src = vendorURL.createObjectURL(mediaStream);
+    } else if (typeof navigator.mozGetUserMedia === 'function') {
+      audio.mozSrcObject = mediaStream;
+    }
+    audio.play();
+    return audio;
+  }
+  throw new Error('Cannot attach to <audio> element');
+}
+
+Track.attachAudio = attachAudio;
+
+function attachVideo(video, mediaStream) {
+  if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
+    if (typeof navigator.webkitGetUserMedia === 'function') {
+      var vendorURL = window.URL || window.webkitURL;
+      video.src = vendorURL.createObjectURL(mediaStream);
+    } else if (typeof navigator.mozGetUserMedia === 'function') {
+      video.mozSrcObject = mediaStream;
+    }
+    video.muted = true;
+    video.play();
+    return video;
+  }
+  throw new Error('Cannot attach to <video> element');
+}
+
+Track.attachVideo = attachVideo;
+
+inherits(Track, EventEmitter);
+
+Track.prototype._enable = function _enable(enabled) {
+  if (this._isEnabled !== enabled) {
+    this._isEnabled = enabled;
+    this.emit(enabled ? ENABLED : DISABLED, this);
+  }
+  return this;
+};
+
+Track.prototype.attach = function attach() {
+  var args = [].slice.call(arguments);
+  args.unshift(this);
+  return attachAudioOrVideoTrack.apply(this, args);
+};
+
+Track.prototype.detach = function detach() {
+  var args = [].slice.call(arguments);
+  args.unshift(this);
+  return detachAudioOrVideoTrack.apply(this, args);
+};
+
+function attachAudioOrVideoTrack(track, el) {
+  if (!el) {
+    return createElementAndAttachAudioOrVideoTrack(track);
+  } else if (typeof el === 'string') {
+    return selectElementAndAttachAudioOrVideoTrack(track, el);
+  }
+  return attachAudioOrVideoTrackToElement(track, el);
+}
+
+function attachAudioOrVideoTrackToElement(track, el) {
+  if (track.attachments.has(el)) {
+    return el;
+  }
+  var attachMethod = track.kind === 'audio' ? attachAudio : attachVideo;
+  attachMethod(el, track.mediaStream);
+  track.attachments.add(el);
+  return el;
+}
+
+function selectElementAndAttachAudioOrVideoTrack(track, selector) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var el = document.querySelector(selector);
+  if (!el) {
+    throw new Error('document.querySelector returned nothing');
+  }
+  return attachAudioOrVideoTrackToElement(track, el);
+}
+
+function createElementAndAttachAudioOrVideoTrack(track) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var el = document.createElement(track.kind);
+  return attachAudioOrVideoTrackToElement(track, el);
+}
+
+function detachAudioOrVideoTrack(track, el) {
+  if (!el) {
+    return detachAudioOrVideoTrackFromAllElements(track);
+  } else if (typeof el === 'string') {
+    return selectElementAndDetachAudioOrVideoTrack(track, el);
+  }
+  return detachAudioOrVideoTrackFromElement(track, el);
+}
+
+Track.detachAudioOrVideoTrack = detachAudioOrVideoTrack;
+
+function detachAudioOrVideoTrackFromElement(track, el) {
+  if (!track.attachments.has(el)) {
+    return el;
+  }
+  el.removeAttribute('src');
+  track.attachments.delete(el);
+  return el;
+}
+
+function selectElementAndDetachAudioOrVideoTrack(track, selector) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var el = document.querySelector(selector);
+  if (!el) {
+    throw new Error('document.querySelector returned nothing');
+  }
+  return detachAudioOrVideoTrackFromElement(track, el);
+}
+
+function detachAudioOrVideoTrackFromAllElements(track) {
+  var els = [];
+  track.attachments.forEach(function(el) {
+    els.push(el);
+    detachAudioOrVideoTrackFromElement(track, el);
+  });
+  return els;
+}
+
+/**
+ * The {@link Track} ID is a string identifier for the {@link Track}.
+ * @type string
+ * @typedef Track.ID
+ */
+
+/**
+ * The {@link Track} was disabled. For {@link AudioTrack}s this means
+ * "muted", and for {@link VideoTrack}s this means "paused".
+ * @param {Track} track - The {@link Track} that was disabled
+ * @event Track#disabled
+ */
+
+/**
+ * The {@link Track} was enabled. For {@link AudioTrack}s this means
+ * "unmuted", and for {@link VideoTrack}s this means "unpaused".
+ * @param {Track} track - The {@link Track} that was enabled
+ * @event Track#enabled
+ */
+
+/**
+ * The {@link Track} ended. This means that the {@link Track} will no longer
+ * playback audio or video.
+ * @param {Track} track - The {@link Track} that ended
+ * @event Track#ended
+ */
+
+/**
+ * The {@link Track} started. This means that the {@link Track} contains
+ * enough audio or video to begin playback.
+ * @param {Track} track - The {@link Track} that started
+ * @event Track#started
+ */
+
+module.exports = Track;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_localaudiotrack.js.html b/dist/docs/media_track_localaudiotrack.js.html new file mode 100644 index 000000000..c141197cd --- /dev/null +++ b/dist/docs/media_track_localaudiotrack.js.html @@ -0,0 +1,101 @@ + + + + + JSDoc: Source: media/track/localaudiotrack.js + + + + + + + + + + +
+ +

Source: media/track/localaudiotrack.js

+ + + + + + +
+
+
'use strict';
+
+var AudioTrack = require('./audiotrack');
+var inherits = require('util').inherits;
+var LocalTrack = require('./localtrack');
+
+/**
+ * Construct a {@link LocalAudioTrack} from MediaStream and MediaStreamTrack.
+ * @class
+ * @classdesc A {@link LocalAudioTrack} is an {@link AudioTrack} representing
+ * audio that your {@link Client} sends to a {@link Conversation}.
+ * @extends {AudioTrack}
+ * @extends {LocalTrack}
+ * @param {MediaStream} mediaStream
+ * @param {MediaStreamTrack} mediaStreamTrack
+ */
+function LocalAudioTrack(mediaStream, mediaStreamTrack) {
+  if (!(this instanceof LocalAudioTrack)) {
+    return new LocalAudioTrack(mediaStream, mediaStreamTrack);
+  }
+  AudioTrack.call(this, mediaStream, mediaStreamTrack);
+}
+
+inherits(LocalAudioTrack, AudioTrack);
+
+/**
+ * Disable the {@link LocalAudioTrack}. This is effectively "mute".
+ * @method
+ * @returns {this}
+ * @fires Track#disabled
+ */
+LocalAudioTrack.prototype.disable = LocalTrack.prototype.disable;
+
+/**
+ * Enable the {@link LocalAudioTrack}. This is effectively "unmute".
+ * @method
+ * @returns {this}
+ * @fires Track#enabled
+*//**
+ * Enable or disable the {@link LocalAudioTrack}. This is effectively "unmute" or
+ * "mute".
+ * @method
+ * @param {boolean} [enabled] - Specify false to mute the {@link LocalAudioTrack}
+ * @returns {this}
+ * @fires Track#disabled
+ * @fires Track#enabled
+ */
+LocalAudioTrack.prototype.enable = LocalTrack.prototype.enable;
+
+LocalAudioTrack.prototype.stop = LocalTrack.prototype.stop;
+
+module.exports = LocalAudioTrack;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_localtrack.js.html b/dist/docs/media_track_localtrack.js.html new file mode 100644 index 000000000..45ab660ef --- /dev/null +++ b/dist/docs/media_track_localtrack.js.html @@ -0,0 +1,103 @@ + + + + + JSDoc: Source: media/track/localtrack.js + + + + + + + + + + +
+ +

Source: media/track/localtrack.js

+ + + + + + +
+
+
'use strict';
+
+/**
+ * @class
+ * @classdesc A {@link LocalTrack} represents audio or video that your
+ * {@link Client} is sending to a {@link Conversation}. As such, it can be
+ * enabled and disabled with {@link LocalTrack#enable} and
+ * {@link LocalTrack#disable} or stopped completely with
+ * {@link LocalTrack#stop}.
+ * @extends Track
+ */
+function LocalTrack() {
+  // A LocalTrack should never be constructed directly; instead,
+  // LocalAudioTrack and LocalVideoTrack borrow methods from LocalTrack's
+  // prototype.
+}
+
+/**
+ * Enable the {@link LocalTrack}.
+ * @returns {this}
+ * @fires Track#enabled
+*//**
+ * Enable or disable the {@link LocalTrack}.
+ * @param {boolean} [enabled] - Specify false to disable the {@link LocalTrack}
+ * @returns {this}
+ * @fires Track#disabled
+ * @fires Track#enabled
+ */
+LocalTrack.prototype.enable = function enable(enabled) {
+  enabled = typeof enabled === 'boolean' ? enabled : true;
+  this.mediaStreamTrack.enabled = enabled;
+  return this._enable(enabled);
+};
+
+/**
+ * Disable the {@link LocalTrack}.
+ * @returns {this}
+ * @fires Track#disabled
+ */
+LocalTrack.prototype.disable = function disable() {
+  return this.enable(false);
+};
+
+/**
+ * Stop sending this {@link LocalTrack}.
+ * @returns {this}
+ * @fires Track#ended
+ */
+LocalTrack.prototype.stop = function stop() {
+  this.mediaStreamTrack.stop();
+  return this;
+};
+
+module.exports = LocalTrack;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_localvideotrack.js.html b/dist/docs/media_track_localvideotrack.js.html new file mode 100644 index 000000000..ea10a1121 --- /dev/null +++ b/dist/docs/media_track_localvideotrack.js.html @@ -0,0 +1,101 @@ + + + + + JSDoc: Source: media/track/localvideotrack.js + + + + + + + + + + +
+ +

Source: media/track/localvideotrack.js

+ + + + + + +
+
+
'use strict';
+
+var inherits = require('util').inherits;
+var LocalTrack = require('./localtrack');
+var VideoTrack = require('./videotrack');
+
+/**
+ * Construct a {@link LocalVideoTrack} from MediaStream and MediaStreamTrack.
+ * @class
+ * @classdesc A {@link LocalVideoTrack} is a {@link VideoTrack} representing
+ * audio that your {@link Client} sends to a {@link Conversation}.
+ * @extends {VideoTrack}
+ * @extends {LocalTrack}
+ * @param {MediaStream} mediaStream
+ * @param {MediaStreamTrack} mediaStreamTrack
+ */
+function LocalVideoTrack(mediaStream, mediaStreamTrack) {
+  if (!(this instanceof LocalVideoTrack)) {
+    return new LocalVideoTrack(mediaStream, mediaStreamTrack);
+  }
+  VideoTrack.call(this, mediaStream, mediaStreamTrack);
+}
+
+inherits(LocalVideoTrack, VideoTrack);
+
+/**
+ * Enable the {@link LocalVideoTrack}. This is effectively "unpause".
+ * @method
+ * @returns {this}
+ * @fires Track#enabled
+*//**
+ * Enable or disable the {@link LocalVideoTrack}. This is effectively "unpause" or
+ * "pause".
+ * @method
+ * @param {boolean} [enabled] - Specify false to pause the {@link LocalVideoTrack}
+ * @returns {this}
+ * @fires Track#disabled
+ * @fires Track#enabled
+ */
+LocalVideoTrack.prototype.enable = LocalTrack.prototype.enable;
+
+/**
+ * Disable the {@link LocalVideoTrack}. This is effectively "pause".
+ * @method
+ * @returns {this}
+ * @fires Track#disabled
+ */
+LocalVideoTrack.prototype.disable = LocalTrack.prototype.disable;
+
+LocalVideoTrack.prototype.stop = LocalTrack.prototype.stop;
+
+module.exports = LocalVideoTrack;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/media_track_videotrack.js.html b/dist/docs/media_track_videotrack.js.html new file mode 100644 index 000000000..8c655563c --- /dev/null +++ b/dist/docs/media_track_videotrack.js.html @@ -0,0 +1,196 @@ + + + + + JSDoc: Source: media/track/videotrack.js + + + + + + + + + + +
+ +

Source: media/track/videotrack.js

+ + + + + + +
+
+
'use strict';
+
+var inherits = require('util').inherits;
+var Track = require('./');
+
+/**
+ * Construct a {@link VideoTrack} from MediaStream and MediaStreamTrack.
+ * @class
+ * @classdesc A {@link VideoTrack} is a {@link Track} representing video.
+ * @extends Track
+ * @param {MediaStream} mediaStream
+ * @param {MediaStreamTrack} mediaStreamTrack
+ * @property {Set<HTMLElement>} attachments - The &lt;video&gt; elements this
+ *   {@link VideoTrack} is currently attached to (managed by
+ *   {@link VideoTrack#attach})
+ * @property {VideoTrack#Dimensions} dimensions - The {@link VideoTrack}'s {@link VideoTrack#Dimensions}
+ * @fires VideoTrack#dimensionsChanged
+ */
+function VideoTrack(mediaStream, mediaStreamTrack) {
+  if (!(this instanceof VideoTrack)) {
+    return new VideoTrack(mediaStream, mediaStreamTrack);
+  }
+  Track.call(this, mediaStream, mediaStreamTrack);
+  Object.defineProperties(this, {
+    dimensions: {
+      enumerable: true,
+      value: {
+        width: null,
+        height: null
+      }
+    }
+  });
+  mediaStream.onaddtrack = function onaddtrack() {
+    this.attachments.forEach(function(video) {
+      Track.detachAudioOrVideoTrack(this, video);
+      Track.attachVideo(video, mediaStream);
+    }, this);
+  }.bind(this);
+  emitDimensionsChangedEvents(this);
+  return this;
+}
+
+var DIMENSIONS_CHANGED = VideoTrack.DIMENSIONS_CHANGED = 'dimensionsChanged';
+
+function emitDimensionsChangedEvents(track) {
+  if (typeof document === 'undefined') {
+    throw new Error('document is undefined');
+  }
+  var elem = document.createElement(track.kind);
+  elem.muted = true;
+  elem.onloadedmetadata = function onloadedmetadata() {
+    if (dimensionsChanged(track, elem)) {
+      track.dimensions.width = elem.videoWidth;
+      track.dimensions.height = elem.videoHeight;
+    }
+  };
+  elem.onresize = function onresize() {
+    if (dimensionsChanged(track, elem)) {
+      track.dimensions.width = elem.videoWidth;
+      track.dimensions.height = elem.videoHeight;
+      if (track.isStarted) {
+        track.emit(DIMENSIONS_CHANGED, track);
+      }
+    }
+  };
+  track.once(Track.ENDED, function() {
+    track.detach(elem);
+    elem.onloadedmetadata = null;
+  });
+  return track.attach(elem);
+}
+
+function dimensionsChanged(track, elem) {
+  return track.dimensions.width !== elem.videoWidth
+    || track.dimensions.height !== elem.videoHeight;
+}
+
+inherits(VideoTrack, Track);
+
+/**
+ * Attach the {@link VideoTrack} to a newly created &lt;video&gt; element.
+ * @method
+ * @returns {HTMLElement}
+ * @example
+ * var remoteVideoEl = videoTrack.attach();
+ * document.getElementById('div#remote-video-container').appendChild(remoteVideoEl);
+*//**
+ * Attach the {@link VideoTrack} to an existing &lt;video&gt; element.
+ * @method
+ * @param {HTMLElement} video - The &lt;video&gt; element to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteVideoEl = document.getElementById('remote-video');
+ * videoTrack.attach(remoteVideoEl);
+*//**
+ * Attach the {@link VideoTrack} to a &lt;video&gt; element selected by
+ * <code>document.querySelector</code>.
+ * @method
+ * @param {string} selector - A query selector for the &lt;video&gt; element to attach to
+ * @returns {HTMLElement}
+ * @example
+ * var remoteVideoEl = videoTrack.attach('video#remote-video');
+ */
+VideoTrack.prototype.attach = Track.prototype.attach;
+
+/**
+ * Detach the {@link VideoTrack} from any and all previously attached &lt;video&gt; elements.
+ * @method
+ * @returns {Array<HTMLElement>}
+ * @example
+ * var detachedVideoEls = videoTrack.detach();
+*//**
+ * Detach the {@link VideoTrack} from a previously attached &lt;video&gt; element.
+ * @method
+ * @param {HTMLElement} video - The &lt;video&gt; element to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var remoteVideoEl = document.getElementById('remote-video');
+ * videoTrack.detach(remoteVideoEl);
+*//**
+ * Detach the {@link VideoTrack} from a previously attached &lt;video&gt; element selected by
+ * <code>document.querySelector</code>.
+ * @method
+ * @param {string} selector - A query selector for the &lt;video&gt; element to detach from
+ * @returns {HTMLElement}
+ * @example
+ * var detachedVideoEl = media.detach('div#remote-video');
+ */
+VideoTrack.prototype.detach = Track.prototype.detach;
+
+/**
+ * A {@link VideoTrack}'s width and height.
+ * @typedef VideoTrack#Dimensions
+ * @type {Object}
+ * @property {?number} width - The {@link VideoTrack}'s width or null if the
+ *   {@link VideoTrack} has not yet started
+ * @property {?number} height - The {@link VideoTrack}'s height or null if the
+ *   {@link VideoTrack} has not yet started
+ */
+
+/**
+ * The {@link VideoTrack}'s dimensions changed.
+ * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
+ * @event VideoTrack#dimensionsChanged
+ */
+
+module.exports = VideoTrack;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/outgoinginvite.js.html b/dist/docs/outgoinginvite.js.html new file mode 100644 index 000000000..adcccbeb1 --- /dev/null +++ b/dist/docs/outgoinginvite.js.html @@ -0,0 +1,354 @@ + + + + + JSDoc: Source: outgoinginvite.js + + + + + + + + + + +
+ +

Source: outgoinginvite.js

+ + + + + + +
+
+
'use strict';
+
+var CancelablePromise = require('./util/cancelablepromise');
+var constants = require('./util/constants');
+var Conversation = require('./conversation');
+var E = constants.twilioErrors;
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+var InviteClientTransaction = require('./signaling/invitetransaction/inviteclienttransaction');
+var LocalMedia = require('./media/localmedia');
+var TimeoutPromise = require('./util/timeoutpromise');
+var util = require('./util');
+
+/**
+ * Construct an {@link OutgoingInvite}.
+ * @class
+ * @classdesc An {@link OutgoingInvite} is a Promise that eventually resolves
+ *   to a {@link Conversation} if one or more {@link Participant}s accept the
+ *   corresponding {@link IncomingInvite}. An {@link OutgoingInvite} may be
+ *   canceled up until a {@link Participant} has accepted it.
+ *   <br><br>
+ *   {@link OutgoingInvite}s are returned by {@link Client#inviteToConversation}.
+ * @extends Promise<Conversation>
+ * @param {UserAgent} userAgent - the {@link UserAgent} to perform the invite
+ * @param {Array<string>} participants - the {@link Participant} identities to
+ *   invite
+ * @param {?object} [options]
+ * @property {string} status - The status of this {@link OutgoingInvite}, either
+ *   "accepted", "rejected", "canceled", "failed", or "pending"
+ * @property {Array<string>} to - The {@link Participant} identities
+ *   invited by this {@link OutgoingInvite}
+ * @fires OutgoingInvite#accepted
+ * @fires OutgoingInvite#canceled
+ * @fires OutgoingInvite#failed
+ * @fires OutgoingInvite#rejected
+ */
+function OutgoingInvite(userAgent, participants, options) {
+  if (!(this instanceof OutgoingInvite)) {
+    return new OutgoingInvite(userAgent, participants, options);
+  }
+  EventEmitter.call(this);
+
+  options = util.withDefaults({ }, options);
+
+  var deferred = util.defer();
+  var timeoutPromise = new TimeoutPromise(deferred.promise);
+  var cancelablePromise = new CancelablePromise(timeoutPromise);
+
+  var shouldStopLocalMediaOnFailure = false;
+
+  var conversation = new Conversation(options);
+  var cookie = util.makeUUID();
+  var ict;
+  var localMedia = null;
+  var self = this;
+  var status = 'pending';
+
+  // TimeoutPromise and CancelablePromise reject with their own errors;
+  // instead, reject with TwilioErrors describing the state of the
+  // OutgoingInvite.
+  //
+  // Also, stop any LocalMedia acquired (otherwise, the user will never get a
+  // handle to it and cannot stop it themselves).
+  var promise = cancelablePromise.catch(function cancelablePromiseRejected(reason) {
+    if (shouldStopLocalMediaOnFailure && localMedia) {
+      localMedia.stop();
+    }
+    if (timeoutPromise.isTimedOut) {
+      reason = E.CONVERSATION_INVITE_FAILED;
+    } else if (cancelablePromise.isCanceled) {
+      reason = E.CONVERSATION_INVITE_CANCELED;
+    }
+    throw reason;
+  });
+
+  Object.defineProperties(this, {
+    _cancelablePromise: {
+      value: cancelablePromise
+    },
+    _conversation: {
+      value: conversation
+    },
+    _cookie: {
+      value: cookie
+    },
+    _localMedia: {
+      get: function() {
+        return localMedia;
+      },
+      set: function(_localMedia) {
+        localMedia = _localMedia;
+      }
+    },
+    _onReceiveInvite: {
+      value: setupConversation
+    },
+    _options: {
+      value: options
+    },
+    _promise: {
+      value: promise
+    },
+    _status: {
+      get: function() {
+        return status;
+      },
+      set: function(_status) {
+        status = _status;
+      }
+    },
+    _timeoutPromise: {
+      value: timeoutPromise
+    },
+    status: {
+      enumerable: true,
+      get: function() {
+        return status;
+      }
+    },
+    to: {
+      enumerable: true,
+      value: participants
+    }
+  });
+
+  getLocalMedia()
+    .then(setLocalMedia)
+    .then(inviteParticipants)
+    .then(setupConversation, inviteParticipantsFailed);
+
+  function getLocalMedia() {
+    if (!options.localMedia && !options.localStream) {
+      shouldStopLocalMediaOnFailure = true;
+    }
+    return LocalMedia.getLocalMedia(options);
+  }
+
+  function setLocalMedia(_localMedia) {
+    localMedia = _localMedia;
+    conversation._localMedia = localMedia;
+    conversation._shouldStopLocalMediaOnDisconnect = shouldStopLocalMediaOnFailure;
+    options.localMedia = localMedia;
+    timeoutPromise.start(constants.DEFAULT_CALL_TIMEOUT);
+  }
+
+  function inviteParticipants() {
+    if (self.status !== 'pending' && self.status !== 'accepted') {
+      return null;
+    }
+
+    options.cookie = cookie;
+    ict = userAgent.invite(participants, options);
+    return ict;
+  }
+
+  function setupConversation(dialog) {
+    if (!dialog || self.status !== 'pending' && self.status !== 'accepted') {
+      return;
+    }
+
+    conversation._onDialog(dialog);
+
+    if (self.status !== 'accepted') {
+      self._status = 'accepted';
+      self.emit('accepted', self);
+    }
+
+    deferred.resolve(conversation);
+  }
+
+  function inviteParticipantsFailed(reason) {
+    // If inviteParticipants failed with the InviteClientTransaction, but this
+    // OutgoingInvite was to more than one Participant, then do not reject
+    // immediately; instead, continue waiting for an InviteServerTransaction.
+    //
+    // If no InviteServerTransaction is received, the TimeoutPromise will
+    // time out. Truly, something else may have happened: the other
+    // Participant(s) may have rejected, a server error may have occurred,
+    // etc.; however, the best we can do for now is raise "failed".
+    if (reason instanceof InviteClientTransaction) {
+      if (participants.length > 1) {
+        return;
+      }
+
+      var error;
+      switch (reason.state) {
+        case 'canceled':
+          error = E.CONVERSATION_INVITE_CANCELED;
+          break;
+        case 'rejected':
+          if (self.status === 'pending') {
+            self._status = 'rejected';
+            self.emit('rejected', self);
+          }
+          error = E.CONVERSATION_INVITE_REJECTED;
+          break;
+        case 'failed':
+        default:
+          if (self.status === 'pending') {
+            self._status = 'failed';
+            self.emit('failed', self);
+          }
+          error = E.CONVERSATION_INVITE_FAILED;
+      }
+
+      deferred.reject(error);
+      return;
+    }
+
+    // If inviteParticipants failed with an Error, if may have been a
+    // getUserMedia error; reject accordingly.
+    if (reason instanceof Error && ['PermissionDeniedError', 'PERMISSION_DENIED'].indexOf(reason.name) > -1) {
+      if (self.status === 'pending') {
+        self._status = 'failed';
+        self.emit('failed', self);
+      }
+      deferred.reject(E.MEDIA_ACCESS_DENIED);
+      return;
+    }
+
+    deferred.reject(reason);
+  }
+
+  // If the TimeoutPromise times out, then no Participants have connected to
+  // the Conversation within the time allotted for the OutgoingInvite, so
+  // consider the OutgoingInvite failed.
+  timeoutPromise.once('timedOut', function() {
+    self._status = 'failed';
+    self.emit('failed', self);
+    if (ict) {
+      ict.cancel(true);
+    }
+  });
+
+  // If the CancelablePromise is canceled, then no Participants have connected
+  // to the Conversation, so consider the OutgoingInvite canceled.
+  cancelablePromise.once('canceled', function() {
+    self._status = 'canceled';
+    self.emit('canceled', self);
+    if (ict) {
+      ict.cancel();
+    }
+  });
+}
+
+inherits(OutgoingInvite, EventEmitter);
+
+OutgoingInvite.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) {
+  if (this.status !== 'pending' && this.status !== 'accepted') {
+    inviteServerTransaction.reject();
+    return this;
+  }
+
+  inviteServerTransaction.accept(this._options)
+    .then(this._onReceiveInvite)
+    .catch(function() {
+      // Do nothing. We will receive a participantFailed Conversation event.
+    });
+
+  return this;
+};
+
+/**
+ * Attempt to cancel the {@link OutgoingInvite}.
+ * @returns {this}
+ */
+OutgoingInvite.prototype.cancel = function cancel() {
+  this._cancelablePromise.cancel();
+  return this;
+};
+
+OutgoingInvite.prototype.catch = function _catch() {
+  return this._promise.catch.apply(this._promise, arguments);
+};
+
+OutgoingInvite.prototype.then = function then() {
+  return this._promise.then.apply(this._promise, arguments);
+};
+
+/**
+ * The {@link OutgoingInvite} was accepted, and the {@link Client} is now
+ * participating in the {@link Conversation}.
+ * @param {OutgoingInvite} invite - The {@link OutgoingInvite}
+ * @event OutgoingInvite#accepted
+ */
+
+/**
+ * The {@link OutgoingInvite} was rejected.
+ * @param {OutgoingInvite} invite - The {@link OutgoingInvite}
+ * @event OutgoingInvite#rejected
+ */
+
+/**
+ * The {@link OutgoingInvite} was canceled.
+ * @param {OutgoingInvite} invite - The {@link OutgoingInvite}
+ * @event OutgoingInvite#canceled
+ */
+
+/**
+ * The {@link OutgoingInvite} failed.
+ * @param {OutgoingInvite} invite - The {@link OutgoingInvite}
+ * @event OutgoingInvite#failed
+ */
+
+module.exports = OutgoingInvite;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/participant.js.html b/dist/docs/participant.js.html new file mode 100644 index 000000000..c553f72bd --- /dev/null +++ b/dist/docs/participant.js.html @@ -0,0 +1,207 @@ + + + + + JSDoc: Source: participant.js + + + + + + + + + + +
+ +

Source: participant.js

+ + + + + + +
+
+
'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var inherits = require('util').inherits;
+var Media = require('./media');
+
+/**
+ * Construct a {@link Participant}.
+ * @class
+ * @classdesc A {@link Participant} represents a remote {@link Client} in a
+ * {@link Conversation}.
+ * @param {Participant.SID} sid - The {@link Participant}'s SID
+ * @param {string} identity - The identity of the {@link Participant}
+ * @param {PeerConnection} peerConnection - The PeerConnection between your {@link Client} and this {@link Participant} within the context of a {@link Conversation}
+ * @param {?Media} [media] - The {@link Media} this {@link Participant} is sharing, if any
+ * @property {string} identity - The identity of the {@link Participant}
+ * @property {Media} media - The {@link Media} this {@link Participant} is sharing, if any
+ * @property {Participant.SID} sid - The {@link Participant}'s SID
+ * @fires Participant#trackAdded
+ * @fires Participant#trackDimensionsChanged
+ * @fires Participant#trackDisabled
+ * @fires Participant#trackEnabled
+ * @fires Participant#trackEnded
+ * @fires Participant#trackRemoved
+ * @fires Participant#trackStarted
+ */
+function Participant(sid, identity, peerConnection, media) {
+  if (!(this instanceof Participant)) {
+    return new Participant(sid, identity, peerConnection, media);
+  }
+
+  EventEmitter.call(this);
+
+  media = media || new Media();
+
+  /* eslint no-use-before-define:0 */
+  media.on(Media.TRACK_ADDED, this.emit.bind(this, TRACK_ADDED));
+  media.on(Media.TRACK_DIMENSIONS_CHANGED, this.emit.bind(this, TRACK_DIMENSIONS_CHANGED));
+  media.on(Media.TRACK_DISABLED, this.emit.bind(this, TRACK_DISABLED));
+  media.on(Media.TRACK_ENABLED, this.emit.bind(this, TRACK_ENABLED));
+  media.on(Media.TRACK_ENDED, this.emit.bind(this, TRACK_ENDED));
+  media.on(Media.TRACK_REMOVED, this.emit.bind(this, TRACK_REMOVED));
+  media.on(Media.TRACK_STARTED, this.emit.bind(this, TRACK_STARTED));
+
+  /* istanbul ignore next */
+  Object.defineProperties(this, {
+    identity: {
+      enumerable: true,
+      value: identity
+    },
+    media: {
+      enumerable: true,
+      value: media
+    },
+    sid: {
+      enumerable: true,
+      value: sid
+    }
+  });
+
+  return this;
+}
+
+var TRACK_ADDED = Participant.TRACK_ADDED = Media.TRACK_ADDED;
+var TRACK_DIMENSIONS_CHANGED = Participant.TRACK_DIMENSIONS_CHANGED = Media.TRACK_DIMENSIONS_CHANGED;
+var TRACK_DISABLED = Participant.TRACK_DISABLED = Media.TRACK_DISABLED;
+var TRACK_ENABLED = Participant.TRACK_ENABLED = Media.TRACK_ENABLED;
+var TRACK_ENDED = Participant.TRACK_ENDED = Media.TRACK_ENDED;
+var TRACK_REMOVED = Participant.TRACK_REMOVED = Media.TRACK_REMOVED;
+var TRACK_STARTED = Participant.TRACK_STARTED = Media.TRACK_STARTED;
+
+inherits(Participant, EventEmitter);
+
+/**
+ * Update the {@link Participant} upon receipt of a {@link ConversationEvent}.
+ * @private
+ * @param {ConversationEvent} event
+ * @returns {Participant}
+ */
+Participant.prototype._onConversationEvent = function _onConversationEvent(event) {
+  if (event.participant_sid !== this.sid) {
+    return this;
+  }
+
+  event.tracks.forEach(function(_track) {
+    var trackId = _track.id;
+    var track = this.media.tracks.get(trackId);
+    if (!track) {
+      return;
+    }
+
+    switch (event.event.toLowerCase()) {
+      case 'track_disabled':
+        track._enable(false);
+        break;
+      case 'track_enabled':
+        track._enable(true);
+        break;
+      case 'track_removed':
+        this.media._removeTrack(track);
+    }
+  }, this);
+
+  return this;
+};
+
+Object.freeze(Participant.prototype);
+
+/**
+ * A {@link Participant.SID} is a 34-character string starting with "PA"
+ * that uniquely identifies a {@link Participant}.
+ * @type string
+ * @typedef Participant.SID
+ */
+
+/**
+ * A {@link Track} was added by the {@link Participant}.
+ * @param {Track} track - The {@link Track} that was added
+ * @event Participant#trackAdded
+ */
+
+/**
+ * One of the {@link Participant}'s {@link VideoTrack}'s dimensions changed.
+ * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed
+ * @event Participant#trackDimensionsChanged
+ */
+
+/**
+ * A {@link Track} was disabled by the {@link Participant}.
+ * @param {Track} track - The {@link Track} that was disabled
+ * @event Participant#trackDisabled
+ */
+
+/**
+ * A {@link Track} was enabled by the {@link Participant}.
+ * @param {Track} track - The {@link Track} that was enabled
+ * @event Participant#trackEnabled
+ */
+
+/**
+ * One of the {@link Participant}'s {@link Track}s ended.
+ * @param {Track} track - The {@link Track} that ended
+ * @event Participant#trackEnded
+ */
+
+/**
+ * A {@link Track} was removed by the {@link Participant}.
+ * @param {Track} track - The {@link Track} that was removed
+ * @event Participant#trackRemoved
+ */
+
+/**
+ * One of the {@link Participant}'s {@link Track}s started.
+ * @param {Track} track - The {@link Track} that started
+ * @event Participant#trackStarted
+ */
+
+module.exports = Participant;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/dist/docs/scripts/linenumber.js b/dist/docs/scripts/linenumber.js new file mode 100644 index 000000000..8d52f7eaf --- /dev/null +++ b/dist/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/dist/docs/scripts/prettify/Apache-License-2.0.txt b/dist/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/dist/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dist/docs/scripts/prettify/lang-css.js b/dist/docs/scripts/prettify/lang-css.js new file mode 100644 index 000000000..041e1f590 --- /dev/null +++ b/dist/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/dist/docs/scripts/prettify/prettify.js b/dist/docs/scripts/prettify/prettify.js new file mode 100644 index 000000000..eef5ad7e6 --- /dev/null +++ b/dist/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p p:first-child, +.props td.description > p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, +.props td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/dist/docs/styles/prettify-jsdoc.css b/dist/docs/styles/prettify-jsdoc.css new file mode 100644 index 000000000..5a2526e37 --- /dev/null +++ b/dist/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/dist/docs/styles/prettify-tomorrow.css b/dist/docs/styles/prettify-tomorrow.css new file mode 100644 index 000000000..b6f92a78d --- /dev/null +++ b/dist/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/dist/twilio-conversations.js b/dist/twilio-conversations.js new file mode 100644 index 000000000..756fc0801 --- /dev/null +++ b/dist/twilio-conversations.js @@ -0,0 +1,22518 @@ +/*! twilio-conversations.js 0.13.0 + +The following license applies to all parts of this software except as +documented below. + + Copyright (c) 2015, Twilio, inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. Neither the name of Twilio nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software includes SIP.js under the following license. + + Copyright (c) 2014 Junction Networks, Inc. + + License: The MIT License + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +SIP.js contains substantial portions of the JsSIP software under the following +license. + + Copyright (c) 2012-2013 José Luis Millán - Versatica + + License: The MIT License + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ +/* eslint strict:0 */ +(function(root) { + var Conversations = (function unpack(){var id=1, bundle=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o} conversations - The {@link Conversation}s this + * {@link Client} is participating in + * @property {bool} isListening - Whether the {@link Client} is listening for + * {@link IncomingInvite}s to {@link Conversation}s + * @fires Client#invite + * @fires Client#error + */ +function Client(accessManager, options) { + if (!(this instanceof Client)) { + return new Client(accessManager, options); + } + + if (typeof accessManager === 'string') { + accessManager = new AccessManager(accessManager); + } + + var self = this; + EventEmitter.call(this); + + options = util.withDefaults(options, { + eventGateway: constants.EVENT_GATEWAY, + logLevel: constants.DEFAULT_LOG_LEVEL, + useConversationEvents: true, + userAgent: SIPJSUserAgent + }); + + var logLevel = options.logLevel; + + if (options.debug === true) { + logLevel = 'debug'; + console.warn('Warning: ClientOptions.debug is deprecated and will be removed in a future release. Please see ClientOptions.logLevel.'); + } + + var log = new Log('Client', logLevel); + + var conversations = new Map(); + var eventGateway = options.eventGateway; + var incomingInvites = new Map(); + var rejectedIncomingInvites = new Map(); + var isListening = false; + var outgoingInvites = new Map(); + var canceledOutgoingInvites = new Map(); + + var UserAgent = options.userAgent; + var userAgent = new UserAgent(accessManager, options); + + userAgent.on('invite', function onInviteServerTransaction(inviteServerTransaction) { + var cookie = inviteServerTransaction.cookie; + var outgoingInvite = outgoingInvites.get(cookie) || canceledOutgoingInvites.get(cookie); + if (outgoingInvite) { + return outgoingInvite._onInviteServerTransaction(inviteServerTransaction); + } + + var key = inviteServerTransaction.key; + var incomingInvite = incomingInvites.get(key) || rejectedIncomingInvites.get(key); + if (incomingInvite) { + return incomingInvite._onInviteServerTransaction(inviteServerTransaction); + } + + var conversation = conversations.get(inviteServerTransaction.conversationSid); + if (conversation) { + return conversation._onInviteServerTransaction(inviteServerTransaction); + } + + self._onInviteServerTransaction(inviteServerTransaction); + }); + + userAgent.on('dialogCreated', function(dialog) { + new StatsReporter(eventGateway, dialog, logLevel); + }); + + userAgent.on('keepAliveTimeout', function() { + self.emit('error', E.GATEWAY_DISCONNECTED); + }); + + /* istanbul ignore next */ + Object.defineProperties(this, { + _canceledOutgoingInvites: { + value: canceledOutgoingInvites + }, + _incomingInvites: { + value: incomingInvites + }, + _isListening: { + set: function(_isListening) { + isListening = _isListening; + } + }, + _isRegistered: { + enumerable: true, + get: function() { + return this._userAgent.isRegistered; + } + }, + _log: { + value: log + }, + _logLevel: { + value: logLevel + }, + _needsRegistration: { + get: function() { + return isListening + || outgoingInvites.size + || incomingInvites.size + || conversations.size; + } + }, + _options: { + value: options + }, + _outgoingInvites: { + value: outgoingInvites + }, + _rejectedIncomingInvites: { + value: rejectedIncomingInvites + }, + _userAgent: { + value: userAgent + }, + accessManager: { + enumerable: true, + value: accessManager + }, + conversations: { + enumerable: true, + value: conversations + }, + identity: { + enumerable: true, + get: function() { + return accessManager.identity; + } + }, + isListening: { + enumerable: true, + get: function() { + return isListening; + } + } + }); + + accessManager.on('tokenUpdated', this._register.bind(this, true)); + + return this; +} + +inherits(Client, EventEmitter); + +Client.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) { + // If not listening, do nothing. Do not even reject, as that would cause + // other listening Clients to be unable to accept the + // InviteServerTransaction. + if (!this.isListening) { + return; + } + + var incomingInvite = new IncomingInvite(inviteServerTransaction, this._options); + var key = inviteServerTransaction.key; + this._incomingInvites.set(key, incomingInvite); + + var self = this; + + // If accepted, the Client has joined the Conversation and will pass any + // InviteServerTransactions with matching Conversation SID to the + // Conversation in order to be accepted or rejected. + incomingInvite.once('accepted', function incomingInviteAccepted() { + self._incomingInvites.delete(key); + + var conversation = incomingInvite._conversation; + self.conversations.set(conversation.sid, conversation); + + conversation.once('disconnected', function() { + self.conversations.delete(conversation.sid); + self._unregisterIfNotNeeded(); + }); + }); + + incomingInvite.once('canceled', function incomingInviteCanceled() { + self._incomingInvites.delete(key); + self._unregisterIfNotNeeded(); + }); + + // If rejected, the Client remembers the IncomingInvite until all the + // InviteServerTransactions with matching Conversation SID and Participant + // SID would have been received and passes them to the IncomingInvite in + // order to be rejected. + incomingInvite.once('rejected', function incomingInviteRejected() { + self._incomingInvites.delete(key); + self._rejectedIncomingInvites.set(key, incomingInvite); + + setTimeout(function deleteRejectedIncomingInvite() { + self._rejectedIncomingInvites.delete(key); + }, 3 * constants.DEFAULT_CALL_TIMEOUT); + + self._unregisterIfNotNeeded(); + }); + + this.emit('invite', incomingInvite); +}; + +/** + * Causes this {@link Client} to stop listening for {@link IncomingInvite}s to + * {@link Conversation}s until {@link Client#listen} is called again. + * @returns {this} + */ +Client.prototype.unlisten = function unlisten() { + this._isListening = false; + this._unregisterIfNotNeeded(); + return this; +}; + +Client.prototype._unregister = function _unregister() { + if (!this._isRegistered) { + return Promise.resolve(this); + } + var self = this; + return this._userAgent.unregister().then(function() { + self._isListening = false; + return self; + }); +}; + +/** + * Causes this {@link Client} to start listening for {@link IncomingInvite}s to + * {@link Conversation}s. + * @returns {Promise} + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var alice = new Twilio.Conversations.Client(manager); + * + * alice.listen().then(function() { + * console.log('Alice is listening'); + * }, function(error) { + * console.error(error); + * }); + */ +Client.prototype.listen = function listen() { + var self = this; + return this._register().then(function() { + self._isListening = true; + return self; + }); +}; + +Client.prototype._register = function _register(reregister) { + var self = this; + if (!reregister && this._isRegistered) { + return Promise.resolve(this); + } + return this._userAgent.register().then(function onRegistered() { + return self; + }, function onRegisterFailed(response) { + var gatewayMessage = util.getOrNull(response, 'headers.X-Twilio-Error.0.raw'); + var sipMessage = response.cause; + + if (sipMessage) { + self._log.throw(E.LISTEN_FAILED, 'Received SIP error: ' + sipMessage); + } else if (gatewayMessage) { + self._log.throw(E.LISTEN_FAILED, 'Gateway responded with: ' + gatewayMessage); + } else { + self._log.throw(E.LISTEN_FAILED, response.message || response); + } + }); +}; + +/** + * Invite remote {@link Client}s to join a {@link Conversation}. + *

+ * By default, this will attempt to setup an {@link AudioTrack} and + * {@link VideoTrack} between local and remote {@link Client}s. You can + * override this by specifying options. + * @param {Array|string} participants - {@link Participant} identities to invite to the {@link Conversation} + * @param {Client.InviteToConversationOptions} + * [options={localStreamConstraints:{audio:true,video:true}}] - Options to override + * {@link Client#inviteToConversation}'s default behavior + * @returns {OutgoingInvite} + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.inviteToConversation(['bob', 'charlie']).then(function(conversation) { + * conversation.on('participantConnected', function(participant) { + * console.log(participant.identity + ' has connected'); + * }); + * }); + */ +Client.prototype.inviteToConversation = function inviteToConversation(participants, options) { + options = util.withDefaults({ }, options, this._options); + + if (!participants) { + this._log.throw(E.INVALID_ARGUMENT, 'No Participant identities were provided'); + } + + participants = participants.forEach ? participants : [participants]; + util.validateAddresses(this.accessManager._tokenPayload.sub, participants); + + // Save a reference to the OutgoingInvite; the Client will pass any + // InviteServerTransactions with matching cookie to the OutgoingInvite + // in order to be accepted or rejected. + var outgoingInvite = new OutgoingInvite(this._userAgent, participants, options); + var cookie = outgoingInvite._cookie; + this._outgoingInvites.set(cookie, outgoingInvite); + + var self = this; + + // If accepted, the Client has joined the Conversation and will pass any + // InviteServerTransactions with matching Conversation SID to the + // Conversation in order to be accepted or rejected. + outgoingInvite.once('accepted', function() { + self._outgoingInvites.delete(cookie); + + var conversation = outgoingInvite._conversation; + self.conversations.set(conversation.sid, conversation); + + conversation.once('disconnected', function() { + self.conversations.delete(conversation.sid); + self._unregisterIfNotNeeded(); + }); + }); + + // If canceled, the Client remembers the OutgoingInvite until all the + // InviteServerTransactions with matching cookies would have been received + // and passes them to the OutgoingInvite in order to be rejected. + outgoingInvite.once('canceled', function outgoingInviteCanceled() { + self._outgoingInvites.delete(cookie); + self._canceledOutgoingInvites.set(cookie, outgoingInvite); + + setTimeout(function deleteCanceledOutgoingInvite() { + self._canceledOutgoingInvites.delete(cookie); + }, constants.DEFAULT_CALL_TIMEOUT * 3); + + self._unregisterIfNotNeeded(); + }); + + outgoingInvite.once('rejected', function() { + self._outgoingInvites.delete(outgoingInvite._cookie); + self._unregisterIfNotNeeded(); + }); + + return outgoingInvite; +}; + +Client.prototype._unregisterIfNotNeeded = function _unregisterIfNotNeeded() { + return this._needsRegistration ? Promise.resolve(this) : this._unregister(); +}; + +Object.freeze(Client.prototype); + +/** + * Your {@link Client} has run into an error. + * @param {Error} error - The Error + * @event Client#error + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.on('error', function(error) { + * console.error(error); + * }); + */ + +/** + * Your {@link Client} has received an {@link IncomingInvite} to participant in a + * {@link Conversation}. + * @param {IncomingInvite} invite - the {@link IncomingInvite} + * @event Client#invite + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.on('invite', function(invite) { + * console.log('Received an IncomingInvite to join a Conversation from ' + invite.from); + * }); + */ + +/** + * You may pass these options to {@link Client}'s constructor to override + * its default behavior. + * @typedef {object} Client.ConstructorOptions + * @property {string} [logLevel='warn'] - Set the verbosity of logging to console. + * Valid values: ['off', 'error', 'warn', 'info', 'debug'] + */ + +/** + * You may pass these options to {@link Client#inviteToConversation} to + * override the default behavior. + * @typedef {object} Client.InviteToConversationOptions + * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing + * {@link LocalMedia} object when creating the {@link Conversation} + * @property {?MediaStream} [localStream=null] - Set to reuse an existing + * MediaStream when creating the {@link Conversation} + * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to + * override the parameters passed to getUserMedia when neither + * localMedia nor localStream are provided + */ + +module.exports = Client; + +},{"./incominginvite":4,"./outgoinginvite":13,"./signaling/sipjsuseragent":26,"./statsreporter":28,"./util":31,"./util/constants":30,"./util/log":32,"events":44,"twilio-common":90,"util":48}],3:[function(require,module,exports){ +'use strict'; + +var constants = require('./util/constants'); +var ConversationInfo = require('./signaling/conversation-info'); +var EventEmitter = require('events').EventEmitter; +var inherits = require('util').inherits; +var Media = require('./media'); +var Participant = require('./participant'); +var util = require('./util'); + +var Log = require('./util/log'); +var E = constants.twilioErrors; + +/** + * Construct a {@link Conversation}. + * @class + * @classdesc A {@link Conversation} represents communication between your + * {@link Client} and one or more {@link Participant}s sharing + * {@link AudioTrack}s and {@link VideoTrack}s. + *

+ * You can join a {@link Conversation} by first creating an + * {@link OutgoingInvite} with {@link Client#inviteToConversation} or by + * accepting an {@link IncomingInvite} with {@link IncomingInvite#accept}. + * @param {Object} [options] - Options to override the constructor's default + * behavior. + * @property {LocalMedia} localMedia - Your {@link Client}'s {@link LocalMedia} in the {@link Conversation} + * @property {Map} participants - The {@link Participant}s + * participating in this {@link Conversation} + * @property {Conversation.SID} sid - The {@link Conversation}'s SID + * @fires Conversation#disconnected + * @fires Conversation#participantConnected + * @fires Conversation#participantDisconnected + * @fires Conversation#participantFailed + * @fires Conversation#trackAdded + * @fires Conversation#trackDimensionsChanged + * @fires Conversation#trackDisabled + * @fires Conversation#trackEnabled + * @fires Conversation#trackEnded + * @fires Conversation#trackRemoved + * @fires Conversation#trackStarted + */ +function Conversation(options) { + if (!(this instanceof Conversation)) { + return new Conversation(options); + } + EventEmitter.call(this); + + options = util.withDefaults({ }, options, { + logLevel: constants.DEFAULT_LOG_LEVEL + }); + + var localMedia = options.localMedia; + var participantSid = null; + var shouldStopLocalMediaOnDisconnect = options.shouldStopLocalMediaOnDisconnect; + var sid; + + /* istanbul ignore next */ + Object.defineProperties(this, { + _dialogs: { + value: new Set() + }, + _localMedia: { + set: function(_localMedia) { + localMedia = _localMedia; + } + }, + _log: { + value: new Log('Conversation', options.logLevel) + }, + _options: { + value: options + }, + _participantSid: { + get: function() { + return participantSid; + }, + set: function(_participantSid) { + participantSid = _participantSid; + } + }, + _shouldStopLocalMediaOnDisconnect: { + get: function() { + return shouldStopLocalMediaOnDisconnect; + }, + set: function(_shouldStopLocalMediaOnDisconnect) { + shouldStopLocalMediaOnDisconnect = _shouldStopLocalMediaOnDisconnect; + } + }, + _sid: { + set: function(_sid) { + sid = _sid; + } + }, + _trackIdToParticipants: { + value: new Map() + }, + localMedia: { + enumerable: true, + get: function() { + return localMedia; + } + }, + participants: { + enumerable: true, + value: new Map() + }, + sid: { + enumerable: true, + get: function() { + return sid; + } + } + }); + + return this; +} + +var TRACK_ADDED = Conversation.TRACK_ADDED = Participant.TRACK_ADDED; +var TRACK_DIMENSIONS_CHANGED = Conversation.TRACK_DIMENSIONS_CHANGED = Participant.TRACK_DIMENSIONS_CHANGED; +var TRACK_DISABLED = Conversation.TRACK_DISABLED = Participant.TRACK_DISABLED; +var TRACK_ENABLED = Conversation.TRACK_ENABLED = Participant.TRACK_ENABLED; +var TRACK_ENDED = Conversation.TRACK_ENDED = Participant.TRACK_ENDED; +var TRACK_REMOVED = Conversation.TRACK_REMOVED = Participant.TRACK_REMOVED; +var TRACK_STARTED = Conversation.TRACK_STARTED = Participant.TRACK_STARTED; + +inherits(Conversation, EventEmitter); + +/** + * Add a {@link Dialog} to the {@link Conversation}. + * @private + * @param {Dialog} dialog - The {@link Dialog} + * @returns {this} + */ +Conversation.prototype._onDialog = function _onDialog(dialog) { + if (this._dialogs.has(dialog)) { + return this; + } + + this._sid = this.sid || dialog.conversationSid; + this._localMedia = this.localMedia || dialog.localMedia; + this._participantSid = this._participantSid || dialog.participantSid; + this._dialogs.add(dialog); + + dialog.once('ended', this._removeDialog.bind(this)); + + dialog.on('notification', this._onNotification.bind(this, dialog)); + dialog.dequeue('notification'); + handleDialogTrackEvents(dialog, this._trackIdToParticipants); + + // NOTE(mroberts): Simulate Conversation Events if disabled. Once we are + // confident in the Conversation Events implementation we will completely + // remove this path. + if (!this._options.useConversationEvents) { + var participantSid = util.makeUUID(); + var notification = ConversationInfo + .simulateParticipantConnectedEvent(dialog, participantSid); + this._onNotification(dialog, notification); + var participant = this.participants.get(participantSid); + handleDialogTrackEventsMesh(dialog, participant); + } + + return this; +}; + +Conversation.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) { + this._options.localMedia = this.localMedia; + return inviteServerTransaction.accept(this._options).then(this._onDialog.bind(this)); +}; + +/** + * Handle {@link Dialog} {@link Track} events using a Map from {@link Track} IDs + * to {@link Participant}s. This technique relies on Conversation Events to + * construct the Map. It is topology-independent. + * @private + * @param {Dialog} dialog - The {@link Dialog} + * @param {Map} trackIdToParticipants - The Map from + * {@link Track} IDs to {@link Participant}s + */ +function handleDialogTrackEvents(dialog, trackIdToParticipants) { + // Add the Track to any Participant associated with the Track ID. + function addTrack(track) { + var participants = trackIdToParticipants.get(track.id) || new Set(); + participants.forEach(function(participant) { + participant.media._addTrack(track); + }); + } + + // Remove the Track from any Participant associated with the Track ID, and + // remove the Track from the Map. + function removeTrack(track) { + var participants = trackIdToParticipants.get(track.id) || new Set(); + participants.forEach(function(participant) { + participant.media._removeTrack(track); + }); + trackIdToParticipants.delete(track.id); + } + + var dialogMedia = dialog.remoteMedia; + dialogMedia.tracks.forEach(addTrack); + dialogMedia.on(Media.TRACK_ADDED, addTrack); + dialogMedia.on(Media.TRACK_REMOVED, removeTrack); + dialog.once('ended', function() { + dialogMedia.removeListener(Media.TRACK_ADDED, addTrack); + dialogMedia.removeListener(Media.TRACK_REMOVED, removeTrack); + }); +} + +/** + * Handle {@link Dialog} {@link Track} events using a one-to-one association + * with a {@link Participant}. This technique only works in mesh topologies. + * @private + * @param {Dialog} dialog - The {@link Dialog} + * @param {Participant} participant - The {@link Participant} + */ +function handleDialogTrackEventsMesh(dialog, participant) { + var dialogMedia = dialog.remoteMedia; + var participantMedia = participant.media; + dialogMedia.tracks.forEach(participantMedia._addTrack, participantMedia); + dialogMedia.on(Media.TRACK_ADDED, participantMedia._addTrack.bind(participantMedia)); + dialogMedia.on(Media.TRACK_REMOVED, participantMedia._removeTrack.bind(participantMedia)); +} + +/** + * Connect a {@link Participant} to the {@link Conversation}. + * @private + * @param {Participant} participant - The {@link Participant} + * @returns {this} + */ +Conversation.prototype._connectParticipant = function _connectParticipant(participant) { + if (this.participants.has(participant.sid)) { + return this; + } + + this.participants.set(participant.sid, participant); + + var self = this; + participant.on(Participant.TRACK_ADDED, function trackAdded(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_ADDED, trackAdded); + } + self.emit(TRACK_ADDED, participant, track); + }); + participant.on(Participant.TRACK_DIMENSIONS_CHANGED, function trackDimensionsChanged(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_DIMENSIONS_CHANGED, trackDimensionsChanged); + } + self.emit(TRACK_DIMENSIONS_CHANGED, participant, track); + }); + participant.on(Participant.TRACK_DISABLED, function trackDisabled(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_DISABLED, trackDisabled); + } + self.emit(TRACK_DISABLED, participant, track); + }); + participant.on(Participant.TRACK_ENABLED, function trackEnabled(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_ENABLED, trackEnabled); + } + self.emit(TRACK_ENABLED, participant, track); + }); + participant.on(Participant.TRACK_ENDED, function trackEnded(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_ENDED, trackEnded); + } + self.emit(TRACK_ENDED, participant, track); + }); + participant.on(Participant.TRACK_REMOVED, function trackRemoved(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_REMOVED, trackRemoved); + } + self.emit(TRACK_REMOVED, participant, track); + }); + participant.on(Participant.TRACK_STARTED, function trackStarted(track) { + if (!self.participants.has(participant.sid)) { + return participant.removeListener(Participant.TRACK_STARTED, trackStarted); + } + self.emit(TRACK_STARTED, participant, track); + }); + + // Emit these events on the next tick so the customer has + // a chance to listen for them. + setTimeout(function() { + self.emit('participantConnected', participant); + + // Re-emit the "trackAdded" event for each of the Participant's Tracks. + participant.media.tracks.forEach(participant.emit.bind(participant, Participant.TRACK_ADDED)); + }); + + return this; +}; + +Conversation.prototype._removeDialog = function _removeDialog(dialog) { + this._dialogs.delete(dialog); + + // NOTE(mroberts): Simulate Conversation Events if disabled. Once we are + // confident in the Conversation Events implementation we will completely + // remove this path. + if (!this._options.useConversationEvents) { + var notification = ConversationInfo + .simulateParticipantDisconnectedEvents(this.participants, dialog); + this._onNotification(dialog, notification); + } + + if (!this._dialogs.size) { + if (this._shouldStopLocalMediaOnDisconnect) { + this.localMedia.stop(); + } + + this.emit('disconnected', this); + + // NOTE(mroberts): Regardless of topology, zero dialogs implies we are + // disconnected from the Conversation; so disconnect any remaining + // Participants (hopefully they have already been disconnected). + this.participants.forEach(this._disconnectParticipant, this); + } + + return this; +}; + +/** + * Associate a {@link Track} ID to a {@link Participant}. + * @private + * @param {Participant} participant - The {@link Participant} + * @param {{id: string}} track - An object containing the {@link Track} ID + * @returns {this} + */ +Conversation.prototype._associateParticipantToTrackId = function _associateParticipantToTrackId(participant, track) { + util.map.addToMapOfSets(this._trackIdToParticipants, track.id, participant); + return this; +}; + +/** + * Associate {@link Track} IDs to a {@link Participant}. + * @private + * @param {Participant} participant - The {@link Participant} + * @param {Array<{id: string}>} tracks - Objects containing the {@link Track} IDs + * @returns {this} + */ +Conversation.prototype._associateParticipantToTrackIds = function _associateParticipantToTrackIds(participant, tracks) { + tracks.forEach(this._associateParticipantToTrackId.bind(this, participant)); + return this; +}; + +/** + * Disassociate a {@link Participant} from a {@link Track} ID. + * @private + * @param {Participant} participant - The {@link Participant} + * @param {{id: string}} track - An object containing the {@link Track} ID + * @returns {this} + */ +Conversation.prototype._disassociateParticipantFromTrackId = function _disassociateParticipantFromTrackId(participant, _track) { + var id = _track.id; + util.map.deleteFromMapOfSets(this._trackIdToParticipants, id, participant); + var track = participant.media.tracks.get(id); + if (track) { + participant.media._removeTrack(track); + } + return this; +}; + +/** + * Associate {@link Track} IDs to a {@link Participant}. + * @private + * @param {Participant} participant - The {@link Participant} + * @param {Array<{id: string}>} tracks - Objects containing the {@link Track} IDs + * @returns {this} + */ +Conversation.prototype._disassociateParticipantFromTrackIds = function _disassociateParticipantFromTrackIds(participant, tracks) { + tracks.forEach(this._disassociateParticipantFromTrackId.bind(this, participant)); + return this; +}; + +/** + * Disconnect a {@link Participant} from the {@link Conversation}. + * @private + * @param {Participant} - The {@link Participant} + * @returns {this} + */ +Conversation.prototype._disconnectParticipant = function _disconnectParticipant(participant) { + participant.media.tracks.forEach(function(track) { + this._disassociateParticipantFromTrackId(participant, track.id); + participant.media._removeTrack(track); + }, this); + this.participants.delete(participant.sid); + this.emit('participantDisconnected', participant); + return this; +}; + +/** + * Update the {@link Conversation} upon receipt of a {@link Notification}. + * @private + * @param {Dialog} dialog - the {@link Dialog} that received the + * {@link PartialNotification} + * @param {PartialNotification} notification + * @returns {this} + */ +Conversation.prototype._onNotification = function _onNotification(dialog, notification) { + var conversationState = notification.conversation_state; + if (conversationState) { + if (this.sid !== conversationState.sid) { + return this; + } + return this._onFullNotification(dialog, notification); + } + return this._onPartialNotification(dialog, notification); +}; + +Conversation.prototype._onFullNotification = function _onFullNotification(dialog, notification) { + notification.conversation_state.participants.forEach(this._onParticipantConnected, this); + return this; +}; + +Conversation.prototype._onPartialNotification = function _onPartialNotification(dialog, notification) { + notification.event_list.forEach(function(event) { + var eventType = event.event.toLowerCase(); + switch (eventType) { + case 'participant_connected': + this._onParticipantConnected(event); + return; + case 'participant_disconnected': + this._onParticipantDisconnected(event); + return; + case 'participant_failed': + this._onParticipantFailed(event); + return; + } + var participant = this.participants.get(event.participant_sid); + if (participant) { + switch (eventType) { + case 'track_added': + this._associateParticipantToTrackIds(participant, event.tracks); + return; + case 'track_removed': + this._disassociateParticipantFromTrackIds(participant, event.tracks); + return; + } + participant._onConversationEvent(event); + } + }, this); + return this; +}; + +/** + * Handle a "participant_connected" Conversation Event. + * @private + * @param {Notification} event + * @returns {this} + */ +Conversation.prototype._onParticipantConnected = function _onParticipantConnected(event) { + if (this._participantSid === event.participant_sid) { + return this; + } + + var participant = this.participants.get(event.participant_sid); + var connectParticipant = false; + + if (!participant) { + participant = new Participant(event.participant_sid, util.getUser(event.address)); + connectParticipant = true; + } + + this._associateParticipantToTrackIds(participant, event.tracks); + + if (connectParticipant) { + this._connectParticipant(participant); + } + + return this; +}; + +Conversation.prototype._onParticipantFailed = function _onParticipantFailed(event) { + if (this._participantSid !== event.participant_sid) { + return this; + } + + var participant = this.participants.get(event.participant_sid) || + new Participant(event.participant_sid, util.getUser(event.address)); + + this.emit('participantFailed', participant); + + return this; +}; + +/** + * Handle a "participant_disconnected" Conversation Event. + * @private + * @param {Notification} event + * @returns {this} + */ +Conversation.prototype._onParticipantDisconnected = function _onParticipantDisconnected(event) { + if (this._participantSid === event.participant_sid) { + return this; + } + + var participant = this.participants.get(event.participant_sid); + + if (participant) { + this._disconnectParticipant(participant); + } + + return this; +}; + +Conversation.prototype.getStats = function getStats() { + var promises = []; + this._dialogs.forEach(function(dialog) { + promises.push(dialog.getStats()); + }); + + return Promise.all(promises); +}; + +/** + * Disconnect from the {@link Conversation}. + * @returns {this} + */ +Conversation.prototype.disconnect = function disconnect() { + this._dialogs.forEach(function(dialog) { + dialog.end(); + }); + return this; +}; + +/** + * Add a {@link Participant} to the {@link Conversation}. + * @param {string} identity - The identity of the {@link Participant} to add + * @returns {this} + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.inviteToConversation('alice').then(function(conversation) { + * conversation.invite('bob'); + * + * conversation.on('participantConnected', function(participant) { + * if (participant.identity === 'bob') { + * console.log('Bob has connected'); + * } + * }); + * }); + * @throws {Error} INVALID_ARGUMENT + *//** + * Add {@link Participant}s to the {@link Conversation}. + * @param {Array} identities - The identities of the {@link Participant}s to add + * @returns {this} + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.inviteToConversation('alice').then(function(conversation) { + * conversation.invite(['bob', 'charlie']); + * + * conversation.on('participantConnected', function() { + * if (participant.identity === 'bob') { + * console.log('Bob has connected'); + * } else if (participant.identity === 'charlie') { + * console.log('Charlie has connected'); + * } + * }); + * }); + * @throws {Error} INVALID_ARGUMENT + */ +Conversation.prototype.invite = function invite(identity) { + if (!identity) { + this._log.throw(E.INVALID_ARGUMENT, 'No Participant identities were provided'); + } + + // there maybe several dialogs within the conversation + // we just pick the first dialog to send the REFER to conversation service + var dialog; + this._dialogs.forEach(function(_dialog) { + dialog = dialog || _dialog; + }); + + var identities = identity.forEach ? identity : [identity]; + + var accessManager = dialog.userAgent.accessManager; + util.validateAddresses(accessManager._tokenPayload.sub, identities); + + identities.forEach(dialog.refer, dialog); + + return this; +}; + +Object.freeze(Conversation.prototype); + +/** + * A {@link Conversation.SID} is a 34-character string starting with "CV" + * that uniquely identifies a {@link Conversation}. + * @type string + * @typedef Conversation.SID + */ + +/** + * Your {@link Client} was disconnected from the {@link Conversation} and all + * other {@link Participant}s. + * @param {Conversation} conversation - The {@link Conversation} your + * {@link Client} was disconnected from + * @event Conversation#disconnected + * @example + * myConversation.on('disconnected', function() { + * myConversation.localMedia.detach(); + * }); + */ + +/** + * A {@link Participant} joined the {@link Conversation}. + * @param {Participant} participant - The {@link Participant} who joined + * @event Conversation#participantConnected + * @example + * myConversation.on('participantConnected', function(participant) { + * console.log(participant.identity + ' joined the Conversation'); + * + * // Get the participant's Media, + * var participantMedia = participant.media; + * + * // And attach it to your application's view. + * var participantView = document.getElementById('participant-view'); + * participantMedia.attach(participantView); + * participantVideos.appendChild(participantView); + * }); + */ + +/** + * A {@link Participant} left the {@link Conversation}. + * @param {Participant} participant - The {@link Participant} who left + * @event Conversation#participantDisconnected + * @example + * myConversation.on('participantDisconnected', function(participant) { + * console.log(participant.identity + ' left the Conversation'); + * }); + */ + +/** + * A {@link Participant} failed to join {@link Conversation}. + * @param {Participant} participant - The {@link Participant} that failed to join + * @event Conversation#participantFailed + * @example + * myConversation.on('participantFailed', function(participant) { + * console.log(participant.identity + ' failed to join the Conversation'); + * }); + */ + +/** + * A {@link Track} was added by a {@link Participant} in the {@link Conversation}. + * @param {Track} track - The {@link Track} that was added + * @param {Participant} participant - The {@link Participant} who added the + * {@link Track} + * @event Conversation#trackAdded + */ + +/** + * One of the {@link Participant}'s {@link VideoTrack}'s dimensions changed. + * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed + * @param {Participant} participant - The {@link Participant} whose {@link VideoTrack}'s + * dimensions changed + * @event Conversation#trackDimensionsChanged + */ + +/** + * A {@link Track} was disabled by a {@link Participant} in the {@link Conversation}. + * @param {Track} track - The {@link Track} that was disabled + * @param {Participant} participant - The {@link Participant} who disabled the + * {@link Track} + * @event Conversation#trackDisabled + */ + +/** + * A {@link Track} was enabled by a {@link Participant} in the {@link Conversation}. + * @param {Track} track - The {@link Track} that was enabled + * @param {Participant} participant - The {@link Participant} who enabled the + * {@link Track} + * @event Conversation#trackEnabled + */ + +/** + * One of a {@link Participant}'s {@link Track}s in the {@link Conversation} ended. + * @param {Track} track - The {@link Track} that ended + * @param {Participant} participant - The {@link Participant} whose {@link Track} ended + * @event Conversation#trackEnded + */ + +/** + * A {@link Track} was removed by a {@link Participant} in the {@link Conversation}. + * @param {Track} track - The {@link Track} that was removed + * @param {Participant} participant - The {@link Participant} who removed the + * {@link Track} + * @event Conversation#trackRemoved + */ + +/** + * One of a {@link Participant}'s {@link Track}s in the {@link Conversation} started. + * @param {Track} track - The {@link Track} that started + * @param {Participant} participant - The {@link Participant} whose {@link Track} started + * @event Conversation#trackStarted + */ + +module.exports = Conversation; + +},{"./media":5,"./participant":14,"./signaling/conversation-info":16,"./util":31,"./util/constants":30,"./util/log":32,"events":44,"util":48}],4:[function(require,module,exports){ +'use strict'; + +var C = require('./util/constants'); +var Conversation = require('./conversation'); +var EventEmitter = require('events').EventEmitter; +var inherits = require('util').inherits; +var util = require('./util'); + +var LocalMedia = require('./media/localmedia'); + +/** + * Construct an {@link IncomingInvite}. + * @class + * @classdesc An {@link IncomingInvite} to a {@link Conversation} can be accepted or + * rejected. + *

+ * {@link IncomingInvite}s are returned by {@link Client#event:invite}. + * @param {InviteServerTransaction} inviteServerTransaction - The + * {@link InviteServerTransaction} that this {@link IncomingInvite} wraps + * @param {Object} [options] - Options to override the constructor's + * default behavior. + * @property {Conversation.SID} conversationSid - The SID of the {@link Conversation} + * this {@link IncomingInvite} invites to + * @property {string} from - The identity of the {@link Participant} that sent this + * {@link IncomingInvite} + * @property {Array} participants - The identities of the {@link Participant}s currently in the {@link Conversation} + * @property {string} status - The status of this {@link IncomingInvite}, either + * "accepting", "accepted", "rejected", "canceled", "failed", or "pending" + * @fires IncomingInvite#accepted + * @fires IncomingInvite#canceled + * @fires IncomingInvite#failed + * @fires IncomingInvite#rejected + */ +function IncomingInvite(inviteServerTransaction, options) { + if (!(this instanceof IncomingInvite)) { + return new IncomingInvite(inviteServerTransaction, options); + } + + options = util.withDefaults({ }, options, { + logLevel: C.DEFAULT_LOG_LEVEL + }); + + var self = this; + EventEmitter.call(this); + + var conversation = null; + var from = util.getUser(inviteServerTransaction.from); + var localMedia = null; + var participants = [from]; + var participantSid = inviteServerTransaction.participantSid; + var pending = 0; + var shouldStopLocalMediaOnFailure = false; + var status = 'pending'; + + var deferred = util.defer(); + var inviteServerTransactions = new Set(); + inviteServerTransactions.add(inviteServerTransaction); + + /* istanbul ignore next */ + Object.defineProperties(this, { + _conversation: { + set: function(_conversation) { + conversation = _conversation; + }, + get: function() { + return conversation; + } + }, + _deferred: { + value: deferred + }, + _inviteServerTransaction: { + value: inviteServerTransaction + }, + _inviteServerTransactions: { + value: inviteServerTransactions + }, + _localMedia: { + get: function() { + return localMedia; + }, + set: function(_localMedia) { + localMedia = _localMedia; + } + }, + _logLevel: { + value: options.logLevel + }, + _options: { + get: function() { + return options; + }, + set: function(_options) { + options = _options; + } + }, + _pending: { + get: function() { + return pending; + }, + set: function(_pending) { + pending = _pending; + } + }, + _promise: { + value: deferred.promise + }, + _shouldStopLocalMediaOnFailure: { + get: function() { + return shouldStopLocalMediaOnFailure; + }, + set: function(_shouldStopLocalMediaOnFailure) { + shouldStopLocalMediaOnFailure = _shouldStopLocalMediaOnFailure; + } + }, + _status: { + get: function() { + return status; + }, + set: function(_status) { + status = _status; + } + }, + conversationSid: { + enumerable: true, + value: inviteServerTransaction.conversationSid + }, + from: { + enumerable: true, + value: from + }, + participants: { + enumerable: true, + value: participants + }, + participantSid: { + enumerable: true, + value: participantSid + }, + status: { + enumerable: true, + get: function() { + return status; + } + } + }); + + inviteServerTransaction.once('canceled', function() { + self.emit('canceled', self); + }); + + return this; +} + +inherits(IncomingInvite, EventEmitter); + +IncomingInvite.prototype._onAcceptFailure = function _onAcceptFailure(reason) { + this._pending--; + + if (this.status === 'accepting' && !this._pending) { + this._status = 'failed'; + if (this._shouldStopLocalMediaOnFailure && this._localMedia) { + this._localMedia.stop(); + } + this._deferred.reject(reason); + this.emit('failed', this); + } + + return this; +}; + +IncomingInvite.prototype._onDialog = function _onDialog(dialog) { + this._pending--; + + var conversation + = this._conversation + = this._conversation || new Conversation(this._options); + + conversation._onDialog(dialog); + + if (this.status === 'accepting') { + this._status = 'accepted'; + this._deferred.resolve(conversation); + this.emit('accepted', this); + } + + return conversation; +}; + +IncomingInvite.prototype._onInviteServerTransaction = function _onInviteServerTransaction(inviteServerTransaction) { + switch (this.status) { + case 'canceled': + case 'rejected': + inviteServerTransaction.reject(); + return; + case 'accepting': + if (!this._inviteServerTransactions.has(inviteServerTransaction)) { + this.participants.push(util.getUser(inviteServerTransaction.from)); + this._inviteServerTransactions.add(inviteServerTransaction); + } + this._pending++; + inviteServerTransaction.accept(this._options) + .then(this._onDialog.bind(this), this._onAcceptFailure.bind(this)); + return; + case 'accepted': + this._conversation._onInviteServerTransaction(inviteServerTransaction); + return; + default: + if (!this._inviteServerTransactions.has(inviteServerTransaction)) { + this.participants.push(util.getUser(inviteServerTransaction.from)); + this._inviteServerTransactions.add(inviteServerTransaction); + } + } +}; + +/** + * Accept the {@link IncomingInvite} and join the {@link Conversation}. + * @param {IncomingInvite.AcceptOptions} + * [options={localStreamConstraints:{audio:true,video:true}}] - Options to override + * {@link IncomingInvite#accept}'s default behavior + * @fires IncomingInvite#accepted + * @fires IncomingInvite#failed + * @returns {Promise} + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.on('invite', function(invite) { + * console.log('Received IncomingInvite to join a Conversation with ' + invite.from); + * + * // By default, accept will request the microphone and camera for you. + * invite.accept(); + * }); + */ +IncomingInvite.prototype.accept = function accept(options) { + if (this.status === 'accepted') { + return this._promise; + } + + options = this._options = util.withDefaults({ }, options, this._options); + this._status = 'accepting'; + + var self = this; + + function getLocalMedia() { + if (!options.localMedia && !options.localStream) { + self._shouldStopLocalMediaOnFailure = true; + self._options.shouldStopLocalMediaOnDisconnect = true; + } + return LocalMedia.getLocalMedia(options); + } + + getLocalMedia().then(function(localMedia) { + self._localMedia = localMedia; + options.localMedia = localMedia; + self._inviteServerTransactions.forEach(self._onInviteServerTransaction, self); + }); + + return this._promise; +}; + +/** + * Reject the {@link IncomingInvite} to a {@link Conversation}. + * @fires IncomingInvite#rejected + * @example + * var initialToken = getAccessToken(); + * var manager = new Twilio.AccessManager(initialToken); + * var client = new Twilio.Conversations.Client(manager); + * + * client.on('invite', function(invite) { + * console.log('Rejecting IncomingInvite to join a Conversation with ' + invite.from); + * invite.reject(); + * }); + * + * @returns {this} + */ +IncomingInvite.prototype.reject = function reject() { + this._inviteServerTransaction.reject(); + this.emit('rejected'); + return this; +}; + +Object.freeze(IncomingInvite.prototype); + +/** + * The {@link IncomingInvite} was accepted, and the {@link Client} is now + * participating in the {@link Conversation}. + * @param {IncomingInvite} invite - The {@link IncomingInvite} + * @event IncomingInvite#accepted + */ + +/** + * The {@link IncomingInvite} was rejected. + * @param {IncomingInvite} invite - The {@link IncomingInvite} + * @event IncomingInvite#rejected + */ + +/** + * The {@link IncomingInvite} was canceled. + * @param {IncomingInvite} invite - The {@link IncomingInvite} + * @event IncomingInvite#canceled + */ + +/** + * The {@link IncomingInvite} failed. + * @param {IncomingInvite} invite - The {@link IncomingInvite} + * @event IncomingInvite#failed + */ + +/** + * You may pass these options to {@link IncomingInvite#accept} to + * override the default behavior. + * @typedef {object} IncomingInvite.AcceptOptions + * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing + * {@link LocalMedia} object when accepting an {@link IncomingInvite} + * @property {?MediaStream} [localStream=null] - Set to reuse an existing + * MediaStream when accepting an {@link IncomingInvite} + * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to + * override the parameters passed to getUserMedia when neither + * localMedia nor localStream are provided + */ + +module.exports = IncomingInvite; + +},{"./conversation":3,"./media/localmedia":6,"./util":31,"./util/constants":30,"events":44,"util":48}],5:[function(require,module,exports){ +'use strict'; + +var EventEmitter = require('events').EventEmitter; +var inherits = require('util').inherits; + +var Track = require('./track'); +var AudioTrack = require('./track/audiotrack'); +var VideoTrack = require('./track/videotrack'); + +/** + * Construct a {@link Media} object. + * @class + * @classdesc A {@link Media} object contains a number of {@link AudioTrack}s + * and {@link VideoTrack}s. You can call {@link Media#attach} with a + * <div> to automatically update your application's user interface with + * <audio> and <video> elements as {@link Track}s are added and + * removed. + * @property {Map>} attachments - A Map + * from <div> elements to a Map from {@link Track}s to their attached + * HTMLElements (managed by {@link Media#attach} and {@link Media#detach}) + * @property {Map} audioTracks - The {@link AudioTrack}s on + * this {@link Media} object + * @property {boolean} isMuted - True if every {@link AudioTrack} on this + * {@link Media} object is disabled + * @property {boolean} isPaused - True if every {@link VideoTrack} on this + * {@link Media} object is disabled + * @property {Set} mediaStreams - The MediaStreams associated with + * the {@link Track}s on this {@link Media} object + * @property {Map} tracks - The {@link AudioTrack}s and + * {@link VideoTrack}s on this {@link Media} object + * @property {Map} videoTracks - The {@link VideoTrack}s on + * this {@link Media} object + * @fires Media#trackAdded + * @fires Media#trackDimensionsChanged + * @fires Media#trackDisabled + * @fires Media#trackEnabled + * @fires Media#trackEnded + * @fires Media#trackRemoved + * @fires Media#trackStarted + */ +function Media() { + EventEmitter.call(this); + + var attachments = new Map(); + var audioTracks = new Map(); + var mediaStreams = new Set(); + var tracks = new Map(); + var videoTracks = new Map(); + + /* istanbul ignore next */ + Object.defineProperties(this, { + attachments: { + enumerable: true, + value: attachments + }, + audioTracks: { + enumerable: true, + value: audioTracks + }, + isMuted: { + enumerable: true, + get: function() { + var isMuted = true; + audioTracks.forEach(function(track) { + isMuted = isMuted && !track.isEnabled; + }); + return isMuted; + } + }, + isPaused: { + enumerable: true, + get: function() { + var isPaused = true; + videoTracks.forEach(function(track) { + isPaused = isPaused && !track.isEnabled; + }); + return isPaused; + } + }, + mediaStreams: { + enumerable: true, + value: mediaStreams + }, + tracks: { + enumerable: true, + value: tracks + }, + videoTracks: { + enumerable: true, + value: videoTracks + } + }); + return this; +} + +var TRACK_ADDED = Media.TRACK_ADDED = 'trackAdded'; +var TRACK_DIMENSIONS_CHANGED = Media.TRACK_DIMENSIONS_CHANGED = 'trackDimensionsChanged'; +var TRACK_DISABLED = Media.TRACK_DISABLED = 'trackDisabled'; +var TRACK_ENABLED = Media.TRACK_ENABLED = 'trackEnabled'; +var TRACK_ENDED = Media.TRACK_ENDED = 'trackEnded'; +var TRACK_REMOVED = Media.TRACK_REMOVED = 'trackRemoved'; +var TRACK_STARTED = Media.TRACK_STARTED = 'trackStarted'; + +inherits(Media, EventEmitter); + +Media.prototype._addRemoteStream = function _addRemoteStream(mediaStream) { + mediaStream.getAudioTracks().forEach(function(mediaStreamTrack) { + var audioTrack = new AudioTrack(mediaStream, mediaStreamTrack); + this._addTrack(audioTrack); + }, this); + mediaStream.getVideoTracks().forEach(function(mediaStreamTrack) { + var videoTrack = new VideoTrack(mediaStream, mediaStreamTrack); + this._addTrack(videoTrack); + }, this); + return this; +}; + +Media.prototype._updateMediaStreams = function _updateMediaStreams() { + this.mediaStreams.clear(); + this.tracks.forEach(function(track) { + this.mediaStreams.add(track.mediaStream); + }, this); + return this.mediaStreams; +}; + +Media.prototype._addTrack = function _addTrack(track) { + if (this.tracks.has(track.id)) { + return this; + } + + var self = this; + this.mediaStreams.add(track.mediaStream); + + this.tracks.set(track.id, track); + this._reemitTrackEvent(track, VideoTrack.DIMENSIONS_CHANGED, TRACK_DIMENSIONS_CHANGED); + this._reemitTrackEvent(track, Track.DISABLED, TRACK_DISABLED); + this._reemitTrackEvent(track, Track.ENABLED, TRACK_ENABLED); + this._reemitTrackEvent(track, Track.ENDED, TRACK_ENDED); + this._reemitTrackEvent(track, Track.STARTED, TRACK_STARTED); + if (track.kind === 'audio') { + this._addAudioTrack(track); + } else { + this._addVideoTrack(track); + } + this._updateMediaStreams(); + + track.once(Track.ENDED, function ended() { + self._removeTrack(track); + }); + + this.emit(TRACK_ADDED, track); + + return this; +}; + +Media.prototype._addAudioTrack = function _addAudioTrack(track) { + this.audioTracks.set(track.id, track); + return this; +}; + +Media.prototype._addVideoTrack = function _addVideoTrack(track) { + this.videoTracks.set(track.id, track); + return this; +}; + +Media.prototype._reemitTrackEvent = function _reemitTrackEvent(track, trackEvent, event) { + var self = this; + var trackSet = track.kind === 'audio' ? this.audioTracks : this.videoTracks; + track.on(trackEvent, function onTrackEvent() { + // FIXME(mroberts): Lazily remove the event handler, but what happens + // if we add the Track twice? We only want to emit the event once. + if (!trackSet.has(track.id)) { + return track.removeListener(trackEvent, onTrackEvent); + } + self.emit(event, track); + }); + return this; +}; + +/** + * Add any new {@link Track}s that did not trigger the onaddtrack event. WebRTC + * does not always call this callback, so we have to check ourselves. + * @private + * @returns {Media} + */ +Media.prototype._refreshTracks = function _refreshTracks() { + this.mediaStreams.forEach(this._addRemoteStream, this); + return this; +}; + +Media.prototype._attachTrack = function _attachTrack(el, attachments, track) { + var self = this; + var trackEl = track.attach(); + // NOTE(mroberts): We want to mute local audio, otherwise we get feedback. + if (this.constructor !== Media && track instanceof AudioTrack) { + trackEl.muted = true; + } + el.appendChild(trackEl); + attachments.set(track, trackEl); + track.once('ended', function() { + self._detachTrack(el, attachments, track); + }); + return this; +}; + +Media.prototype._detachTrack = function _detachTrack(el, attachments, track) { + var trackEl = attachments.get(track); + if (!trackEl) { + return this; + } + track.detach(trackEl); + if (trackEl.parentNode) { + trackEl.parentNode.removeChild(trackEl); + } + attachments.delete(track); + return this; +}; + +Media.prototype._removeTrack = function _removeTrack(track) { + if (!this.tracks.has(track.id)) { + return this; + } + this.tracks.delete(track.id); + (track.kind === 'audio' ? this.audioTracks : this.videoTracks).delete(track.id); + this._updateMediaStreams(); + this.emit(TRACK_REMOVED, track); + return this; +}; + +/** + * Attach the {@link Media} to a newly created <div> element. + * @returns {HTMLElement} + * @example + * var remoteMediaEl = media.attach(); + * document.getElementById('div#remote-media-container').appendChild(remoteMediaEl); +*//** + * Attach the {@link Media} to an existing HTMLElement. + * @param {HTMLElement} el - The HTMLElement to attach to + * @returns {HTMLElement} + * @example + * var remoteMediaEl = document.getElementById('remote-media'); + * media.attach(remoteMediaEl); +*//** + * Attach the {@link Media} to an HTMLElement selected by + * document.querySelector. + * @param {string} selector - A query selector for the HTMLElement to attach to + * @returns {HTMLElement} + * @example + * var remoteMediaEl = media.attach('div#remote-media'); + */ +Media.prototype.attach = function attach(el) { + if (!el) { + return createDivAndAttach(this); + } else if (typeof el === 'string') { + return selectElementAndAttach(this, el); + } + return attachToElement(this, el); +}; + +function attachToElement(media, el) { + if (media.attachments.has(el)) { + return el; + } + var attachments = new Map(); + // Attach existing audio and video tracks to the element, + media.tracks.forEach(function(track) { + media._attachTrack(el, attachments, track); + }, media); + // And update the element as tracks are added, + media.on(TRACK_ADDED, function trackAdded(track) { + // But stop updating the element if we've been detached. + if (!media.attachments.has(el)) { + return media.removeListener(TRACK_ADDED, trackAdded); + } + media._attachTrack(el, attachments, track); + }); + media.on(TRACK_REMOVED, function trackRemoved(track) { + if (!media.attachments.has(el)) { + return media.removeListener(TRACK_REMOVED, trackRemoved); + } + media._detachTrack(el, attachments, track); + }); + media.attachments.set(el, attachments); + return el; +} + +function selectElementAndAttach(media, selector) { + if (typeof document === 'undefined') { + throw new Error('document is undefined'); + } + var el = document.querySelector(selector); + if (!el) { + throw new Error('document.querySelector returned nothing'); + } + return media.attach(el); +} + +function createDivAndAttach(media) { + if (typeof document === 'undefined') { + throw new Error('document is undefined'); + } + return media.attach(document.createElement('div')); +} + +/** + * Detach the {@link Media} from any and all previously attached HTMLElements. + * @returns {Array} + * @example + * var detachedMediaEls = media.detach(); +*//** + * Detach the {@link Media} from a previously attached HTMLElement. + * @param {HTMLElement} el - The HTMLElement to detach from + * @returns {HTMLElement} + * @example + * var remoteMediaEl = document.getElementById('remote-media'); + * media.detach(remoteMediaEl); +*//** + * Detach the {@link Media} from a previously attached HTMLElement selected by + * document.querySelector. + * @param {string} selector - A query selector for the HTMLElement to detach from + * @returns {HTMLElement} + * @example + * var detachedMediaEl = media.detach('div#remote-media'); + */ +Media.prototype.detach = function detach(el) { + if (!el) { + return detachFromAllElements(this); + } else if (typeof el === 'string') { + return selectElementAndDetach(this, el); + } + return detachFromElement(this, el); +}; + +function detachFromElement(media, el) { + if (!media.attachments.has(el)) { + return el; + } + var attachments = media.attachments.get(el); + media.attachments.delete(el); + attachments.forEach(function(trackEl, track) { + media._detachTrack(el, attachments, track); + }); + return el; +} + +function selectElementAndDetach(media, selector) { + if (typeof document === 'undefined') { + throw new Error('document is undefined'); + } + var el = document.querySelector(selector); + if (!el) { + throw new Error('document.querySelector returned nothing'); + } + return detachFromElement(media, el); +} + +function detachFromAllElements(media) { + var els = []; + media.attachments.forEach(function(attachments, el) { + els.push(el); + detachFromElement(media, el); + }); + return els; +} + +/** + * A {@link Track} was added to this {@link Media} object. + * @param {Track} track - The {@link Track} that was added + * @event Media#trackAdded + */ + +/** + * The dimensions of a {@link VideoTrack} on this {@link Media} object changed. + * @param {VideoTrack} track - The {@link VideoTrack} whose dimensions changed + * @event Media#trackDimensionsChanged + */ + +/** + * A {@link Track} on this {@link Media} object was disabled. + * @param {Track} track - The {@link Track} that was disabled + * @event Media#trackDisabled + */ + +/** + * A {@link Track} on this {@link Media} object was enabled. + * @param {Track} track - The {@link Track} that was enabled + * @event Media#trackEnabled + */ + +/** + * A {@link Track} on this {@link Media} object ended. + * @param {Track} track - The {@link Track} that ended + * @event Media#trackEnded + */ + +/** + * A {@link Track} was removed from this {@link Media} object. + * @param {Track} track - The {@link Track} that was removed + * @event Media#trackRemoved + */ + +/** + * A {@link Track} on this {@link Media} object was started. + * @param {Track} track - The {@link Track} that was started + * @event Media#trackStarted + */ + +module.exports = Media; + +},{"./track":8,"./track/audiotrack":7,"./track/videotrack":12,"events":44,"util":48}],6:[function(require,module,exports){ +'use strict'; + +var getUserMedia = require('../webrtc/getusermedia'); +var inherits = require('util').inherits; +var LocalAudioTrack = require('./track/localaudiotrack'); +var LocalVideoTrack = require('./track/localvideotrack'); +var Media = require('./'); + +/** + * Construct a {@link LocalMedia} object. + * @class + * @classdesc A {@link LocalMedia} object is a {@link Media} object representing + * {@link LocalAudioTrack}s and {@link LocalVideoTrack}s that your {@link Client} may + * share in a {@link Conversation}. + * @extends Media + * @property {Map} audioTracks - The {@link LocalAudioTrack}s on + * this {@link Media} object + * @property {Map} tracks - The {@link LocalAudioTrack}s and + * {@link LocalVideoTrack}s on this {@link Media} object + * @property {Map} videoTracks - The {@link LocalVideoTrack}s on + * this {@link Media} object + */ +function LocalMedia() { + if (!(this instanceof LocalMedia)) { + return new LocalMedia(); + } + Media.call(this); + return this; +} + +/** + * Get {@link LocalMedia}. By default, this requests a + * {@link LocalAudioTrack} and a {@link LocalVideoTrack} representing a microphone and + * camera. + *

+ * This method calls getUserMedia internally. Pass in + * options to override the default behavior. + * @param {?LocalMedia.GetLocalMediaOptions} + * [options={localStreamConstraints:{audio:true,video:true}}] - Options to override + * {@link LocalMedia.getLocalMedia}'s default behavior + * @returns {Promise} + */ +LocalMedia.getLocalMedia = function getLocalMedia(options) { + options = options || {}; + if (options.localMedia) { + return Promise.resolve(options.localMedia); + } + var localMedia = new LocalMedia(); + if (options.localStream) { + return Promise.resolve(localMedia.addStream(options.localStream)); + } + return getUserMedia(options.localStreamConstraints) + .then(function(mediaStream) { + return localMedia.addStream(mediaStream); + }); +}; + +inherits(LocalMedia, Media); + +/** + * Adds a {@link LocalTrack} to the {@link LocalMedia} object, if not already added. + * @method + * @param {LocalTrack} track - The {@link LocalTrack} to add + * @returns {this} + * @fires Media#trackAdded + */ +LocalMedia.prototype.addTrack = Media.prototype._addTrack; + +/** + * Removes a {@link LocalTrack} from the {@link LocalMedia} object, if it was added. + * @method + * @param {LocalTrack} track - The {@link LocalTrack} to remove + * @param {?boolean} [stop=true] - Whether or not to call + * {@link LocalTrack#stop} + * @returns {this} + * @fires Media#trackRemoved + */ +LocalMedia.prototype.removeTrack = Media.prototype._removeTrack; + +/** + * Adds a {@link LocalAudioTrack} representing your browser's microphone to the + * {@link LocalMedia} object, if not already added. + *

+ * Internally, this calls getUserMedia({ audio: true }). + * @returns {Promise} + * @fires Media#trackAdded + */ +LocalMedia.prototype.addMicrophone = function addMicrophone() { + var self = this; + var microphone = null; + this.audioTracks.forEach(function(audioTrack) { + microphone = microphone || audioTrack; + }); + if (microphone) { + return Promise.resolve(microphone); + } + return getUserMedia({ audio: true, video: false }) + .then(function gotMicrophone(mediaStream) { + var audioTracks = mediaStream.getAudioTracks(); + var mediaStreamTrack = audioTracks[0]; + var audioTrack = new LocalAudioTrack(mediaStream, mediaStreamTrack); + self._addTrack(audioTrack); + return audioTrack; + }); +}; + +/** + * Removes the {@link LocalAudioTrack} representing your browser's microphone, if it + * has been added. + * @returns {?LocalAudioTrack} + * @fires Media#trackRemoved + */ +LocalMedia.prototype.removeMicrophone = function removeMicrophone() { + var microphone = null; + this.audioTracks.forEach(function(audioTrack) { + microphone = microphone || audioTrack; + }); + if (microphone) { + return this._removeTrack(microphone); + } + return null; +}; + +/** + * Adds a {@link LocalVideoTrack} representing your browser's camera to the + * {@link LocalMedia} object, if not already added. + *

+ * Internally, this calls getUserMedia({ video: true }). + * @returns {Promise} + * @fires Media#trackAdded + */ +LocalMedia.prototype.addCamera = function addCamera() { + var self = this; + var camera = null; + this.videoTracks.forEach(function(videoTrack) { + camera = camera || videoTrack; + }); + if (camera) { + return Promise.resolve(camera); + } + return getUserMedia({ audio: false, video: true }) + .then(function gotCamera(mediaStream) { + var videoTracks = mediaStream.getVideoTracks(); + var mediaStreamTrack = videoTracks[0]; + var videoTrack = new LocalVideoTrack(mediaStream, mediaStreamTrack); + self._addTrack(videoTrack); + return videoTrack; + }); +}; + +/** + * Removes the {@link LocalVideoTrack} representing your browser's camera, if it + * has been added. + * @returns {?LocalVideoTrack} + * @fires Media#trackRemoved + */ +LocalMedia.prototype.removeCamera = function removeCamera() { + var camera = null; + this.videoTracks.forEach(function(videoTrack) { + camera = camera || videoTrack; + }); + if (camera) { + return this._removeTrack(camera); + } + return null; +}; + +/** + * Add a MediaStream to the {@link LocalMedia} object, constructing + * {@link LocalTrack}s as necessary for each MediaStreamTrack contained + * within. + * @param {MediaStream} mediaStream - The MediaStream to add + * @returns {this} + * @fires Media#trackAdded + */ +LocalMedia.prototype.addStream = function addStream(mediaStream) { + mediaStream.getAudioTracks().forEach(function(mediaStreamTrack) { + var audioTrack = new LocalAudioTrack(mediaStream, mediaStreamTrack); + this._addTrack(audioTrack); + }, this); + mediaStream.getVideoTracks().forEach(function(mediaStreamTrack) { + var videoTrack = new LocalVideoTrack(mediaStream, mediaStreamTrack); + this._addTrack(videoTrack); + }, this); + return this; +}; + +/** + * Remove a MediaStream from the {@link LocalMedia} object. This + * will remove any {@link LocalTrack}s corresponding to + * MediaStreamTracks contained within the MediaStream. + * @param {MediaStream} mediaStream - The MediaStream to remove + * @param {?boolean} [stop=true] - Whether or not to call + * {@link LocalTrack#stop} on the corresponding {@link LocalTrack}s + * @returns {this} + * @fires Media#trackRemoved + */ +LocalMedia.prototype.removeStream = function removeStream(mediaStream, stop) { + mediaStream.getTracks().forEach(function(mediaStreamTrack) { + var track = this.tracks.get(mediaStreamTrack.id); + if (track) { + this.removeTrack(track, stop); + } + }, this); + return this; +}; + +LocalMedia.prototype._removeTrack = function _removeTrack(track, stop) { + if (typeof stop === 'boolean' ? stop : true) { + track.stop(); + } + try { + track.mediaStream.removeTrack(track.mediaStreamTrack); + } catch (error) { + // Firefox doesn't support removeStream/removeTrack, so we can't yet truly + // remove and renegotiate media. + } + Media.prototype._removeTrack.call(this, track); + return track; +}; + +/** + * Disable every {@link LocalAudioTrack} on this {@link LocalMedia} object. + * @returns {this} + * @fires Media#trackDisabled +*//** + * Disable or enable every {@link LocalAudioTrack} on this {@link LocalMedia} object. + * @param {?boolean} enabled - Specify false to enable the {@link LocalAudioTrack}s + * @returns {this} + * @fires Media#trackDisabled + * @fires Media#trackEnabled + */ +LocalMedia.prototype.mute = function mute(muted) { + muted = typeof muted === 'boolean' ? muted : true; + this.audioTracks.forEach(function(track) { + track.enable(!muted); + }); + return this; +}; + +/** + * Disable every {@link LocalVideoTrack} on this {@link LocalMedia} object. + * @returns {this} + * @fires Media#trackDisabled +*//** + * Disable or enable every {@link LocalVideoTrack} on this {@link LocalMedia} object. + * @param {?boolean} enabled - Specify false to enable the {@link LocalVideoTrack}s + * @returns {this} + * @fires Media#trackDisabled + * @fires Media#trackEnabled + */ +LocalMedia.prototype.pause = function pause(paused) { + paused = typeof paused === 'boolean' ? paused : true; + this.videoTracks.forEach(function(track) { + track.enable(!paused); + }); + return this; +}; + +/** + * Stop all {@link LocalAudioTrack}s and {@link LocalVideoTrack}s on this {@link LocalMedia} object. + * @returns {this} + * @fires Media#trackEnded + */ +LocalMedia.prototype.stop = function stop() { + this.tracks.forEach(function(track) { + track.stop(); + }); + return this; +}; + +/** + * Enable every {@link LocalAudioTrack} on this {@link LocalMedia} object. + * @returns {this} + * @fires Media#trackEnabled + */ +LocalMedia.prototype.unmute = function unmute() { + return this.mute(false); +}; + +/** + * Enable every {@link LocalVideoTrack} on this {@link LocalMedia} object. + * @returns {this} + * @fires Media#trackEnabled + */ +LocalMedia.prototype.unpause = function unpause() { + return this.pause(false); +}; + +/** + * You may pass these options to {@link LocalMedia.getLocalMedia} to + * override the default behavior. + * @typedef {object} LocalMedia.GetLocalMediaOptions + * @property {?LocalMedia} [localMedia=null] - Set to reuse an existing + * {@link LocalMedia} object + * @property {?MediaStream} [localStream=null] - Set to reuse an existing + * MediaStream + * @property {?object} [localStreamConstraints={audio:true,video:true}] - Set to + * override the parameters passed to getUserMedia when neither + * localMedia nor localStream are provided + */ + +module.exports = LocalMedia; + +},{"../webrtc/getusermedia":39,"./":5,"./track/localaudiotrack":9,"./track/localvideotrack":11,"util":48}],7:[function(require,module,exports){ +'use strict'; + +var inherits = require('util').inherits; +var Track = require('./'); + +/** + * Construct an {@link AudioTrack} from MediaStream and MediaStreamTrack. + * @class + * @classdesc An {@link AudioTrack} is a {@link Track} representing audio. + * @extends Track + * @param {MediaStream} mediaStream + * @param {MediaStreamTrack} mediaStreamTrack + * @property {Set} attachments - The <audio> elements this + * {@link AudioTrack} is currently attached to (managed by + * {@link AudioTrack#attach}) + */ +function AudioTrack(mediaStream, mediaStreamTrack) { + Track.call(this, mediaStream, mediaStreamTrack); +} + +inherits(AudioTrack, Track); + +/** + * Attach the {@link AudioTrack} to a newly created <audio> element. + * @method + * @returns {HTMLElement} + * @example + * var remoteAudioEl = audioTrack.attach(); + * document.getElementById('div#remote-audio-container').appendChild(remoteAudioEl); +*//** + * Attach the {@link AudioTrack} to an existing <audio> element. + * @method + * @param {HTMLElement} audio - The <audio> element to attach to + * @returns {HTMLElement} + * @example + * var remoteAudioEl = document.getElementById('remote-audio'); + * audioTrack.attach(remoteAudioEl); +*//** + * Attach the {@link AudioTrack} to a <audio> element selected by + * document.querySelector. + * @method + * @param {string} selector - A query selector for the <audio> element to attach to + * @returns {HTMLElement} + * @example + * var remoteAudioEl = audioTrack.attach('audio#remote-audio'); + */ +AudioTrack.prototype.attach = Track.prototype.attach; + +/** + * Detach the {@link AudioTrack} from any and all previously attached <audio> elements. + * @method + * @returns {Array} + * @example + * var detachedAudioEls = audioTrack.detach(); +*//** + * Detach the {@link AudioTrack} from a previously attached <audio> element. + * @method + * @param {HTMLElement} audio - The <audio> element to detach from + * @returns {HTMLElement} + * @example + * var remoteAudioEl = document.getElementById('remote-audio'); + * audioTrack.detach(remoteAudioEl); +*//** + * Detach the {@link AudioTrack} from a previously attached <audio> element selected by + * document.querySelector. + * @method + * @param {string} selector - A query selector for the <audio> element to detach from + * @returns {HTMLElement} + * @example + * var detachedAudioEl = media.detach('div#remote-audio'); + */ +AudioTrack.prototype.detach = Track.prototype.detach; + +module.exports = AudioTrack; + +},{"./":8,"util":48}],8:[function(require,module,exports){ +'use strict'; + +var EventEmitter = require('events').EventEmitter; +var inherits = require('util').inherits; + +/** + * Construct a {@link Track} from a MediaStream and MediaStreamTrack. + * @class + * @classdesc A {@link Track} represents audio or video that can be sent to or + * received from a {@link Conversation}. {@link Track}s abstract away the notion + * of MediaStream and MediaStreamTrack. + * @param {MediaStream} mediaStream + * @param {MediaStreamTrack} mediaStreamTrack + * @property {Track.ID} id - This {@link Track}'s ID + * @property {boolean} isEnded - Whether or not the {@link Track} has ended + * @property {boolean} isStarted - Whether or not the {@link Track} has started + * @property {boolean} isEnabled - Whether or not the {@link Track} is enabled + * (i.e., whether it is paused or muted) + * @property {string} kind - The kind of the underlying + * {@link MediaStreamTrack}; e.g. "audio" or "video" + * @property {MediaStream} mediaStream - The underlying MediaStream + * @property {MediaStreamTrack} mediaStreamTrack - The underlying + * MediaStreamTrack + * @fires Track#disabled + * @fires Track#enabled + * @fires Track#ended + * @fires Track#started + */ +function Track(mediaStream, mediaStreamTrack) { + EventEmitter.call(this); + var isEnabled = true; + var isEnded = false; + var isStarted = false; + /* istanbul ignore next */ + Object.defineProperties(this, { + _isEnabled: { + get: function() { + return isEnabled; + }, + set: function(_isEnabled) { + isEnabled = _isEnabled; + } + }, + _isEnded: { + get: function() { + return isEnded; + }, + set: function(_isEnded) { + isEnded = _isEnded; + } + }, + _isStarted: { + get: function() { + return isStarted; + }, + set: function(_isStarted) { + isStarted = _isStarted; + } + }, + attachments: { + value: new Set() + }, + id: { + enumerable: true, + value: mediaStreamTrack.id + }, + isEnabled: { + get: function() { + return isEnabled; + } + }, + isEnded: { + get: function() { + return isEnded; + } + }, + isStarted: { + get: function() { + return isStarted; + } + }, + kind: { + enumerable: true, + value: mediaStreamTrack.kind + }, + mediaStream: { + enumerable: true, + value: mediaStream + }, + mediaStreamTrack: { + enumerable: true, + value: mediaStreamTrack + } + }); + var self = this; + mediaStreamTrack.onended = function onended() { + /* eslint no-use-before-define:0 */ + self.emit(ENDED, self); + }; + emitStartedEvent(this); +} + +var DISABLED = Track.DISABLED = 'disabled'; +var ENABLED = Track.ENABLED = 'enabled'; +var ENDED = Track.ENDED = 'ended'; +var STARTED = Track.STARTED = 'started'; + +function emitStartedEvent(track) { + if (typeof document === 'undefined') { + throw new Error('document is undefined'); + } + var elem = document.createElement(track.kind); + elem.muted = true; + elem.oncanplay = function oncanplay() { + track.detach(elem); + track._isStarted = true; + if (track.kind === 'video') { + track.dimensions.width = elem.videoWidth; + track.dimensions.height = elem.videoHeight; + } + track.emit(STARTED, track); + elem.oncanplay = null; + }; + track.once(ENDED, function() { + track.detach(elem); + elem.oncanplay = null; + }); + return track.attach(elem); +} + +function attachAudio(audio, mediaStream) { + if (typeof window !== 'undefined' && typeof navigator !== 'undefined') { + if (typeof navigator.webkitGetUserMedia === 'function') { + var vendorURL = window.URL || window.webkitURL; + audio.src = vendorURL.createObjectURL(mediaStream); + } else if (typeof navigator.mozGetUserMedia === 'function') { + audio.mozSrcObject = mediaStream; + } + audio.play(); + return audio; + } + throw new Error('Cannot attach to