diff --git a/JitsiConference.js b/JitsiConference.js index 57568cd6d..8ee9eb5f3 100644 --- a/JitsiConference.js +++ b/JitsiConference.js @@ -5,6 +5,7 @@ var RTC = require("./modules/RTC/RTC"); var XMPPEvents = require("./service/xmpp/XMPPEvents"); var AuthenticationEvents = require("./service/authentication/AuthenticationEvents"); var RTCEvents = require("./service/RTC/RTCEvents"); +var DSEvents = require("./service/desktopsharing/DesktopSharingEventTypes"); var EventEmitter = require("events"); var JitsiConferenceEvents = require("./JitsiConferenceEvents"); var JitsiConferenceErrors = require("./JitsiConferenceErrors"); @@ -57,6 +58,10 @@ function JitsiConference(options) { this.startAudioMuted = false; this.startVideoMuted = false; this.startMutedPolicy = {audio: false, video: false}; + this.availableDevices = { + audio: undefined, + video: undefined + }; } /** @@ -801,6 +806,24 @@ function setupListeners(conference) { conference.room.addListener(XMPPEvents.BRIDGE_DOWN, function () { conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE); }); + conference.room.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.RESERVATION_ERROR, code, msg); + }); + conference.room.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.GRACEFUL_SHUTDOWN); + }); + conference.room.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function () { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.JINGLE_FATAL_ERROR); + }); + conference.room.addListener(XMPPEvents.MUC_DESTROYED, function (reason) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONFERENCE_DESTROYED, reason); + }); + conference.room.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, function (err, msg) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CHAT_ERROR, err, msg); + }); + conference.room.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focus, retrySec) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.FOCUS_DISCONNECTED, focus, retrySec); + }); // FIXME // conference.room.addListener(XMPPEvents.MUC_JOINED, function () { // conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT); @@ -852,6 +875,20 @@ function setupListeners(conference) { conference.eventEmitter.emit(JitsiConferenceEvents.MESSAGE_RECEIVED, id, txt, ts); }); + conference.room.addListener(XMPPEvents.PRESENCE_STATUS, function (jid, status) { + var id = Strophe.getResourceFromJid(jid); + var participant = conference.getParticipantById(id); + if (!participant || participant._status === status) { + return; + } + participant._status = status; + conference.eventEmitter.emit(JitsiConferenceEvents.USER_STATUS_CHANGED, id, status); + }); + + conference.rtc.addListener(DSEvents.FIREFOX_EXTENSION_NEEDED, function (url) { + conference.eventEmitter.emit(JitsiConferenceEvents.FIREFOX_EXTENSION_NEEDED, url); + }); + conference.rtc.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (id) { if(conference.lastDominantSpeaker !== id && conference.room) { conference.lastDominantSpeaker = id; @@ -929,6 +966,50 @@ function setupListeners(conference) { } }); + conference.rtc.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { + conference.room.updateDeviceAvailability(devices); + }); + conference.room.addPresenceListener("devices", function (data, from) { + var isAudioAvailable = false; + var isVideoAvailable = false; + data.children.forEach(function (config) { + if (config.tagName === 'audio') { + isAudioAvailable = config.value === 'true'; + } + if (config.tagName === 'video') { + isVideoAvailable = config.value === 'true'; + } + }); + + var availableDevices; + if (conference.myUserId() === from) { + availableDevices = conference.availableDevices; + } else { + var participant = conference.getParticipantById(from); + if (!participant) { + return; + } + + availableDevices = participant._availableDevices; + } + + var updated = false; + + if (availableDevices.audio !== isAudioAvailable) { + updated = true; + availableDevices.audio = isAudioAvailable; + } + + if (availableDevices.video !== isVideoAvailable) { + updated = true; + availableDevices.video = isVideoAvailable; + } + + if (updated) { + conference.eventEmitter.emit(JitsiConferenceEvents.AVAILABLE_DEVICES_CHANGED, from, availableDevices); + } + }); + if(conference.statistics) { //FIXME: Maybe remove event should not be associated with the conference. conference.statistics.addAudioLevelListener(function (ssrc, level) { @@ -943,10 +1024,6 @@ function setupListeners(conference) { function () { conference.statistics.dispose(); }); - // FIXME: Maybe we should move this. - // RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { - // conference.room.updateDeviceAvailability(devices); - // }); conference.room.addListener(XMPPEvents.PEERCONNECTION_READY, function (session) { diff --git a/JitsiConferenceErrors.js b/JitsiConferenceErrors.js index 32fa35e3d..3407f04f8 100644 --- a/JitsiConferenceErrors.js +++ b/JitsiConferenceErrors.js @@ -27,7 +27,31 @@ var JitsiConferenceErrors = { /** * Indicates that there is no available videobridge. */ - VIDEOBRIDGE_NOT_AVAILABLE: "conference.videobridgeNotAvailable" + VIDEOBRIDGE_NOT_AVAILABLE: "conference.videobridgeNotAvailable", + /** + * Indicates that reservation system returned error. + */ + RESERVATION_ERROR: "conference.reservationError", + /** + * Indicates that graceful shutdown happened. + */ + GRACEFUL_SHUTDOWN: "conference.gracefulShutdown", + /** + * Indicates that jingle fatal error happened. + */ + JINGLE_FATAL_ERROR: "conference.jingleFatalError", + /** + * Indicates that conference has been destroyed. + */ + CONFERENCE_DESTROYED: "conference.destroyed", + /** + * Indicates that chat error occured. + */ + CHAT_ERROR: "conference.chatError", + /** + * Indicates that focus error happened. + */ + FOCUS_DISCONNECTED: "conference.focusDisconnected" /** * Many more errors TBD here. */ diff --git a/JitsiConferenceEvents.js b/JitsiConferenceEvents.js index b80d3f0dd..6f4243154 100644 --- a/JitsiConferenceEvents.js +++ b/JitsiConferenceEvents.js @@ -27,6 +27,10 @@ var JitsiConferenceEvents = { * User role changed. */ USER_ROLE_CHANGED: "conference.roleChanged", + /** + * User status changed. + */ + USER_STATUS_CHANGED: "conference.statusChanged", /** * New text message was received. */ @@ -103,7 +107,16 @@ var JitsiConferenceEvents = { /** * Indicates that phone number changed. */ - PHONE_NUMBER_CHANGED: "conference.phoneNumberChanged" + PHONE_NUMBER_CHANGED: "conference.phoneNumberChanged", + /** + * Indicates that to proceed with screen sharing + * browser extension must be installed first. + */ + FIREFOX_EXTENSION_NEEDED: "conference.firefoxExtensionRequired", + /** + * Indicates that available devices changed. + */ + AVAILABLE_DEVICES_CHANGED: "conference.availableDevicesChanged" }; module.exports = JitsiConferenceEvents; diff --git a/JitsiParticipant.js b/JitsiParticipant.js index 7be1958d7..e6a98fee2 100644 --- a/JitsiParticipant.js +++ b/JitsiParticipant.js @@ -11,6 +11,11 @@ function JitsiParticipant(jid, conference, displayName){ this._supportsDTMF = false; this._tracks = []; this._role = 'none'; + this._status = null; + this._availableDevices = { + audio: undefined, + video: undefined + }; } /** @@ -48,6 +53,13 @@ JitsiParticipant.prototype.getDisplayName = function() { return this._displayName; }; +/** + * @returns {String} The status of the participant. + */ +JitsiParticipant.prototype.getStatus = function () { + return this._status; +}; + /** * @returns {Boolean} Whether this participant is a moderator or not. */ diff --git a/doc/API.md b/doc/API.md index 9aa8e5d43..6de033e86 100644 --- a/doc/API.md +++ b/doc/API.md @@ -89,10 +89,13 @@ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR); - CONFERENCE_LEFT - notifies the local user that he left the conference successfully. (no parameters) - DTMF_SUPPORT_CHANGED - notifies if at least one user supports DTMF. (parameters - supports(boolean)) - USER_ROLE_CHANGED - notifies that role of some user changed. (parameters - id(string), role(string)) + - USER_STATUS_CHANGED - notifies that status of some user changed. (parameters - id(string), status(string)) - CONFERENCE_FAILED - notifies that user failed to join the conference. (parameters - errorCode(JitsiMeetJS.errors.conference)) - KICKED - notifies that user has been kicked from the conference. - START_MUTED_POLICY_CHANGED - notifies that all new participants will join with muted audio/video stream (parameters - JS object with 2 properties - audio(boolean), video(boolean)) - STARTED_MUTED - notifies that the local user has started muted + - FIREFOX_EXTENSION_NEEDED - notifies that browser extension must be installed to proceed with screen sharing (parameters - extension url(string)) + - AVAILABLE_DEVICES_CHANGED - notifies that available participant devices changed (camera or microphone was added or removed) (parameters - id(string), devices(JS object with 2 properties - audio(boolean), video(boolean))) 2. connection - CONNECTION_FAILED - indicates that the server connection failed. @@ -111,6 +114,12 @@ JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.ERROR); - PASSWORD_REQUIRED - that error can be passed when the connection to the conference failed. You should try to join the conference with password. - PASSWORD_NOT_SUPPORTED - indicates that conference cannot be locked - VIDEOBRIDGE_NOT_AVAILABLE - video bridge issues. + - RESERVATION_ERROR - error in reservation system + - GRACEFUL_SHUTDOWN - graceful shutdown + - JINGLE_FATAL_ERROR - error in jingle + - CONFERENCE_DESTROYED - conference has been destroyed + - CHAT_ERROR - chat error happened + - FOCUS_DISCONNECTED - focus error happened 2. connection - PASSWORD_REQUIRED - passed when the connection to the server failed. You should try to authenticate with password. - CONNECTION_ERROR - indicates connection failures. diff --git a/lib-jitsi-meet.js b/lib-jitsi-meet.js index 3d5459695..7fc6850f4 100644 --- a/lib-jitsi-meet.js +++ b/lib-jitsi-meet.js @@ -7,6 +7,7 @@ var RTC = require("./modules/RTC/RTC"); var XMPPEvents = require("./service/xmpp/XMPPEvents"); var AuthenticationEvents = require("./service/authentication/AuthenticationEvents"); var RTCEvents = require("./service/RTC/RTCEvents"); +var DSEvents = require("./service/desktopsharing/DesktopSharingEventTypes"); var EventEmitter = require("events"); var JitsiConferenceEvents = require("./JitsiConferenceEvents"); var JitsiConferenceErrors = require("./JitsiConferenceErrors"); @@ -59,6 +60,10 @@ function JitsiConference(options) { this.startAudioMuted = false; this.startVideoMuted = false; this.startMutedPolicy = {audio: false, video: false}; + this.availableDevices = { + audio: undefined, + video: undefined + }; } /** @@ -803,6 +808,24 @@ function setupListeners(conference) { conference.room.addListener(XMPPEvents.BRIDGE_DOWN, function () { conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE); }); + conference.room.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.RESERVATION_ERROR, code, msg); + }); + conference.room.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.GRACEFUL_SHUTDOWN); + }); + conference.room.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function () { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.JINGLE_FATAL_ERROR); + }); + conference.room.addListener(XMPPEvents.MUC_DESTROYED, function (reason) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONFERENCE_DESTROYED, reason); + }); + conference.room.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, function (err, msg) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CHAT_ERROR, err, msg); + }); + conference.room.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focus, retrySec) { + conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.FOCUS_DISCONNECTED, focus, retrySec); + }); // FIXME // conference.room.addListener(XMPPEvents.MUC_JOINED, function () { // conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT); @@ -854,6 +877,20 @@ function setupListeners(conference) { conference.eventEmitter.emit(JitsiConferenceEvents.MESSAGE_RECEIVED, id, txt, ts); }); + conference.room.addListener(XMPPEvents.PRESENCE_STATUS, function (jid, status) { + var id = Strophe.getResourceFromJid(jid); + var participant = conference.getParticipantById(id); + if (!participant || participant._status === status) { + return; + } + participant._status = status; + conference.eventEmitter.emit(JitsiConferenceEvents.USER_STATUS_CHANGED, id, status); + }); + + conference.rtc.addListener(DSEvents.FIREFOX_EXTENSION_NEEDED, function (url) { + conference.eventEmitter.emit(JitsiConferenceEvents.FIREFOX_EXTENSION_NEEDED, url); + }); + conference.rtc.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (id) { if(conference.lastDominantSpeaker !== id && conference.room) { conference.lastDominantSpeaker = id; @@ -931,6 +968,50 @@ function setupListeners(conference) { } }); + conference.rtc.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { + conference.room.updateDeviceAvailability(devices); + }); + conference.room.addPresenceListener("devices", function (data, from) { + var isAudioAvailable = false; + var isVideoAvailable = false; + data.children.forEach(function (config) { + if (config.tagName === 'audio') { + isAudioAvailable = config.value === 'true'; + } + if (config.tagName === 'video') { + isVideoAvailable = config.value === 'true'; + } + }); + + var availableDevices; + if (conference.myUserId() === from) { + availableDevices = conference.availableDevices; + } else { + var participant = conference.getParticipantById(from); + if (!participant) { + return; + } + + availableDevices = participant._availableDevices; + } + + var updated = false; + + if (availableDevices.audio !== isAudioAvailable) { + updated = true; + availableDevices.audio = isAudioAvailable; + } + + if (availableDevices.video !== isVideoAvailable) { + updated = true; + availableDevices.video = isVideoAvailable; + } + + if (updated) { + conference.eventEmitter.emit(JitsiConferenceEvents.AVAILABLE_DEVICES_CHANGED, from, availableDevices); + } + }); + if(conference.statistics) { //FIXME: Maybe remove event should not be associated with the conference. conference.statistics.addAudioLevelListener(function (ssrc, level) { @@ -945,10 +1026,6 @@ function setupListeners(conference) { function () { conference.statistics.dispose(); }); - // FIXME: Maybe we should move this. - // RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { - // conference.room.updateDeviceAvailability(devices); - // }); conference.room.addListener(XMPPEvents.PEERCONNECTION_READY, function (session) { @@ -1001,7 +1078,7 @@ function setupListeners(conference) { module.exports = JitsiConference; }).call(this,"/JitsiConference.js") -},{"./JitsiConferenceErrors":2,"./JitsiConferenceEvents":3,"./JitsiParticipant":8,"./JitsiTrackEvents":10,"./modules/DTMF/JitsiDTMFManager":11,"./modules/RTC/RTC":16,"./modules/settings/Settings":21,"./modules/statistics/statistics":25,"./service/RTC/RTCEvents":131,"./service/authentication/AuthenticationEvents":133,"./service/xmpp/XMPPEvents":137,"events":51,"jitsi-meet-logger":79}],2:[function(require,module,exports){ +},{"./JitsiConferenceErrors":2,"./JitsiConferenceEvents":3,"./JitsiParticipant":8,"./JitsiTrackEvents":10,"./modules/DTMF/JitsiDTMFManager":11,"./modules/RTC/RTC":16,"./modules/settings/Settings":21,"./modules/statistics/statistics":25,"./service/RTC/RTCEvents":133,"./service/authentication/AuthenticationEvents":135,"./service/desktopsharing/DesktopSharingEventTypes":136,"./service/xmpp/XMPPEvents":139,"events":46,"jitsi-meet-logger":50}],2:[function(require,module,exports){ /** * Enumeration with the errors for the conference. * @type {{string: string}} @@ -1031,7 +1108,31 @@ var JitsiConferenceErrors = { /** * Indicates that there is no available videobridge. */ - VIDEOBRIDGE_NOT_AVAILABLE: "conference.videobridgeNotAvailable" + VIDEOBRIDGE_NOT_AVAILABLE: "conference.videobridgeNotAvailable", + /** + * Indicates that reservation system returned error. + */ + RESERVATION_ERROR: "conference.reservationError", + /** + * Indicates that graceful shutdown happened. + */ + GRACEFUL_SHUTDOWN: "conference.gracefulShutdown", + /** + * Indicates that jingle fatal error happened. + */ + JINGLE_FATAL_ERROR: "conference.jingleFatalError", + /** + * Indicates that conference has been destroyed. + */ + CONFERENCE_DESTROYED: "conference.destroyed", + /** + * Indicates that chat error occured. + */ + CHAT_ERROR: "conference.chatError", + /** + * Indicates that focus error happened. + */ + FOCUS_DISCONNECTED: "conference.focusDisconnected" /** * Many more errors TBD here. */ @@ -1069,6 +1170,10 @@ var JitsiConferenceEvents = { * User role changed. */ USER_ROLE_CHANGED: "conference.roleChanged", + /** + * User status changed. + */ + USER_STATUS_CHANGED: "conference.statusChanged", /** * New text message was received. */ @@ -1145,7 +1250,16 @@ var JitsiConferenceEvents = { /** * Indicates that phone number changed. */ - PHONE_NUMBER_CHANGED: "conference.phoneNumberChanged" + PHONE_NUMBER_CHANGED: "conference.phoneNumberChanged", + /** + * Indicates that to proceed with screen sharing + * browser extension must be installed first. + */ + FIREFOX_EXTENSION_NEEDED: "conference.firefoxExtensionRequired", + /** + * Indicates that available devices changed. + */ + AVAILABLE_DEVICES_CHANGED: "conference.availableDevicesChanged" }; module.exports = JitsiConferenceEvents; @@ -1428,7 +1542,7 @@ window.Promise = window.Promise || require("es6-promise").Promise; module.exports = LibJitsiMeet; -},{"./JitsiConferenceErrors":2,"./JitsiConferenceEvents":3,"./JitsiConnection":4,"./JitsiConnectionErrors":5,"./JitsiConnectionEvents":6,"./JitsiTrackErrors":9,"./JitsiTrackEvents":10,"./modules/RTC/RTC":16,"./modules/statistics/statistics":25,"./modules/util/ScriptUtil":27,"./service/RTC/Resolutions":132,"es6-promise":72,"jitsi-meet-logger":79}],8:[function(require,module,exports){ +},{"./JitsiConferenceErrors":2,"./JitsiConferenceEvents":3,"./JitsiConnection":4,"./JitsiConnectionErrors":5,"./JitsiConnectionEvents":6,"./JitsiTrackErrors":9,"./JitsiTrackEvents":10,"./modules/RTC/RTC":16,"./modules/statistics/statistics":25,"./modules/util/ScriptUtil":27,"./service/RTC/Resolutions":134,"es6-promise":48,"jitsi-meet-logger":50}],8:[function(require,module,exports){ /* global Strophe */ /** @@ -1442,6 +1556,11 @@ function JitsiParticipant(jid, conference, displayName){ this._supportsDTMF = false; this._tracks = []; this._role = 'none'; + this._status = null; + this._availableDevices = { + audio: undefined, + video: undefined + }; } /** @@ -1479,6 +1598,13 @@ JitsiParticipant.prototype.getDisplayName = function() { return this._displayName; }; +/** + * @returns {String} The status of the participant. + */ +JitsiParticipant.prototype.getStatus = function () { + return this._status; +}; + /** * @returns {Boolean} Whether this participant is a moderator or not. */ @@ -1652,7 +1778,7 @@ JitsiDTMFManager.prototype.sendTones = function (tones, duration, pause) { }; }).call(this,"/modules/DTMF/JitsiDTMFManager.js") -},{"jitsi-meet-logger":79}],12:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],12:[function(require,module,exports){ (function (__filename){ /* global config, APP, Strophe */ @@ -1874,9 +2000,8 @@ DataChannels.prototype._some = function (callback, thisArg) { module.exports = DataChannels; - }).call(this,"/modules/RTC/DataChannels.js") -},{"../../service/RTC/RTCEvents":131,"jitsi-meet-logger":79}],13:[function(require,module,exports){ +},{"../../service/RTC/RTCEvents":133,"jitsi-meet-logger":50}],13:[function(require,module,exports){ var JitsiTrack = require("./JitsiTrack"); var RTCBrowserType = require("./RTCBrowserType"); var JitsiTrackEvents = require('../../JitsiTrackEvents'); @@ -2401,7 +2526,7 @@ JitsiTrack.prototype.setAudioLevel = function (audioLevel) { module.exports = JitsiTrack; -},{"../../JitsiTrackEvents":10,"./RTCBrowserType":17,"./RTCUtils":18,"events":51}],16:[function(require,module,exports){ +},{"../../JitsiTrackEvents":10,"./RTCBrowserType":17,"./RTCUtils":18,"events":46}],16:[function(require,module,exports){ /* global APP */ var EventEmitter = require("events"); var RTCBrowserType = require("./RTCBrowserType"); @@ -2410,8 +2535,6 @@ var JitsiTrack = require("./JitsiTrack"); var JitsiLocalTrack = require("./JitsiLocalTrack.js"); var DataChannels = require("./DataChannels"); var JitsiRemoteTrack = require("./JitsiRemoteTrack.js"); -var DesktopSharingEventTypes - = require("../../service/desktopsharing/DesktopSharingEventTypes"); var MediaStreamType = require("../../service/RTC/MediaStreamTypes"); var RTCEvents = require("../../service/RTC/RTCEvents.js"); @@ -2670,7 +2793,7 @@ RTC.prototype.setAudioLevel = function (jid, audioLevel) { } module.exports = RTC; -},{"../../service/RTC/MediaStreamTypes":130,"../../service/RTC/RTCEvents.js":131,"../../service/desktopsharing/DesktopSharingEventTypes":134,"./DataChannels":12,"./JitsiLocalTrack.js":13,"./JitsiRemoteTrack.js":14,"./JitsiTrack":15,"./RTCBrowserType":17,"./RTCUtils.js":18,"events":51}],17:[function(require,module,exports){ +},{"../../service/RTC/MediaStreamTypes":132,"../../service/RTC/RTCEvents.js":133,"./DataChannels":12,"./JitsiLocalTrack.js":13,"./JitsiRemoteTrack.js":14,"./JitsiTrack":15,"./RTCBrowserType":17,"./RTCUtils.js":18,"events":46}],17:[function(require,module,exports){ var currentBrowser; @@ -3663,7 +3786,7 @@ var RTCUtils = { module.exports = RTCUtils; }).call(this,"/modules/RTC/RTCUtils.js") -},{"../../JitsiTrackErrors":9,"../../service/RTC/RTCEvents":131,"../../service/RTC/Resolutions":132,"../xmpp/SDPUtil":34,"./RTCBrowserType":17,"./ScreenObtainer":19,"./adapter.screenshare":20,"events":51,"jitsi-meet-logger":79}],19:[function(require,module,exports){ +},{"../../JitsiTrackErrors":9,"../../service/RTC/RTCEvents":133,"../../service/RTC/Resolutions":134,"../xmpp/SDPUtil":34,"./RTCBrowserType":17,"./ScreenObtainer":19,"./adapter.screenshare":20,"events":46,"jitsi-meet-logger":50}],19:[function(require,module,exports){ (function (__filename){ /* global chrome, $, alert */ /* jshint -W003 */ @@ -4079,7 +4202,7 @@ function initFirefoxExtensionDetection(options) { module.exports = ScreenObtainer; }).call(this,"/modules/RTC/ScreenObtainer.js") -},{"../../JitsiTrackErrors":9,"../../service/desktopsharing/DesktopSharingEventTypes":134,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":79}],20:[function(require,module,exports){ +},{"../../JitsiTrackErrors":9,"../../service/desktopsharing/DesktopSharingEventTypes":136,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":50}],20:[function(require,module,exports){ (function (__filename){ /*! adapterjs - v0.12.3 - 2015-11-16 */ var console = require("jitsi-meet-logger").getLogger(__filename); @@ -5263,7 +5386,7 @@ if (navigator.mozGetUserMedia) { } }).call(this,"/modules/RTC/adapter.screenshare.js") -},{"jitsi-meet-logger":79}],21:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],21:[function(require,module,exports){ (function (__filename){ var logger = require("jitsi-meet-logger").getLogger(__filename); @@ -5352,7 +5475,7 @@ Settings.prototype.getCallStatsUserName = function () { module.exports = Settings; }).call(this,"/modules/settings/Settings.js") -},{"../util/UsernameGenerator":28,"jitsi-meet-logger":79}],22:[function(require,module,exports){ +},{"../util/UsernameGenerator":28,"jitsi-meet-logger":50}],22:[function(require,module,exports){ (function (__filename){ /* global $, Strophe, callstats */ var logger = require("jitsi-meet-logger").getLogger(__filename); @@ -5613,7 +5736,7 @@ CallStats.sendAddIceCandidateFailed = _try_catch(function (e, pc, cs) { module.exports = CallStats; }).call(this,"/modules/statistics/CallStats.js") -},{"jitsi-meet-logger":79,"jssha":81,"socket.io-client":118}],23:[function(require,module,exports){ +},{"jitsi-meet-logger":50,"jssha":51,"socket.io-client":82}],23:[function(require,module,exports){ /* global config */ /** * Provides statistics for the local stream. @@ -6470,7 +6593,7 @@ StatsCollector.prototype.processAudioLevelReport = function () { }; }).call(this,"/modules/statistics/RTPStatsCollector.js") -},{"../../service/statistics/Events":135,"../RTC/RTCBrowserType":17,"jitsi-meet-logger":79}],25:[function(require,module,exports){ +},{"../../service/statistics/Events":137,"../RTC/RTCBrowserType":17,"jitsi-meet-logger":50}],25:[function(require,module,exports){ /* global require, APP */ var LocalStats = require("./LocalStatsCollector.js"); var RTPStats = require("./RTPStatsCollector.js"); @@ -6750,7 +6873,7 @@ Statistics.LOCAL_JID = require("../../service/statistics/constants").LOCAL_JID; module.exports = Statistics; -},{"../../service/statistics/Events":135,"../../service/statistics/constants":136,"../util/ScriptUtil":27,"./CallStats":22,"./LocalStatsCollector.js":23,"./RTPStatsCollector.js":24,"events":51}],26:[function(require,module,exports){ +},{"../../service/statistics/Events":137,"../../service/statistics/constants":138,"../util/ScriptUtil":27,"./CallStats":22,"./LocalStatsCollector.js":23,"./RTPStatsCollector.js":24,"events":46}],26:[function(require,module,exports){ /** /** * @const @@ -7628,12 +7751,9 @@ ChatRoom.prototype.onPresence = function (pres) { } } - if(!member.isFocus) - this.eventEmitter.emit(XMPPEvents.USER_ID_CHANGED, from, member.id); - // Trigger status message update if (member.status) { - this.eventEmitter.emit(XMPPEvents.PRESENCE_STATUS, from, member); + this.eventEmitter.emit(XMPPEvents.PRESENCE_STATUS, from, member.status); } if(jibri) @@ -8118,7 +8238,7 @@ ChatRoom.prototype.onMute = function (iq) { module.exports = ChatRoom; }).call(this,"/modules/xmpp/ChatRoom.js") -},{"../../service/xmpp/XMPPEvents":137,"./moderator":36,"./recording":37,"events":51,"jitsi-meet-logger":79}],30:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"./moderator":36,"./recording":37,"events":46,"jitsi-meet-logger":50}],30:[function(require,module,exports){ (function (__filename){ /* * JingleSession provides an API to manage a single Jingle session. We will @@ -8254,7 +8374,7 @@ JingleSession.prototype.setAnswer = function(jingle) {}; module.exports = JingleSession; }).call(this,"/modules/xmpp/JingleSession.js") -},{"jitsi-meet-logger":79}],31:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],31:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -9917,7 +10037,7 @@ JingleSessionPC.prototype.getIceConnectionState = function () { module.exports = JingleSessionPC; }).call(this,"/modules/xmpp/JingleSessionPC.js") -},{"../../service/xmpp/XMPPEvents":137,"../RTC/RTC":16,"../RTC/RTCBrowserType":17,"./JingleSession":30,"./SDP":32,"./SDPDiffer":33,"./SDPUtil":34,"./TraceablePeerConnection":35,"async":47,"jitsi-meet-logger":79,"sdp-transform":115}],32:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"../RTC/RTC":16,"../RTC/RTCBrowserType":17,"./JingleSession":30,"./SDP":32,"./SDPDiffer":33,"./SDPUtil":34,"./TraceablePeerConnection":35,"async":45,"jitsi-meet-logger":50,"sdp-transform":79}],32:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -10568,7 +10688,7 @@ SDP.prototype.jingle2media = function (content) { module.exports = SDP; }).call(this,"/modules/xmpp/SDP.js") -},{"./SDPUtil":34,"jitsi-meet-logger":79}],33:[function(require,module,exports){ +},{"./SDPUtil":34,"jitsi-meet-logger":50}],33:[function(require,module,exports){ var SDPUtil = require("./SDPUtil"); function SDPDiffer(mySDP, otherSDP) @@ -11105,7 +11225,7 @@ SDPUtil = { module.exports = SDPUtil; }).call(this,"/modules/xmpp/SDPUtil.js") -},{"../RTC/RTCBrowserType":17,"jitsi-meet-logger":79}],35:[function(require,module,exports){ +},{"../RTC/RTCBrowserType":17,"jitsi-meet-logger":50}],35:[function(require,module,exports){ (function (__filename){ /* global $ */ var RTC = require('../RTC/RTC'); @@ -11612,7 +11732,7 @@ TraceablePeerConnection.prototype.getStats = function(callback, errback) { module.exports = TraceablePeerConnection; }).call(this,"/modules/xmpp/TraceablePeerConnection.js") -},{"../../service/xmpp/XMPPEvents":137,"../RTC/RTC":16,"../RTC/RTCBrowserType.js":17,"../util/RandomUtil":26,"jitsi-meet-logger":79,"sdp-interop":105,"sdp-simulcast":112,"sdp-transform":115}],36:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"../RTC/RTC":16,"../RTC/RTCBrowserType.js":17,"../util/RandomUtil":26,"jitsi-meet-logger":50,"sdp-interop":69,"sdp-simulcast":76,"sdp-transform":79}],36:[function(require,module,exports){ (function (__filename){ /* global $, $iq, Promise, Strophe */ @@ -12068,7 +12188,7 @@ Moderator.prototype.logout = function (callback) { module.exports = Moderator; }).call(this,"/modules/xmpp/moderator.js") -},{"../../service/authentication/AuthenticationEvents":133,"../../service/xmpp/XMPPEvents":137,"jitsi-meet-logger":79}],37:[function(require,module,exports){ +},{"../../service/authentication/AuthenticationEvents":135,"../../service/xmpp/XMPPEvents":139,"jitsi-meet-logger":50}],37:[function(require,module,exports){ (function (__filename){ /* global $, $iq, config, connection, focusMucJid, messageHandler, Toolbar, Util, Promise */ @@ -12295,7 +12415,7 @@ Recording.prototype.getURL = function () { module.exports = Recording; }).call(this,"/modules/xmpp/recording.js") -},{"../../service/xmpp/XMPPEvents":137,"jitsi-meet-logger":79}],38:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"jitsi-meet-logger":50}],38:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ /* a simple MUC connection plugin @@ -12408,7 +12528,7 @@ module.exports = function(XMPP) { }; }).call(this,"/modules/xmpp/strophe.emuc.js") -},{"./ChatRoom":29,"jitsi-meet-logger":79}],39:[function(require,module,exports){ +},{"./ChatRoom":29,"jitsi-meet-logger":50}],39:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -12705,7 +12825,7 @@ module.exports = function(XMPP, eventEmitter) { }).call(this,"/modules/xmpp/strophe.jingle.js") -},{"../../service/xmpp/XMPPEvents":137,"../RTC/RTCBrowserType":17,"./JingleSessionPC":31,"jitsi-meet-logger":79}],40:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"../RTC/RTCBrowserType":17,"./JingleSessionPC":31,"jitsi-meet-logger":50}],40:[function(require,module,exports){ /* global Strophe */ module.exports = function () { @@ -12853,7 +12973,7 @@ module.exports = function (XMPP, eventEmitter) { }; }).call(this,"/modules/xmpp/strophe.ping.js") -},{"../../service/xmpp/XMPPEvents":137,"jitsi-meet-logger":79}],42:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":139,"jitsi-meet-logger":50}],42:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ var logger = require("jitsi-meet-logger").getLogger(__filename); @@ -12970,7 +13090,7 @@ module.exports = function() { }; }).call(this,"/modules/xmpp/strophe.rayo.js") -},{"jitsi-meet-logger":79}],43:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],43:[function(require,module,exports){ (function (__filename){ /* global Strophe */ /** @@ -13019,7 +13139,7 @@ module.exports = function () { }; }).call(this,"/modules/xmpp/strophe.util.js") -},{"jitsi-meet-logger":79}],44:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],44:[function(require,module,exports){ (function (__filename){ /* global $, APP, config, Strophe*/ @@ -13076,9 +13196,6 @@ function initStrophePlugins(XMPP) // broadcastLocalVideoType, // StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED // ); -// RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) { -// XMPP.addToPresence("devices", devices); -// }); //} function XMPP(options) { @@ -13345,68 +13462,7 @@ XMPP.prototype.disconnect = function () { module.exports = XMPP; }).call(this,"/modules/xmpp/xmpp.js") -},{"../../JitsiConnectionErrors":5,"../../JitsiConnectionEvents":6,"../../service/RTC/RTCEvents":131,"../../service/xmpp/XMPPEvents":137,"../RTC/RTC":16,"./strophe.emuc":38,"./strophe.jingle":39,"./strophe.logger":40,"./strophe.ping":41,"./strophe.rayo":42,"./strophe.util":43,"events":51,"jitsi-meet-logger":79,"pako":84}],45:[function(require,module,exports){ -module.exports = after - -function after(count, callback, err_cb) { - var bail = false - err_cb = err_cb || noop - proxy.count = count - - return (count === 0) ? callback() : proxy - - function proxy(err, result) { - if (proxy.count <= 0) { - throw new Error('after called too many times') - } - --proxy.count - - // after first error, rest are passed to err_cb - if (err) { - bail = true - callback(err) - // future error callbacks will go to error handler - callback = err_cb - } else if (proxy.count === 0 && !bail) { - callback(null, result) - } - } -} - -function noop() {} - -},{}],46:[function(require,module,exports){ -/** - * An abstraction for slicing an arraybuffer even when - * ArrayBuffer.prototype.slice is not supported - * - * @api public - */ - -module.exports = function(arraybuffer, start, end) { - var bytes = arraybuffer.byteLength; - start = start || 0; - end = end || bytes; - - if (arraybuffer.slice) { return arraybuffer.slice(start, end); } - - if (start < 0) { start += bytes; } - if (end < 0) { end += bytes; } - if (end > bytes) { end = bytes; } - - if (start >= bytes || start >= end || bytes === 0) { - return new ArrayBuffer(0); - } - - var abv = new Uint8Array(arraybuffer); - var result = new Uint8Array(end - start); - for (var i = start, ii = 0; i < end; i++, ii++) { - result[ii] = abv[i]; - } - return result.buffer; -}; - -},{}],47:[function(require,module,exports){ +},{"../../JitsiConnectionErrors":5,"../../JitsiConnectionEvents":6,"../../service/RTC/RTCEvents":133,"../../service/xmpp/XMPPEvents":139,"../RTC/RTC":16,"./strophe.emuc":38,"./strophe.jingle":39,"./strophe.logger":40,"./strophe.ping":41,"./strophe.rayo":42,"./strophe.util":43,"events":46,"jitsi-meet-logger":50,"pako":52}],45:[function(require,module,exports){ (function (process){ /*! * async @@ -14533,208 +14589,7 @@ module.exports = function(arraybuffer, start, end) { }()); }).call(this,require('_process')) -},{"_process":103}],48:[function(require,module,exports){ - -/** - * Expose `Backoff`. - */ - -module.exports = Backoff; - -/** - * Initialize backoff timer with `opts`. - * - * - `min` initial timeout in milliseconds [100] - * - `max` max timeout [10000] - * - `jitter` [0] - * - `factor` [2] - * - * @param {Object} opts - * @api public - */ - -function Backoff(opts) { - opts = opts || {}; - this.ms = opts.min || 100; - this.max = opts.max || 10000; - this.factor = opts.factor || 2; - this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; - this.attempts = 0; -} - -/** - * Return the backoff duration. - * - * @return {Number} - * @api public - */ - -Backoff.prototype.duration = function(){ - var ms = this.ms * Math.pow(this.factor, this.attempts++); - if (this.jitter) { - var rand = Math.random(); - var deviation = Math.floor(rand * this.jitter * ms); - ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; - } - return Math.min(ms, this.max) | 0; -}; - -/** - * Reset the number of attempts. - * - * @api public - */ - -Backoff.prototype.reset = function(){ - this.attempts = 0; -}; - -/** - * Set the minimum duration - * - * @api public - */ - -Backoff.prototype.setMin = function(min){ - this.ms = min; -}; - -/** - * Set the maximum duration - * - * @api public - */ - -Backoff.prototype.setMax = function(max){ - this.max = max; -}; - -/** - * Set the jitter - * - * @api public - */ - -Backoff.prototype.setJitter = function(jitter){ - this.jitter = jitter; -}; - - -},{}],49:[function(require,module,exports){ -/* - * base64-arraybuffer - * https://github.com/niklasvh/base64-arraybuffer - * - * Copyright (c) 2012 Niklas von Hertzen - * Licensed under the MIT license. - */ -(function(chars){ - "use strict"; - - exports.encode = function(arraybuffer) { - var bytes = new Uint8Array(arraybuffer), - i, len = bytes.length, base64 = ""; - - for (i = 0; i < len; i+=3) { - base64 += chars[bytes[i] >> 2]; - base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; - base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; - base64 += chars[bytes[i + 2] & 63]; - } - - if ((len % 3) === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - - return base64; - }; - - exports.decode = function(base64) { - var bufferLength = base64.length * 0.75, - len = base64.length, i, p = 0, - encoded1, encoded2, encoded3, encoded4; - - if (base64[base64.length - 1] === "=") { - bufferLength--; - if (base64[base64.length - 2] === "=") { - bufferLength--; - } - } - - var arraybuffer = new ArrayBuffer(bufferLength), - bytes = new Uint8Array(arraybuffer); - - for (i = 0; i < len; i+=4) { - encoded1 = chars.indexOf(base64[i]); - encoded2 = chars.indexOf(base64[i+1]); - encoded3 = chars.indexOf(base64[i+2]); - encoded4 = chars.indexOf(base64[i+3]); - - bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); - bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); - bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); - } - - return arraybuffer; - }; -})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); - -},{}],50:[function(require,module,exports){ -(function (global){ -/** - * Create a blob builder even when vendor prefixes exist - */ - -var BlobBuilder = global.BlobBuilder - || global.WebKitBlobBuilder - || global.MSBlobBuilder - || global.MozBlobBuilder; - -/** - * Check if Blob constructor is supported - */ - -var blobSupported = (function() { - try { - var b = new Blob(['hi']); - return b.size == 2; - } catch(e) { - return false; - } -})(); - -/** - * Check if BlobBuilder is supported - */ - -var blobBuilderSupported = BlobBuilder - && BlobBuilder.prototype.append - && BlobBuilder.prototype.getBlob; - -function BlobBuilderConstructor(ary, options) { - options = options || {}; - - var bb = new BlobBuilder(); - for (var i = 0; i < ary.length; i++) { - bb.append(ary[i]); - } - return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); -}; - -module.exports = (function() { - if (blobSupported) { - return global.Blob; - } else if (blobBuilderSupported) { - return BlobBuilderConstructor; - } else { - return undefined; - } -})(); - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],51:[function(require,module,exports){ +},{"_process":47}],46:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -15037,3508 +14892,100 @@ function isUndefined(arg) { return arg === void 0; } -},{}],52:[function(require,module,exports){ -/** - * Slice reference. - */ - -var slice = [].slice; - -/** - * Bind `obj` to `fn`. - * - * @param {Object} obj - * @param {Function|String} fn or string - * @return {Function} - * @api public - */ - -module.exports = function(obj, fn){ - if ('string' == typeof fn) fn = obj[fn]; - if ('function' != typeof fn) throw new Error('bind() requires a function'); - var args = slice.call(arguments, 2); - return function(){ - return fn.apply(obj, args.concat(slice.call(arguments))); - } -}; - -},{}],53:[function(require,module,exports){ - -/** - * Expose `Emitter`. - */ - -module.exports = Emitter; - -/** - * Initialize a new `Emitter`. - * - * @api public - */ - -function Emitter(obj) { - if (obj) return mixin(obj); -}; - -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} - -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.on = -Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; -}; - -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; -}; - -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = -Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; -}; - -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; -}; - -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; -}; - -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; - -},{}],54:[function(require,module,exports){ - -module.exports = function(a, b){ - var fn = function(){}; - fn.prototype = b.prototype; - a.prototype = new fn; - a.prototype.constructor = a; -}; -},{}],55:[function(require,module,exports){ - -/** - * Expose `debug()` as the module. - */ - -module.exports = debug; - -/** - * Create a debugger with the given `name`. - * - * @param {String} name - * @return {Type} - * @api public - */ - -function debug(name) { - if (!debug.enabled(name)) return function(){}; - - return function(fmt){ - fmt = coerce(fmt); - - var curr = new Date; - var ms = curr - (debug[name] || curr); - debug[name] = curr; - - fmt = name - + ' ' - + fmt - + ' +' + debug.humanize(ms); - - // This hackery is required for IE8 - // where `console.log` doesn't have 'apply' - window.console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); - } -} - -/** - * The currently active debug mode names. - */ - -debug.names = []; -debug.skips = []; - -/** - * Enables a debug mode by name. This can include modes - * separated by a colon and wildcards. - * - * @param {String} name - * @api public - */ - -debug.enable = function(name) { - try { - localStorage.debug = name; - } catch(e){} - - var split = (name || '').split(/[\s,]+/) - , len = split.length; - - for (var i = 0; i < len; i++) { - name = split[i].replace('*', '.*?'); - if (name[0] === '-') { - debug.skips.push(new RegExp('^' + name.substr(1) + '$')); - } - else { - debug.names.push(new RegExp('^' + name + '$')); - } - } -}; - -/** - * Disable debug output. - * - * @api public - */ - -debug.disable = function(){ - debug.enable(''); -}; - -/** - * Humanize the given `ms`. - * - * @param {Number} m - * @return {String} - * @api private - */ - -debug.humanize = function(ms) { - var sec = 1000 - , min = 60 * 1000 - , hour = 60 * min; - - if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; - if (ms >= min) return (ms / min).toFixed(1) + 'm'; - if (ms >= sec) return (ms / sec | 0) + 's'; - return ms + 'ms'; -}; - -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - -debug.enabled = function(name) { - for (var i = 0, len = debug.skips.length; i < len; i++) { - if (debug.skips[i].test(name)) { - return false; - } - } - for (var i = 0, len = debug.names.length; i < len; i++) { - if (debug.names[i].test(name)) { - return true; - } - } - return false; -}; - -/** - * Coerce `val`. - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - -// persist - -try { - if (window.localStorage) debug.enable(localStorage.debug); -} catch(e){} - -},{}],56:[function(require,module,exports){ - -module.exports = require('./lib/'); - -},{"./lib/":57}],57:[function(require,module,exports){ - -module.exports = require('./socket'); - -/** - * Exports parser - * - * @api public - * - */ -module.exports.parser = require('engine.io-parser'); - -},{"./socket":58,"engine.io-parser":69}],58:[function(require,module,exports){ -(function (global){ -/** - * Module dependencies. - */ - -var transports = require('./transports'); -var Emitter = require('component-emitter'); -var debug = require('debug')('engine.io-client:socket'); -var index = require('indexof'); -var parser = require('engine.io-parser'); -var parseuri = require('parseuri'); -var parsejson = require('parsejson'); -var parseqs = require('parseqs'); - -/** - * Module exports. - */ - -module.exports = Socket; - -/** - * Noop function. - * - * @api private - */ - -function noop(){} - -/** - * Socket constructor. - * - * @param {String|Object} uri or options - * @param {Object} options - * @api public - */ - -function Socket(uri, opts){ - if (!(this instanceof Socket)) return new Socket(uri, opts); - - opts = opts || {}; - - if (uri && 'object' == typeof uri) { - opts = uri; - uri = null; - } - - if (uri) { - uri = parseuri(uri); - opts.host = uri.host; - opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; - opts.port = uri.port; - if (uri.query) opts.query = uri.query; - } - - this.secure = null != opts.secure ? opts.secure : - (global.location && 'https:' == location.protocol); - - if (opts.host) { - var pieces = opts.host.split(':'); - opts.hostname = pieces.shift(); - if (pieces.length) { - opts.port = pieces.pop(); - } else if (!opts.port) { - // if no port is specified manually, use the protocol default - opts.port = this.secure ? '443' : '80'; - } - } - - this.agent = opts.agent || false; - this.hostname = opts.hostname || - (global.location ? location.hostname : 'localhost'); - this.port = opts.port || (global.location && location.port ? - location.port : - (this.secure ? 443 : 80)); - this.query = opts.query || {}; - if ('string' == typeof this.query) this.query = parseqs.decode(this.query); - this.upgrade = false !== opts.upgrade; - this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; - this.forceJSONP = !!opts.forceJSONP; - this.jsonp = false !== opts.jsonp; - this.forceBase64 = !!opts.forceBase64; - this.enablesXDR = !!opts.enablesXDR; - this.timestampParam = opts.timestampParam || 't'; - this.timestampRequests = opts.timestampRequests; - this.transports = opts.transports || ['polling', 'websocket']; - this.readyState = ''; - this.writeBuffer = []; - this.callbackBuffer = []; - this.policyPort = opts.policyPort || 843; - this.rememberUpgrade = opts.rememberUpgrade || false; - this.binaryType = null; - this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; - - // SSL options for Node.js client - this.pfx = opts.pfx || null; - this.key = opts.key || null; - this.passphrase = opts.passphrase || null; - this.cert = opts.cert || null; - this.ca = opts.ca || null; - this.ciphers = opts.ciphers || null; - this.rejectUnauthorized = opts.rejectUnauthorized || null; - - this.open(); -} - -Socket.priorWebsocketSuccess = false; - -/** - * Mix in `Emitter`. - */ - -Emitter(Socket.prototype); - -/** - * Protocol version. - * - * @api public - */ - -Socket.protocol = parser.protocol; // this is an int - -/** - * Expose deps for legacy compatibility - * and standalone browser access. - */ - -Socket.Socket = Socket; -Socket.Transport = require('./transport'); -Socket.transports = require('./transports'); -Socket.parser = require('engine.io-parser'); - -/** - * Creates transport of the given type. - * - * @param {String} transport name - * @return {Transport} - * @api private - */ - -Socket.prototype.createTransport = function (name) { - debug('creating transport "%s"', name); - var query = clone(this.query); - - // append engine.io protocol identifier - query.EIO = parser.protocol; - - // transport name - query.transport = name; - - // session id if we already have one - if (this.id) query.sid = this.id; - - var transport = new transports[name]({ - agent: this.agent, - hostname: this.hostname, - port: this.port, - secure: this.secure, - path: this.path, - query: query, - forceJSONP: this.forceJSONP, - jsonp: this.jsonp, - forceBase64: this.forceBase64, - enablesXDR: this.enablesXDR, - timestampRequests: this.timestampRequests, - timestampParam: this.timestampParam, - policyPort: this.policyPort, - socket: this, - pfx: this.pfx, - key: this.key, - passphrase: this.passphrase, - cert: this.cert, - ca: this.ca, - ciphers: this.ciphers, - rejectUnauthorized: this.rejectUnauthorized - }); - - return transport; -}; - -function clone (obj) { - var o = {}; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - o[i] = obj[i]; - } - } - return o; -} - -/** - * Initializes transport to use and starts probe. - * - * @api private - */ -Socket.prototype.open = function () { - var transport; - if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { - transport = 'websocket'; - } else if (0 == this.transports.length) { - // Emit error on next tick so it can be listened to - var self = this; - setTimeout(function() { - self.emit('error', 'No transports available'); - }, 0); - return; - } else { - transport = this.transports[0]; - } - this.readyState = 'opening'; - - // Retry with the next transport if the transport is disabled (jsonp: false) - var transport; - try { - transport = this.createTransport(transport); - } catch (e) { - this.transports.shift(); - this.open(); - return; - } - - transport.open(); - this.setTransport(transport); -}; - -/** - * Sets the current transport. Disables the existing one (if any). - * - * @api private - */ - -Socket.prototype.setTransport = function(transport){ - debug('setting transport %s', transport.name); - var self = this; - - if (this.transport) { - debug('clearing existing transport %s', this.transport.name); - this.transport.removeAllListeners(); - } - - // set up transport - this.transport = transport; - - // set up transport listeners - transport - .on('drain', function(){ - self.onDrain(); - }) - .on('packet', function(packet){ - self.onPacket(packet); - }) - .on('error', function(e){ - self.onError(e); - }) - .on('close', function(){ - self.onClose('transport close'); - }); -}; - -/** - * Probes a transport. - * - * @param {String} transport name - * @api private - */ - -Socket.prototype.probe = function (name) { - debug('probing transport "%s"', name); - var transport = this.createTransport(name, { probe: 1 }) - , failed = false - , self = this; - - Socket.priorWebsocketSuccess = false; - - function onTransportOpen(){ - if (self.onlyBinaryUpgrades) { - var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; - failed = failed || upgradeLosesBinary; - } - if (failed) return; - - debug('probe transport "%s" opened', name); - transport.send([{ type: 'ping', data: 'probe' }]); - transport.once('packet', function (msg) { - if (failed) return; - if ('pong' == msg.type && 'probe' == msg.data) { - debug('probe transport "%s" pong', name); - self.upgrading = true; - self.emit('upgrading', transport); - if (!transport) return; - Socket.priorWebsocketSuccess = 'websocket' == transport.name; - - debug('pausing current transport "%s"', self.transport.name); - self.transport.pause(function () { - if (failed) return; - if ('closed' == self.readyState) return; - debug('changing transport and sending upgrade packet'); - - cleanup(); - - self.setTransport(transport); - transport.send([{ type: 'upgrade' }]); - self.emit('upgrade', transport); - transport = null; - self.upgrading = false; - self.flush(); - }); - } else { - debug('probe transport "%s" failed', name); - var err = new Error('probe error'); - err.transport = transport.name; - self.emit('upgradeError', err); - } - }); - } - - function freezeTransport() { - if (failed) return; - - // Any callback called by transport should be ignored since now - failed = true; - - cleanup(); - - transport.close(); - transport = null; - } - - //Handle any error that happens while probing - function onerror(err) { - var error = new Error('probe error: ' + err); - error.transport = transport.name; - - freezeTransport(); - - debug('probe transport "%s" failed because of error: %s', name, err); - - self.emit('upgradeError', error); - } - - function onTransportClose(){ - onerror("transport closed"); - } - - //When the socket is closed while we're probing - function onclose(){ - onerror("socket closed"); - } - - //When the socket is upgraded while we're probing - function onupgrade(to){ - if (transport && to.name != transport.name) { - debug('"%s" works - aborting "%s"', to.name, transport.name); - freezeTransport(); - } - } - - //Remove all listeners on the transport and on self - function cleanup(){ - transport.removeListener('open', onTransportOpen); - transport.removeListener('error', onerror); - transport.removeListener('close', onTransportClose); - self.removeListener('close', onclose); - self.removeListener('upgrading', onupgrade); - } - - transport.once('open', onTransportOpen); - transport.once('error', onerror); - transport.once('close', onTransportClose); - - this.once('close', onclose); - this.once('upgrading', onupgrade); - - transport.open(); - -}; - -/** - * Called when connection is deemed open. - * - * @api public - */ - -Socket.prototype.onOpen = function () { - debug('socket open'); - this.readyState = 'open'; - Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; - this.emit('open'); - this.flush(); - - // we check for `readyState` in case an `open` - // listener already closed the socket - if ('open' == this.readyState && this.upgrade && this.transport.pause) { - debug('starting upgrade probes'); - for (var i = 0, l = this.upgrades.length; i < l; i++) { - this.probe(this.upgrades[i]); - } - } -}; - -/** - * Handles a packet. - * - * @api private - */ - -Socket.prototype.onPacket = function (packet) { - if ('opening' == this.readyState || 'open' == this.readyState) { - debug('socket receive: type "%s", data "%s"', packet.type, packet.data); - - this.emit('packet', packet); - - // Socket is live - any packet counts - this.emit('heartbeat'); - - switch (packet.type) { - case 'open': - this.onHandshake(parsejson(packet.data)); - break; - - case 'pong': - this.setPing(); - break; - - case 'error': - var err = new Error('server error'); - err.code = packet.data; - this.emit('error', err); - break; - - case 'message': - this.emit('data', packet.data); - this.emit('message', packet.data); - break; - } - } else { - debug('packet received with socket readyState "%s"', this.readyState); - } -}; - -/** - * Called upon handshake completion. - * - * @param {Object} handshake obj - * @api private - */ - -Socket.prototype.onHandshake = function (data) { - this.emit('handshake', data); - this.id = data.sid; - this.transport.query.sid = data.sid; - this.upgrades = this.filterUpgrades(data.upgrades); - this.pingInterval = data.pingInterval; - this.pingTimeout = data.pingTimeout; - this.onOpen(); - // In case open handler closes socket - if ('closed' == this.readyState) return; - this.setPing(); - - // Prolong liveness of socket on heartbeat - this.removeListener('heartbeat', this.onHeartbeat); - this.on('heartbeat', this.onHeartbeat); -}; - -/** - * Resets ping timeout. - * - * @api private - */ - -Socket.prototype.onHeartbeat = function (timeout) { - clearTimeout(this.pingTimeoutTimer); - var self = this; - self.pingTimeoutTimer = setTimeout(function () { - if ('closed' == self.readyState) return; - self.onClose('ping timeout'); - }, timeout || (self.pingInterval + self.pingTimeout)); -}; - -/** - * Pings server every `this.pingInterval` and expects response - * within `this.pingTimeout` or closes connection. - * - * @api private - */ - -Socket.prototype.setPing = function () { - var self = this; - clearTimeout(self.pingIntervalTimer); - self.pingIntervalTimer = setTimeout(function () { - debug('writing ping packet - expecting pong within %sms', self.pingTimeout); - self.ping(); - self.onHeartbeat(self.pingTimeout); - }, self.pingInterval); -}; - -/** -* Sends a ping packet. -* -* @api public -*/ - -Socket.prototype.ping = function () { - this.sendPacket('ping'); -}; - -/** - * Called on `drain` event - * - * @api private - */ - -Socket.prototype.onDrain = function() { - for (var i = 0; i < this.prevBufferLen; i++) { - if (this.callbackBuffer[i]) { - this.callbackBuffer[i](); - } - } - - this.writeBuffer.splice(0, this.prevBufferLen); - this.callbackBuffer.splice(0, this.prevBufferLen); - - // setting prevBufferLen = 0 is very important - // for example, when upgrading, upgrade packet is sent over, - // and a nonzero prevBufferLen could cause problems on `drain` - this.prevBufferLen = 0; - - if (this.writeBuffer.length == 0) { - this.emit('drain'); - } else { - this.flush(); - } -}; - -/** - * Flush write buffers. - * - * @api private - */ - -Socket.prototype.flush = function () { - if ('closed' != this.readyState && this.transport.writable && - !this.upgrading && this.writeBuffer.length) { - debug('flushing %d packets in socket', this.writeBuffer.length); - this.transport.send(this.writeBuffer); - // keep track of current length of writeBuffer - // splice writeBuffer and callbackBuffer on `drain` - this.prevBufferLen = this.writeBuffer.length; - this.emit('flush'); - } -}; - -/** - * Sends a message. - * - * @param {String} message. - * @param {Function} callback function. - * @return {Socket} for chaining. - * @api public - */ - -Socket.prototype.write = -Socket.prototype.send = function (msg, fn) { - this.sendPacket('message', msg, fn); - return this; -}; - -/** - * Sends a packet. - * - * @param {String} packet type. - * @param {String} data. - * @param {Function} callback function. - * @api private - */ - -Socket.prototype.sendPacket = function (type, data, fn) { - if ('closing' == this.readyState || 'closed' == this.readyState) { - return; - } - - var packet = { type: type, data: data }; - this.emit('packetCreate', packet); - this.writeBuffer.push(packet); - this.callbackBuffer.push(fn); - this.flush(); -}; - -/** - * Closes the connection. - * - * @api private - */ - -Socket.prototype.close = function () { - if ('opening' == this.readyState || 'open' == this.readyState) { - this.readyState = 'closing'; - - var self = this; - - function close() { - self.onClose('forced close'); - debug('socket closing - telling transport to close'); - self.transport.close(); - } - - function cleanupAndClose() { - self.removeListener('upgrade', cleanupAndClose); - self.removeListener('upgradeError', cleanupAndClose); - close(); - } - - function waitForUpgrade() { - // wait for upgrade to finish since we can't send packets while pausing a transport - self.once('upgrade', cleanupAndClose); - self.once('upgradeError', cleanupAndClose); - } - - if (this.writeBuffer.length) { - this.once('drain', function() { - if (this.upgrading) { - waitForUpgrade(); - } else { - close(); - } - }); - } else if (this.upgrading) { - waitForUpgrade(); +},{}],47:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); } else { - close(); + queueIndex = -1; + } + if (queue.length) { + drainQueue(); } - } - - return this; -}; - -/** - * Called upon transport error - * - * @api private - */ - -Socket.prototype.onError = function (err) { - debug('socket error %j', err); - Socket.priorWebsocketSuccess = false; - this.emit('error', err); - this.onClose('transport error', err); -}; - -/** - * Called upon transport close. - * - * @api private - */ - -Socket.prototype.onClose = function (reason, desc) { - if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) { - debug('socket close with reason: "%s"', reason); - var self = this; - - // clear timers - clearTimeout(this.pingIntervalTimer); - clearTimeout(this.pingTimeoutTimer); - - // clean buffers in next tick, so developers can still - // grab the buffers on `close` event - setTimeout(function() { - self.writeBuffer = []; - self.callbackBuffer = []; - self.prevBufferLen = 0; - }, 0); - - // stop event from firing again for transport - this.transport.removeAllListeners('close'); - - // ensure transport won't stay open - this.transport.close(); - - // ignore further transport communication - this.transport.removeAllListeners(); - - // set ready state - this.readyState = 'closed'; - - // clear session id - this.id = null; - - // emit close event - this.emit('close', reason, desc); - } -}; - -/** - * Filters upgrades, returning only those matching client transports. - * - * @param {Array} server upgrades - * @api private - * - */ - -Socket.prototype.filterUpgrades = function (upgrades) { - var filteredUpgrades = []; - for (var i = 0, j = upgrades.length; i