Update libs/app.bundle.js.
This commit is contained in:
parent
ee6fd63c25
commit
777422c87d
|
@ -19,7 +19,7 @@
|
||||||
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
|
||||||
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
|
||||||
<script src="interface_config.js?v=5"></script>
|
<script src="interface_config.js?v=5"></script>
|
||||||
<script src="libs/app.bundle.js?v=67"></script>
|
<script src="libs/app.bundle.js?v=68"></script>
|
||||||
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
|
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
|
||||||
<link rel="stylesheet" href="css/font.css?v=7"/>
|
<link rel="stylesheet" href="css/font.css?v=7"/>
|
||||||
<link rel="stylesheet" href="css/toastr.css?v=1">
|
<link rel="stylesheet" href="css/toastr.css?v=1">
|
||||||
|
|
|
@ -705,28 +705,26 @@ var eventEmitter = new EventEmitter();
|
||||||
function getMediaStreamUsage()
|
function getMediaStreamUsage()
|
||||||
{
|
{
|
||||||
var result = {
|
var result = {
|
||||||
audio: 1,
|
audio: true,
|
||||||
video: 1
|
video: true
|
||||||
};
|
};
|
||||||
if( config.startAudioMuted === true)
|
|
||||||
result.audio = 0;
|
|
||||||
if( config.startVideoMuted === true)
|
|
||||||
result.video = 0;
|
|
||||||
|
|
||||||
/** There are some issues with the desktop sharing
|
/** There are some issues with the desktop sharing
|
||||||
* when this property is enabled.
|
* when this property is enabled.
|
||||||
|
* WARNING: We must change the implementation to start video/audio if we
|
||||||
|
* receive from the focus that the peer is not muted.
|
||||||
|
|
||||||
if(result.audio > 0 && result.video > 0)
|
|
||||||
return result;
|
|
||||||
var isSecureConnection = window.location.protocol == "https:";
|
var isSecureConnection = window.location.protocol == "https:";
|
||||||
|
|
||||||
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
||||||
{
|
{
|
||||||
if(result.audio === 0)
|
result = {
|
||||||
result.audio = -1;
|
audio: false,
|
||||||
if(result.video === 0)
|
video: false
|
||||||
result.video = -1;
|
};
|
||||||
}**/
|
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1276,7 +1274,9 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
|
||||||
* We ask for audio and video combined stream in order to get permissions and
|
* We ask for audio and video combined stream in order to get permissions and
|
||||||
* not to ask twice.
|
* not to ask twice.
|
||||||
*/
|
*/
|
||||||
RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) {
|
RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
||||||
|
function(devices, callback, usageOptions)
|
||||||
|
{
|
||||||
var self = this;
|
var self = this;
|
||||||
// Get AV
|
// Get AV
|
||||||
|
|
||||||
|
@ -1297,7 +1297,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback,
|
||||||
for(var i = 0; i < devices.length; i++)
|
for(var i = 0; i < devices.length; i++)
|
||||||
{
|
{
|
||||||
var device = devices[i];
|
var device = devices[i];
|
||||||
if(usageOptions[device] !== -1)
|
if(usageOptions[device] === true)
|
||||||
newDevices.push(device);
|
newDevices.push(device);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1431,11 +1431,12 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
||||||
videoStream = stream.videoStream;
|
videoStream = stream.videoStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
var audioMuted = (usageOptions && usageOptions.audio != 1),
|
var audioMuted = (usageOptions && usageOptions.audio === false),
|
||||||
videoMuted = (usageOptions && usageOptions.video != 1);
|
videoMuted = (usageOptions && usageOptions.video === false);
|
||||||
|
|
||||||
|
var audioGUM = (!usageOptions || usageOptions.audio !== false),
|
||||||
|
videoGUM = (!usageOptions || usageOptions.video !== false);
|
||||||
|
|
||||||
var audioGUM = (!usageOptions || usageOptions.audio != -1),
|
|
||||||
videoGUM = (!usageOptions || usageOptions.video != -1);
|
|
||||||
|
|
||||||
this.service.createLocalStream(audioStream, "audio", null, null,
|
this.service.createLocalStream(audioStream, "audio", null, null,
|
||||||
audioMuted, audioGUM);
|
audioMuted, audioGUM);
|
||||||
|
@ -1505,10 +1506,8 @@ var roomName = null;
|
||||||
|
|
||||||
function notifyForInitialMute()
|
function notifyForInitialMute()
|
||||||
{
|
{
|
||||||
if(config.startAudioMuted || config.startVideoMuted)
|
messageHandler.notify(null, "notify.mutedTitle", "connected",
|
||||||
{
|
"notify.muted", null, {timeOut: 120000});
|
||||||
messageHandler.notify(null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupPrezi()
|
function setupPrezi()
|
||||||
|
@ -1725,6 +1724,9 @@ function registerListeners() {
|
||||||
|
|
||||||
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
||||||
onDtmfSupportChanged);
|
onDtmfSupportChanged);
|
||||||
|
APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
|
||||||
|
SettingsMenu.setStartMuted(audio, video);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1879,8 +1881,6 @@ UI.start = function (init) {
|
||||||
|
|
||||||
SettingsMenu.init();
|
SettingsMenu.init();
|
||||||
|
|
||||||
notifyForInitialMute();
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function chatAddError(errorMessage, originalText)
|
function chatAddError(errorMessage, originalText)
|
||||||
|
@ -1956,6 +1956,7 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
|
||||||
console.info("My role changed, new role: " + info.role);
|
console.info("My role changed, new role: " + info.role);
|
||||||
onModeratorStatusChanged(isModerator);
|
onModeratorStatusChanged(isModerator);
|
||||||
VideoLayout.showModeratorIndicator();
|
VideoLayout.showModeratorIndicator();
|
||||||
|
SettingsMenu.onRoleChanged();
|
||||||
|
|
||||||
if (isModerator) {
|
if (isModerator) {
|
||||||
Authentication.closeAuthenticationWindow();
|
Authentication.closeAuthenticationWindow();
|
||||||
|
@ -2200,6 +2201,12 @@ UI.getRoomName = function () {
|
||||||
return roomName;
|
return roomName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
|
||||||
|
if(muteAudio || muteVideo) notifyForInitialMute();
|
||||||
|
if(muteAudio) UI.setAudioMuted(true);
|
||||||
|
if(muteVideo) UI.setVideoMute(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutes/unmutes the local video.
|
* Mutes/unmutes the local video.
|
||||||
*/
|
*/
|
||||||
|
@ -5090,7 +5097,7 @@ function generateLanguagesSelectBox()
|
||||||
var SettingsMenu = {
|
var SettingsMenu = {
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
$("#updateSettings").before(generateLanguagesSelectBox());
|
$("#startMutedOptions").before(generateLanguagesSelectBox());
|
||||||
APP.translation.translateElement($("#languages_selectbox"));
|
APP.translation.translateElement($("#languages_selectbox"));
|
||||||
$('#settingsmenu>input').keyup(function(event){
|
$('#settingsmenu>input').keyup(function(event){
|
||||||
if(event.keyCode === 13) {//enter
|
if(event.keyCode === 13) {//enter
|
||||||
|
@ -5098,11 +5105,36 @@ var SettingsMenu = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(APP.xmpp.isModerator())
|
||||||
|
{
|
||||||
|
$("#startMutedOptions").css("display", "block");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$("#startMutedOptions").css("display", "none");
|
||||||
|
}
|
||||||
|
|
||||||
$("#updateSettings").click(function () {
|
$("#updateSettings").click(function () {
|
||||||
SettingsMenu.update();
|
SettingsMenu.update();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRoleChanged: function () {
|
||||||
|
if(APP.xmpp.isModerator())
|
||||||
|
{
|
||||||
|
$("#startMutedOptions").css("display", "block");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$("#startMutedOptions").css("display", "none");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setStartMuted: function (audio, video) {
|
||||||
|
$("#startAudioMuted").attr("checked", audio);
|
||||||
|
$("#startVideoMuted").attr("checked", video);
|
||||||
|
},
|
||||||
|
|
||||||
update: function() {
|
update: function() {
|
||||||
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
|
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
|
||||||
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
|
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
|
||||||
|
@ -5119,6 +5151,10 @@ var SettingsMenu = {
|
||||||
APP.xmpp.addToPresence("email", newEmail);
|
APP.xmpp.addToPresence("email", newEmail);
|
||||||
var email = Settings.setEmail(newEmail);
|
var email = Settings.setEmail(newEmail);
|
||||||
|
|
||||||
|
var startAudioMuted = ($("#startAudioMuted").is(":checked"));
|
||||||
|
var startVideoMuted = ($("#startVideoMuted").is(":checked"));
|
||||||
|
APP.xmpp.addToPresence("startMuted",
|
||||||
|
[startAudioMuted, startVideoMuted]);
|
||||||
|
|
||||||
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
|
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
|
||||||
},
|
},
|
||||||
|
@ -15638,6 +15674,20 @@ var Moderator = {
|
||||||
{ name: 'openSctp', value: config.openSctp})
|
{ name: 'openSctp', value: config.openSctp})
|
||||||
.up();
|
.up();
|
||||||
}
|
}
|
||||||
|
if(config.startAudioMuted !== undefined)
|
||||||
|
{
|
||||||
|
elem.c(
|
||||||
|
'property',
|
||||||
|
{ name: 'startAudioMuted', value: config.startAudioMuted})
|
||||||
|
.up();
|
||||||
|
}
|
||||||
|
if(config.startVideoMuted !== undefined)
|
||||||
|
{
|
||||||
|
elem.c(
|
||||||
|
'property',
|
||||||
|
{ name: 'startVideoMuted', value: config.startVideoMuted})
|
||||||
|
.up();
|
||||||
|
}
|
||||||
elem.up();
|
elem.up();
|
||||||
return elem;
|
return elem;
|
||||||
},
|
},
|
||||||
|
@ -16187,6 +16237,13 @@ module.exports = function(XMPP, eventEmitter) {
|
||||||
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
|
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var startMuted = $(pres).find('>startmuted');
|
||||||
|
if (startMuted.length)
|
||||||
|
{
|
||||||
|
eventEmitter.emit(XMPPEvents.START_MUTED,
|
||||||
|
startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
|
||||||
|
}
|
||||||
|
|
||||||
var devices = $(pres).find('>devices');
|
var devices = $(pres).find('>devices');
|
||||||
if(devices.length)
|
if(devices.length)
|
||||||
{
|
{
|
||||||
|
@ -16541,6 +16598,15 @@ module.exports = function(XMPP, eventEmitter) {
|
||||||
|| 'sendrecv' }
|
|| 'sendrecv' }
|
||||||
).up();
|
).up();
|
||||||
}
|
}
|
||||||
|
pres.up();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.presMap["startMuted"] !== undefined)
|
||||||
|
{
|
||||||
|
pres.c("startmuted", {audio: this.presMap["startMuted"].audio,
|
||||||
|
video: this.presMap["startMuted"].video,
|
||||||
|
xmlns: "http://jitsi.org/jitmeet/start-muted"});
|
||||||
|
delete this.presMap["startMuted"];
|
||||||
}
|
}
|
||||||
|
|
||||||
pres.up();
|
pres.up();
|
||||||
|
@ -16621,6 +16687,9 @@ module.exports = function(XMPP, eventEmitter) {
|
||||||
addUserIdToPresence: function (userId) {
|
addUserIdToPresence: function (userId) {
|
||||||
this.presMap['userId'] = userId;
|
this.presMap['userId'] = userId;
|
||||||
},
|
},
|
||||||
|
addStartMutedToPresence: function (audio, video) {
|
||||||
|
this.presMap["startMuted"] = {audio: audio, video: video};
|
||||||
|
},
|
||||||
isModerator: function () {
|
isModerator: function () {
|
||||||
return this.role === 'moderator';
|
return this.role === 'moderator';
|
||||||
},
|
},
|
||||||
|
@ -16808,6 +16877,14 @@ module.exports = function(XMPP, eventEmitter)
|
||||||
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
|
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'session-initiate':
|
case 'session-initiate':
|
||||||
|
var startMuted = $(iq).find('jingle>startmuted');
|
||||||
|
if(startMuted && startMuted.length > 0)
|
||||||
|
{
|
||||||
|
var audioMuted = startMuted.attr("audio");
|
||||||
|
var videoMuted = startMuted.attr("video");
|
||||||
|
APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
|
||||||
|
(videoMuted === "true"));
|
||||||
|
}
|
||||||
sess = new JingleSession(
|
sess = new JingleSession(
|
||||||
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
||||||
this.connection, XMPP);
|
this.connection, XMPP);
|
||||||
|
@ -17450,8 +17527,13 @@ function registerListeners() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupEvents() {
|
var unload = (function () {
|
||||||
$(window).bind('beforeunload', function () {
|
var unloaded = false;
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
if (unloaded) { return; }
|
||||||
|
unloaded = true;
|
||||||
|
|
||||||
if (connection && connection.connected) {
|
if (connection && connection.connected) {
|
||||||
// ensure signout
|
// ensure signout
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
@ -17460,24 +17542,41 @@ function setupEvents() {
|
||||||
async: false,
|
async: false,
|
||||||
cache: false,
|
cache: false,
|
||||||
contentType: 'application/xml',
|
contentType: 'application/xml',
|
||||||
data: "<body rid='" + (connection.rid || connection._proto.rid)
|
data: "<body rid='" + (connection.rid || connection._proto.rid) +
|
||||||
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='"
|
"' xmlns='http://jabber.org/protocol/httpbind' sid='" +
|
||||||
+ (connection.sid || connection._proto.sid)
|
(connection.sid || connection._proto.sid) +
|
||||||
+ "' type='terminate'>" +
|
"' type='terminate'>" +
|
||||||
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
"<presence xmlns='jabber:client' type='unavailable'/>" +
|
||||||
"</body>",
|
"</body>",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
console.log('signed out');
|
console.log('signed out');
|
||||||
console.log(data);
|
console.log(data);
|
||||||
},
|
},
|
||||||
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
||||||
console.log('signout error',
|
console.log('signout error',
|
||||||
textStatus + ' (' + errorThrown + ')');
|
textStatus + ' (' + errorThrown + ')');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
XMPP.disposeConference(true);
|
XMPP.disposeConference(true);
|
||||||
});
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function setupEvents() {
|
||||||
|
// In recent versions of FF the 'beforeunload' event is not fired when the
|
||||||
|
// window or the tab is closed. It is only fired when we leave the page
|
||||||
|
// (change URL). If this participant doesn't unload properly, then it
|
||||||
|
// becomes a ghost for the rest of the participants that stay in the
|
||||||
|
// conference. Thankfully handling the 'unload' event in addition to the
|
||||||
|
// 'beforeunload' event seems to garante the execution of the 'unload'
|
||||||
|
// method at least once.
|
||||||
|
//
|
||||||
|
// The 'unload' method can safely be run multiple times, it will actually do
|
||||||
|
// something only the first time that it's run, so we're don't have to worry
|
||||||
|
// about browsers that fire both events.
|
||||||
|
|
||||||
|
$(window).bind('beforeunload', unload);
|
||||||
|
$(window).bind('unload', unload);
|
||||||
}
|
}
|
||||||
|
|
||||||
var XMPP = {
|
var XMPP = {
|
||||||
|
@ -17741,6 +17840,12 @@ var XMPP = {
|
||||||
case "devices":
|
case "devices":
|
||||||
connection.emuc.addDevicesToPresence(value);
|
connection.emuc.addDevicesToPresence(value);
|
||||||
break;
|
break;
|
||||||
|
case "startMuted":
|
||||||
|
if(!Moderator.isModerator())
|
||||||
|
return;
|
||||||
|
connection.emuc.addStartMutedToPresence(value[0],
|
||||||
|
value[1]);
|
||||||
|
break;
|
||||||
default :
|
default :
|
||||||
console.log("Unknown tag for presence: " + name);
|
console.log("Unknown tag for presence: " + name);
|
||||||
return;
|
return;
|
||||||
|
@ -28054,6 +28159,8 @@ module.exports = function arrayEquals(array) {
|
||||||
exports.Interop = require('./interop');
|
exports.Interop = require('./interop');
|
||||||
|
|
||||||
},{"./interop":88}],88:[function(require,module,exports){
|
},{"./interop":88}],88:[function(require,module,exports){
|
||||||
|
"use strict";
|
||||||
|
|
||||||
var transform = require('./transform');
|
var transform = require('./transform');
|
||||||
var arrayEquals = require('./array-equals');
|
var arrayEquals = require('./array-equals');
|
||||||
|
|
||||||
|
@ -28122,46 +28229,47 @@ Interop.prototype.toPlanB = function(desc) {
|
||||||
session.media = [];
|
session.media = [];
|
||||||
|
|
||||||
// Associative array that maps channel types to channel objects for fast
|
// Associative array that maps channel types to channel objects for fast
|
||||||
// access to channel objects by their type, e.g. channels['audio']->channel
|
// access to channel objects by their type, e.g. type2bl['audio']->channel
|
||||||
// obj.
|
// obj.
|
||||||
var channels = {};
|
var type2bl = {};
|
||||||
|
|
||||||
// Used to build the group:BUNDLE value after the channels construction
|
// Used to build the group:BUNDLE value after the channels construction
|
||||||
// loop.
|
// loop.
|
||||||
var types = [];
|
var types = [];
|
||||||
|
|
||||||
// Implode the Unified Plan m-lines/tracks into Plan B "channels".
|
// Implode the Unified Plan m-lines/tracks into Plan B channels.
|
||||||
media.forEach(function(mLine) {
|
media.forEach(function(unifiedLine) {
|
||||||
|
|
||||||
// rtcp-mux is required in the Plan B SDP.
|
// rtcp-mux is required in the Plan B SDP.
|
||||||
if (typeof mLine.rtcpMux !== 'string' ||
|
if ((typeof unifiedLine.rtcpMux !== 'string' ||
|
||||||
mLine.rtcpMux !== 'rtcp-mux') {
|
unifiedLine.rtcpMux !== 'rtcp-mux') &&
|
||||||
|
unifiedLine.direction !== 'inactive') {
|
||||||
throw new Error('Cannot convert to Plan B because m-lines ' +
|
throw new Error('Cannot convert to Plan B because m-lines ' +
|
||||||
'without the rtcp-mux attribute were found.');
|
'without the rtcp-mux attribute were found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have a channel for this mLine.type, then use this mLine
|
// If we don't have a channel for this unifiedLine.type, then use this unifiedLine
|
||||||
// as the channel basis.
|
// as the channel basis.
|
||||||
if (typeof channels[mLine.type] === 'undefined') {
|
if (typeof type2bl[unifiedLine.type] === 'undefined') {
|
||||||
channels[mLine.type] = mLine;
|
type2bl[unifiedLine.type] = unifiedLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sources to the channel and handle a=msid.
|
// Add sources to the channel and handle a=msid.
|
||||||
if (typeof mLine.sources === 'object') {
|
if (typeof unifiedLine.sources === 'object') {
|
||||||
Object.keys(mLine.sources).forEach(function(ssrc) {
|
Object.keys(unifiedLine.sources).forEach(function(ssrc) {
|
||||||
if (typeof channels[mLine.type].sources !== 'object')
|
if (typeof type2bl[unifiedLine.type].sources !== 'object')
|
||||||
channels[mLine.type].sources = {};
|
type2bl[unifiedLine.type].sources = {};
|
||||||
|
|
||||||
// Assign the sources to the channel.
|
// Assign the sources to the channel.
|
||||||
channels[mLine.type].sources[ssrc] = mLine.sources[ssrc];
|
type2bl[unifiedLine.type].sources[ssrc] = unifiedLine.sources[ssrc];
|
||||||
|
|
||||||
if (typeof mLine.msid !== 'undefined') {
|
if (typeof unifiedLine.msid !== 'undefined') {
|
||||||
// In Plan B the msid is an SSRC attribute. Also, we don't
|
// In Plan B the msid is an SSRC attribute. Also, we don't
|
||||||
// care about the obsolete label and mslabel attributes.
|
// care about the obsolete label and mslabel attributes.
|
||||||
//
|
//
|
||||||
// Note that it is not guaranteed that the mLine will have
|
// Note that it is not guaranteed that the unifiedLine will have
|
||||||
// an msid. recvonly channels in particular don't have one.
|
// an msid. recvonly channels in particular don't have one.
|
||||||
channels[mLine.type].sources[ssrc].msid = mLine.msid;
|
type2bl[unifiedLine.type].sources[ssrc].msid = unifiedLine.msid;
|
||||||
}
|
}
|
||||||
// NOTE ssrcs in ssrc groups will share msids, as
|
// NOTE ssrcs in ssrc groups will share msids, as
|
||||||
// draft-uberti-rtcweb-plan-00 mandates.
|
// draft-uberti-rtcweb-plan-00 mandates.
|
||||||
|
@ -28169,39 +28277,39 @@ Interop.prototype.toPlanB = function(desc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ssrc groups to the channel.
|
// Add ssrc groups to the channel.
|
||||||
if (typeof mLine.ssrcGroups !== 'undefined' &&
|
if (typeof unifiedLine.ssrcGroups !== 'undefined' &&
|
||||||
Array.isArray(mLine.ssrcGroups)) {
|
Array.isArray(unifiedLine.ssrcGroups)) {
|
||||||
|
|
||||||
// Create the ssrcGroups array, if it's not defined.
|
// Create the ssrcGroups array, if it's not defined.
|
||||||
if (typeof channel.ssrcGroups === 'undefined' ||
|
if (typeof type2bl[unifiedLine.type].ssrcGroups === 'undefined' ||
|
||||||
!Array.isArray(channel.ssrcGroups)) {
|
!Array.isArray(type2bl[unifiedLine.type].ssrcGroups)) {
|
||||||
channel.ssrcGroups = [];
|
type2bl[unifiedLine.type].ssrcGroups = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups);
|
type2bl[unifiedLine.type].ssrcGroups = type2bl[unifiedLine.type].ssrcGroups.concat(unifiedLine.ssrcGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channels[mLine.type] === mLine) {
|
if (type2bl[unifiedLine.type] === unifiedLine) {
|
||||||
// Copy ICE related stuff from the principal media line.
|
// Copy ICE related stuff from the principal media line.
|
||||||
mLine.candidates = media[0].candidates;
|
unifiedLine.candidates = media[0].candidates;
|
||||||
mLine.iceUfrag = media[0].iceUfrag;
|
unifiedLine.iceUfrag = media[0].iceUfrag;
|
||||||
mLine.icePwd = media[0].icePwd;
|
unifiedLine.icePwd = media[0].icePwd;
|
||||||
mLine.fingerprint = media[0].fingerprint;
|
unifiedLine.fingerprint = media[0].fingerprint;
|
||||||
|
|
||||||
// Plan B mids are in ['audio', 'video', 'data']
|
// Plan B mids are in ['audio', 'video', 'data']
|
||||||
mLine.mid = mLine.type;
|
unifiedLine.mid = unifiedLine.type;
|
||||||
|
|
||||||
// Plan B doesn't support/need the bundle-only attribute.
|
// Plan B doesn't support/need the bundle-only attribute.
|
||||||
delete mLine.bundleOnly;
|
delete unifiedLine.bundleOnly;
|
||||||
|
|
||||||
// In Plan B the msid is an SSRC attribute.
|
// In Plan B the msid is an SSRC attribute.
|
||||||
delete mLine.msid;
|
delete unifiedLine.msid;
|
||||||
|
|
||||||
// Used to build the group:BUNDLE value after this loop.
|
// Used to build the group:BUNDLE value after this loop.
|
||||||
types.push(mLine.type);
|
types.push(unifiedLine.type);
|
||||||
|
|
||||||
// Add the channel to the new media array.
|
// Add the channel to the new media array.
|
||||||
session.media.push(mLine);
|
session.media.push(unifiedLine);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28317,34 +28425,35 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
|
|
||||||
// A helper map that sends mids to m-line objects. We use it later to
|
// A helper map that sends mids to m-line objects. We use it later to
|
||||||
// rebuild the Unified Plan style session.media array.
|
// rebuild the Unified Plan style session.media array.
|
||||||
var mid2ml = {};
|
var mid2ul = {};
|
||||||
session.media.forEach(function(channel) {
|
session.media.forEach(function(bLine) {
|
||||||
if (typeof channel.rtcpMux !== 'string' ||
|
if ((typeof bLine.rtcpMux !== 'string' ||
|
||||||
channel.rtcpMux !== 'rtcp-mux') {
|
bLine.rtcpMux !== 'rtcp-mux') &&
|
||||||
|
bLine.direction !== 'inactive') {
|
||||||
throw new Error("Cannot convert to Unified Plan because m-lines " +
|
throw new Error("Cannot convert to Unified Plan because m-lines " +
|
||||||
"without the rtcp-mux attribute were found.");
|
"without the rtcp-mux attribute were found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// With rtcp-mux and bundle all the channels should have the same ICE
|
// With rtcp-mux and bundle all the channels should have the same ICE
|
||||||
// stuff.
|
// stuff.
|
||||||
var sources = channel.sources;
|
var sources = bLine.sources;
|
||||||
var ssrcGroups = channel.ssrcGroups;
|
var ssrcGroups = bLine.ssrcGroups;
|
||||||
var candidates = channel.candidates;
|
var candidates = bLine.candidates;
|
||||||
var iceUfrag = channel.iceUfrag;
|
var iceUfrag = bLine.iceUfrag;
|
||||||
var icePwd = channel.icePwd;
|
var icePwd = bLine.icePwd;
|
||||||
var fingerprint = channel.fingerprint;
|
var fingerprint = bLine.fingerprint;
|
||||||
var port = channel.port;
|
var port = bLine.port;
|
||||||
|
|
||||||
// We'll use the "channel" object as a prototype for each new "mLine"
|
// We'll use the "bLine" object as a prototype for each new "mLine"
|
||||||
// that we create, but first we need to clean it up a bit.
|
// that we create, but first we need to clean it up a bit.
|
||||||
delete channel.sources;
|
delete bLine.sources;
|
||||||
delete channel.ssrcGroups;
|
delete bLine.ssrcGroups;
|
||||||
delete channel.candidates;
|
delete bLine.candidates;
|
||||||
delete channel.iceUfrag;
|
delete bLine.iceUfrag;
|
||||||
delete channel.icePwd;
|
delete bLine.icePwd;
|
||||||
delete channel.fingerprint;
|
delete bLine.fingerprint;
|
||||||
delete channel.port;
|
delete bLine.port;
|
||||||
delete channel.mid;
|
delete bLine.mid;
|
||||||
|
|
||||||
// inverted ssrc group map
|
// inverted ssrc group map
|
||||||
var ssrc2group = {};
|
var ssrc2group = {};
|
||||||
|
@ -28378,11 +28487,11 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
// Explode the Plan B channel sources with one m-line per source.
|
// Explode the Plan B channel sources with one m-line per source.
|
||||||
Object.keys(sources).forEach(function(ssrc) {
|
Object.keys(sources).forEach(function(ssrc) {
|
||||||
|
|
||||||
// The m-line for this SSRC. We either create it from scratch
|
// The (unified) m-line for this SSRC. We either create it from
|
||||||
// or, if it's a grouped SSRC, we re-use a related mline. In
|
// scratch or, if it's a grouped SSRC, we re-use a related
|
||||||
// other words, if the source is grouped with another source,
|
// mline. In other words, if the source is grouped with another
|
||||||
// put the two together in the same m-line.
|
// source, put the two together in the same m-line.
|
||||||
var mLine;
|
var unifiedLine;
|
||||||
if (typeof ssrc2group[ssrc] !== 'undefined' &&
|
if (typeof ssrc2group[ssrc] !== 'undefined' &&
|
||||||
Array.isArray(ssrc2group[ssrc])) {
|
Array.isArray(ssrc2group[ssrc])) {
|
||||||
ssrc2group[ssrc].some(function (ssrcGroup) {
|
ssrc2group[ssrc].some(function (ssrcGroup) {
|
||||||
|
@ -28390,21 +28499,21 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
// again here.
|
// again here.
|
||||||
return ssrcGroup.ssrcs.some(function (related) {
|
return ssrcGroup.ssrcs.some(function (related) {
|
||||||
if (typeof ssrc2ml[related] === 'object') {
|
if (typeof ssrc2ml[related] === 'object') {
|
||||||
mLine = ssrc2ml[related];
|
unifiedLine = ssrc2ml[related];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof mLine === 'object') {
|
if (typeof unifiedLine === 'object') {
|
||||||
// the m-line already exists. Just add the source.
|
// the m-line already exists. Just add the source.
|
||||||
mLine.sources[ssrc] = sources[ssrc];
|
unifiedLine.sources[ssrc] = sources[ssrc];
|
||||||
delete sources[ssrc].msid;
|
delete sources[ssrc].msid;
|
||||||
} else {
|
} else {
|
||||||
// Use the "channel" as a prototype for the "mLine".
|
// Use the "bLine" as a prototype for the "unifiedLine".
|
||||||
mLine = Object.create(channel);
|
unifiedLine = Object.create(bLine);
|
||||||
ssrc2ml[ssrc] = mLine;
|
ssrc2ml[ssrc] = unifiedLine;
|
||||||
|
|
||||||
if (typeof sources[ssrc].msid !== 'undefined') {
|
if (typeof sources[ssrc].msid !== 'undefined') {
|
||||||
// Assign the msid of the source to the m-line. Note
|
// Assign the msid of the source to the m-line. Note
|
||||||
|
@ -28412,14 +28521,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
// msid. In particular "recvonly" sources don't have an
|
// msid. In particular "recvonly" sources don't have an
|
||||||
// msid. Note that "recvonly" is a term only defined
|
// msid. Note that "recvonly" is a term only defined
|
||||||
// for m-lines.
|
// for m-lines.
|
||||||
mLine.msid = sources[ssrc].msid;
|
unifiedLine.msid = sources[ssrc].msid;
|
||||||
delete sources[ssrc].msid;
|
delete sources[ssrc].msid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assign one SSRC per media line.
|
// We assign one SSRC per media line.
|
||||||
mLine.sources = {};
|
unifiedLine.sources = {};
|
||||||
mLine.sources[ssrc] = sources[ssrc];
|
unifiedLine.sources[ssrc] = sources[ssrc];
|
||||||
mLine.ssrcGroups = ssrc2group[ssrc];
|
unifiedLine.ssrcGroups = ssrc2group[ssrc];
|
||||||
|
|
||||||
// Use the cached Unified Plan SDP (if it exists) to assign
|
// Use the cached Unified Plan SDP (if it exists) to assign
|
||||||
// SSRCs to mids.
|
// SSRCs to mids.
|
||||||
|
@ -28431,14 +28540,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
if (typeof m.sources === 'object') {
|
if (typeof m.sources === 'object') {
|
||||||
Object.keys(m.sources).forEach(function (s) {
|
Object.keys(m.sources).forEach(function (s) {
|
||||||
if (s === ssrc) {
|
if (s === ssrc) {
|
||||||
mLine.mid = m.mid;
|
unifiedLine.mid = m.mid;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof mLine.mid === 'undefined') {
|
if (typeof unifiedLine.mid === 'undefined') {
|
||||||
|
|
||||||
// If this is an SSRC that we see for the first time
|
// If this is an SSRC that we see for the first time
|
||||||
// assign it a new mid. This is typically the case when
|
// assign it a new mid. This is typically the case when
|
||||||
|
@ -28451,23 +28560,23 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
//
|
//
|
||||||
// Because FF generates answers in Unified Plan style,
|
// Because FF generates answers in Unified Plan style,
|
||||||
// we MUST already have a cached answer with all the
|
// we MUST already have a cached answer with all the
|
||||||
// local SSRCs mapped to some mLine/mid.
|
// local SSRCs mapped to some m-line/mid.
|
||||||
|
|
||||||
if (desc.type === 'answer') {
|
if (desc.type === 'answer') {
|
||||||
throw new Error("An unmapped SSRC was found.");
|
throw new Error("An unmapped SSRC was found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
mLine.mid = [channel.type, '-', ssrc].join('');
|
unifiedLine.mid = [bLine.type, '-', ssrc].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include the candidates in the 1st media line.
|
// Include the candidates in the 1st media line.
|
||||||
mLine.candidates = candidates;
|
unifiedLine.candidates = candidates;
|
||||||
mLine.iceUfrag = iceUfrag;
|
unifiedLine.iceUfrag = iceUfrag;
|
||||||
mLine.icePwd = icePwd;
|
unifiedLine.icePwd = icePwd;
|
||||||
mLine.fingerprint = fingerprint;
|
unifiedLine.fingerprint = fingerprint;
|
||||||
mLine.port = port;
|
unifiedLine.port = port;
|
||||||
|
|
||||||
mid2ml[mLine.mid] = mLine;
|
mid2ul[unifiedLine.mid] = unifiedLine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -28481,71 +28590,57 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
if (desc.type === 'answer') {
|
if (desc.type === 'answer') {
|
||||||
|
|
||||||
// The media lines in the answer must match the media lines in the
|
// The media lines in the answer must match the media lines in the
|
||||||
// offer. The order is important too. Here we use the cached offer to
|
// offer. The order is important too. Here we assume that Firefox is the
|
||||||
// find the m-lines that are missing (from the converted answer), and
|
// answerer, so we merely have to use the reconstructed (unified) answer
|
||||||
// use the cached answer to complete the converted answer.
|
// to update the cached (unified) answer accordingly.
|
||||||
|
//
|
||||||
|
// In the general case, one would have to use the cached (unified) offer
|
||||||
|
// to find the m-lines that are missing from the reconstructed answer,
|
||||||
|
// potentially grabbing them from the cached (unified) answer. One has
|
||||||
|
// to be carefull with this approach because inactive m-lines do not
|
||||||
|
// always have an mid, making it tricky (impossible?) to find where
|
||||||
|
// exactly and which m-lines are missing from the reconstructed answer.
|
||||||
|
|
||||||
if (typeof this.cache['offer'] === 'undefined') {
|
for (var i = 0; i < cached.media.length; i++) {
|
||||||
throw new Error("An answer is being processed but we couldn't " +
|
var unifiedLine = cached.media[i];
|
||||||
"find a cached offer.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var cachedOffer = transform.parse(this.cache['offer']);
|
if (typeof mid2ul[unifiedLine.mid] === 'undefined') {
|
||||||
|
|
||||||
if (typeof cachedOffer === 'undefined' ||
|
// The mid isn't in the reconstructed (unified) answer.
|
||||||
typeof cachedOffer.media === 'undefined' ||
|
// This is either a (unified) m-line containing a remote
|
||||||
!Array.isArray(cachedOffer.media)) {
|
// track only, or a (unified) m-line containing a remote
|
||||||
// FIXME(gp) is this really a problem in the general case?
|
// track and a local track that has been removed.
|
||||||
throw new Error("The cached offer has no media.");
|
// In either case, it MUST exist in the cached
|
||||||
}
|
// (unified) answer.
|
||||||
|
//
|
||||||
|
// In case this is a removed local track, clean-up
|
||||||
|
// the (unified) m-line and make sure it's 'recvonly' or
|
||||||
|
// 'inactive'.
|
||||||
|
|
||||||
cachedOffer.media.forEach(function(mo) {
|
delete unifiedLine.msid;
|
||||||
|
delete unifiedLine.sources;
|
||||||
var mLine;
|
delete unifiedLine.ssrcGroups;
|
||||||
cached.media.some(function(ma) {
|
if (!unifiedLine.direction
|
||||||
if (mo.mid == ma.mid) {
|
|| unifiedLine.direction === 'sendrecv')
|
||||||
if (typeof mid2ml[mo.mid] === 'undefined') {
|
unifiedLine.direction = 'recvonly';
|
||||||
|
if (!unifiedLine.direction
|
||||||
// This is either an m-line containing a remote
|
|| unifiedLine.direction === 'sendonly')
|
||||||
// track only, or an m-line containing a remote
|
unifiedLine.direction = 'inactive';
|
||||||
// track and a local track that has been removed.
|
} else {
|
||||||
// In either case, it MUST exist in the cached
|
// This is an (unified) m-line/channel that contains a local
|
||||||
// answer.
|
// track (sendrecv or sendonly channel) or it's a unified
|
||||||
//
|
// recvonly m-line/channel. In either case, since we're
|
||||||
// In case this is a removed local track, clean-up
|
// going from PlanB -> Unified Plan this m-line MUST
|
||||||
// the m-line and make sure it's 'recvonly'.
|
// exist in the cached answer.
|
||||||
|
|
||||||
// TODO sendonly -> inactive makes more sense.
|
|
||||||
delete ma.msid;
|
|
||||||
delete ma.sources;
|
|
||||||
delete ma.ssrcGroups;
|
|
||||||
if (!ma.direction
|
|
||||||
|| ma.direction === 'sendonly'
|
|
||||||
|| ma.direction === 'sendrecv')
|
|
||||||
ma.direction = 'recvonly';
|
|
||||||
} else {
|
|
||||||
// This is an m-line/channel that contains a local
|
|
||||||
// track (sendrecv or sendonly channel) or it's a
|
|
||||||
// recvonly m-line/channel. In either case, since we're
|
|
||||||
// going from PlanB -> Unified Plan this m-line MUST
|
|
||||||
// exist in the cached answer.
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign the found object.
|
|
||||||
mLine = ma;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (typeof mLine === 'undefined') {
|
|
||||||
throw new Error("The cached offer contains an m-line that " +
|
|
||||||
"doesn't exist neither in the cached answer nor in " +
|
|
||||||
"the converted answer.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
session.media.push(mLine);
|
session.media.push(unifiedLine);
|
||||||
mids.push(mLine.mid);
|
|
||||||
});
|
if (typeof unifiedLine.mid === 'string') {
|
||||||
|
// inactive lines don't/may not have an mid.
|
||||||
|
mids.push(unifiedLine.mid);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// SDP offer/answer (and the JSEP spec) forbids removing an m-section
|
// SDP offer/answer (and the JSEP spec) forbids removing an m-section
|
||||||
|
@ -28559,41 +28654,48 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
||||||
if (typeof cached !== 'undefined' &&
|
if (typeof cached !== 'undefined' &&
|
||||||
typeof cached.media !== 'undefined' &&
|
typeof cached.media !== 'undefined' &&
|
||||||
Array.isArray(cached.media)) {
|
Array.isArray(cached.media)) {
|
||||||
cached.media.forEach(function(pm) {
|
cached.media.forEach(function(unifiedLine) {
|
||||||
mids.push(pm.mid);
|
mids.push(unifiedLine.mid);
|
||||||
if (typeof mid2ml[pm.mid] !== 'undefined') {
|
if (typeof mid2ul[unifiedLine.mid] !== 'undefined') {
|
||||||
session.media.push(mid2ml[pm.mid]);
|
session.media.push(mid2ul[unifiedLine.mid]);
|
||||||
} else {
|
} else {
|
||||||
delete pm.msid;
|
delete unifiedLine.msid;
|
||||||
delete pm.sources;
|
delete unifiedLine.sources;
|
||||||
delete pm.ssrcGroups;
|
delete unifiedLine.ssrcGroups;
|
||||||
pm.direction = 'recvonly';
|
if (!unifiedLine.direction
|
||||||
session.media.push(pm);
|
|| unifiedLine.direction === 'sendrecv')
|
||||||
|
unifiedLine.direction = 'recvonly';
|
||||||
|
if (!unifiedLine.direction
|
||||||
|
|| unifiedLine.direction === 'sendonly')
|
||||||
|
unifiedLine.direction = 'inactive';
|
||||||
|
session.media.push(unifiedLine);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all the remaining (new) m-lines of the transformed SDP.
|
// Add all the remaining (new) m-lines of the transformed SDP.
|
||||||
Object.keys(mid2ml).forEach(function(mid) {
|
Object.keys(mid2ul).forEach(function(mid) {
|
||||||
if (mids.indexOf(mid) === -1) {
|
if (mids.indexOf(mid) === -1) {
|
||||||
mids.push(mid);
|
mids.push(mid);
|
||||||
if (typeof mid2ml[mid].msid === 'undefined') {
|
if (typeof mid2ul[mid].direction === 'recvonly') {
|
||||||
// This is a remote recvonly channel. Add its SSRC to the
|
// This is a remote recvonly channel. Add its SSRC to the
|
||||||
// sendrecv channel.
|
// appropriate sendrecv or sendonly channel.
|
||||||
// TODO(gp) what if there is no sendrecv channel?
|
// TODO(gp) what if we don't have sendrecv/sendonly channel?
|
||||||
session.media.some(function (ml) {
|
session.media.some(function (unifiedLine) {
|
||||||
if (ml.direction === 'sendrecv' && ml.type == mid2ml[mid].type) {
|
if ((unifiedLine.direction === 'sendrecv' ||
|
||||||
|
unifiedLine.direction === 'sendonly') &&
|
||||||
|
unifiedLine.type === mid2ul[mid].type) {
|
||||||
|
|
||||||
// this shouldn't have any ssrc-groups
|
// mid2ul[mid] shouldn't have any ssrc-groups
|
||||||
Object.keys(mid2ml[mid].sources).forEach(function (ssrc) {
|
Object.keys(mid2ul[mid].sources).forEach(function (ssrc) {
|
||||||
ml.sources[ssrc] = mid2ml[mid].sources[ssrc];
|
unifiedLine.sources[ssrc] = mid2ul[mid].sources[ssrc];
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
session.media.push(mid2ml[mid]);
|
session.media.push(mid2ul[mid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -29388,7 +29490,8 @@ var XMPPEvents = {
|
||||||
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
||||||
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
||||||
ETHERPAD: "xmpp.etherpad",
|
ETHERPAD: "xmpp.etherpad",
|
||||||
DEVICE_AVAILABLE: "xmpp.device_available"
|
DEVICE_AVAILABLE: "xmpp.device_available",
|
||||||
|
START_MUTED: "xmpp.start_muted"
|
||||||
};
|
};
|
||||||
module.exports = XMPPEvents;
|
module.exports = XMPPEvents;
|
||||||
},{}],106:[function(require,module,exports){
|
},{}],106:[function(require,module,exports){
|
||||||
|
|
Loading…
Reference in New Issue