From 6998827982b5b965ad974ad3dc393a4188956a74 Mon Sep 17 00:00:00 2001 From: isymchych Date: Thu, 28 Jan 2016 13:59:33 +0200 Subject: [PATCH] do not hide small video until stream has been attached --- libs/lib-jitsi-meet.js | 4208 ++++++++++++------------- modules/UI/videolayout/RemoteVideo.js | 6 +- 2 files changed, 1972 insertions(+), 2242 deletions(-) diff --git a/libs/lib-jitsi-meet.js b/libs/lib-jitsi-meet.js index 2b4bd996a..aa0232c31 100644 --- a/libs/lib-jitsi-meet.js +++ b/libs/lib-jitsi-meet.js @@ -1087,7 +1087,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":135,"./service/authentication/AuthenticationEvents":137,"./service/xmpp/XMPPEvents":141,"events":47,"jitsi-meet-logger":51}],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":137,"./service/authentication/AuthenticationEvents":139,"./service/xmpp/XMPPEvents":143,"events":46,"jitsi-meet-logger":50}],2:[function(require,module,exports){ /** * Enumeration with the errors for the conference. * @type {{string: string}} @@ -1560,7 +1560,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":136,"es6-promise":49,"jitsi-meet-logger":51}],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":138,"es6-promise":48,"jitsi-meet-logger":50}],8:[function(require,module,exports){ /* global Strophe */ /** @@ -1796,7 +1796,7 @@ JitsiDTMFManager.prototype.sendTones = function (tones, duration, pause) { }; }).call(this,"/modules/DTMF/JitsiDTMFManager.js") -},{"jitsi-meet-logger":51}],12:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],12:[function(require,module,exports){ (function (__filename){ /* global config, APP, Strophe */ @@ -2020,7 +2020,7 @@ DataChannels.prototype._some = function (callback, thisArg) { module.exports = DataChannels; }).call(this,"/modules/RTC/DataChannels.js") -},{"../../service/RTC/RTCEvents":135,"jitsi-meet-logger":51}],13:[function(require,module,exports){ +},{"../../service/RTC/RTCEvents":137,"jitsi-meet-logger":50}],13:[function(require,module,exports){ var JitsiTrack = require("./JitsiTrack"); var RTCBrowserType = require("./RTCBrowserType"); var JitsiTrackEvents = require('../../JitsiTrackEvents'); @@ -2101,7 +2101,7 @@ JitsiLocalTrack.prototype._setMute = function (mute) { }; if (isAudio) { streamOptions['micDeviceId'] = self.deviceId; - } else {self.videoType === 'camera'} { + } else if(self.videoType === 'camera') { streamOptions['cameraDeviceId'] = self.deviceId; } RTCUtils.obtainAudioAndVideoPermissions(streamOptions) @@ -2545,7 +2545,7 @@ JitsiTrack.prototype.setAudioLevel = function (audioLevel) { module.exports = JitsiTrack; -},{"../../JitsiTrackEvents":10,"./RTCBrowserType":17,"./RTCUtils":18,"events":47}],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"); @@ -2812,7 +2812,7 @@ RTC.prototype.setAudioLevel = function (jid, audioLevel) { } module.exports = RTC; -},{"../../service/RTC/MediaStreamTypes":134,"../../service/RTC/RTCEvents.js":135,"./DataChannels":12,"./JitsiLocalTrack.js":13,"./JitsiRemoteTrack.js":14,"./JitsiTrack":15,"./RTCBrowserType":17,"./RTCUtils.js":18,"events":47}],17:[function(require,module,exports){ +},{"../../service/RTC/MediaStreamTypes":136,"../../service/RTC/RTCEvents.js":137,"./DataChannels":12,"./JitsiLocalTrack.js":13,"./JitsiRemoteTrack.js":14,"./JitsiTrack":15,"./RTCBrowserType":17,"./RTCUtils.js":18,"events":46}],17:[function(require,module,exports){ var currentBrowser; @@ -3532,6 +3532,11 @@ var RTCUtils = { return; } + var isVideoStream = !!stream.getVideoTracks().length; + if (isVideoStream && !elSel.is(':visible')) { + throw new Error('video element must be visible to attach video stream'); + } + attachMediaStream(elSel[0], stream); }; self.getStreamID = function (stream) { @@ -3805,7 +3810,7 @@ var RTCUtils = { module.exports = RTCUtils; }).call(this,"/modules/RTC/RTCUtils.js") -},{"../../JitsiTrackErrors":9,"../../service/RTC/RTCEvents":135,"../../service/RTC/Resolutions":136,"../xmpp/SDPUtil":34,"./RTCBrowserType":17,"./ScreenObtainer":19,"./adapter.screenshare":20,"events":47,"jitsi-meet-logger":51}],19:[function(require,module,exports){ +},{"../../JitsiTrackErrors":9,"../../service/RTC/RTCEvents":137,"../../service/RTC/Resolutions":138,"../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 */ @@ -4221,7 +4226,7 @@ function initFirefoxExtensionDetection(options) { module.exports = ScreenObtainer; }).call(this,"/modules/RTC/ScreenObtainer.js") -},{"../../JitsiTrackErrors":9,"../../service/desktopsharing/DesktopSharingEventTypes":138,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":51}],20:[function(require,module,exports){ +},{"../../JitsiTrackErrors":9,"../../service/desktopsharing/DesktopSharingEventTypes":140,"./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); @@ -5405,7 +5410,7 @@ if (navigator.mozGetUserMedia) { } }).call(this,"/modules/RTC/adapter.screenshare.js") -},{"jitsi-meet-logger":51}],21:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],21:[function(require,module,exports){ (function (__filename){ var logger = require("jitsi-meet-logger").getLogger(__filename); @@ -5494,7 +5499,7 @@ Settings.prototype.getCallStatsUserName = function () { module.exports = Settings; }).call(this,"/modules/settings/Settings.js") -},{"../util/UsernameGenerator":28,"jitsi-meet-logger":51}],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); @@ -5515,10 +5520,50 @@ var wrtcFuncNames = { getUserMedia: "getUserMedia" }; +/** + * @const + * @see http://www.callstats.io/api/#enumeration-of-fabricevent + */ +var fabricEvent = { + fabricSetupFailed:"fabricSetupFailed", + fabricHold:"fabricHold", + fabricResume:"fabricResume", + audioMute:"audioMute", + audioUnmute:"audioUnmute", + videoPause:"videoPause", + videoResume:"videoResume", + fabricUsageEvent:"fabricUsageEvent", + fabricStats:"fabricStats", + fabricTerminated:"fabricTerminated" +}; + var callStats = null; function initCallback (err, msg) { logger.log("CallStats Status: err=" + err + " msg=" + msg); + + // there is no lib, nothing to report to + if (err !== 'success') + return; + + // notify callstats about failures if there were any + if (CallStats.reportsQueue.length) { + CallStats.reportsQueue.forEach(function (report) { + if (report.type === reportType.ERROR) + { + var error = report.data; + CallStats._reportError.call(this, error.type, error.error, + error.pc); + } + else if (report.type === reportType.EVENT) + { + var data = report.data; + callStats.sendFabricEvent( + this.peerconnection, data.event, this.confID); + } + }, this); + CallStats.reportsQueue.length = 0; + } } /** @@ -5568,7 +5613,7 @@ var CallStats = _try_catch(function(jingleSession, Settings, options) { callStats.initialize(options.callStatsID, options.callStatsSecret, this.userID, - initCallback); + initCallback.bind(this)); callStats.addNewFabric(this.peerconnection, Strophe.getResourceFromJid(jingleSession.peerjid), @@ -5582,28 +5627,27 @@ var CallStats = _try_catch(function(jingleSession, Settings, options) { callStats = null; logger.error(e); } - // notify callstats about failures if there were any - if (CallStats.pendingErrors.length) { - CallStats.pendingErrors.forEach(function (error) { - CallStats._reportError.call(this, error.type, error.error, - error.pc); - }, this); - CallStats.pendingErrors.length = 0; - } }); -// some errors may happen before CallStats init +// some errors/events may happen before CallStats init // in this case we accumulate them in this array // and send them to callstats on init -CallStats.pendingErrors = []; +CallStats.reportsQueue = []; + +/** + * Type of pending reports, can be event or an error. + * @type {{ERROR: string, EVENT: string}} + */ +var reportType = { + ERROR: "error", + EVENT: "event" +}; CallStats.prototype.pcCallback = _try_catch(function (err, msg) { if (!callStats) { return; } logger.log("Monitoring status: "+ err + " msg: " + msg); - callStats.sendFabricEvent(this.peerconnection, - callStats.fabricEvent.fabricSetup, this.confID); }); /** @@ -5611,22 +5655,38 @@ CallStats.prototype.pcCallback = _try_catch(function (err, msg) { * @param mute {boolean} true for muted and false for not muted * @param type {String} "audio"/"video" */ -CallStats.prototype.sendMuteEvent = _try_catch(function (mute, type) { - if (!callStats) { - return; - } +CallStats.sendMuteEvent = _try_catch(function (mute, type, cs) { + var event = null; if (type === "video") { - event = (mute? callStats.fabricEvent.videoPause : - callStats.fabricEvent.videoResume); + event = (mute? fabricEvent.videoPause : fabricEvent.videoResume); } else { - event = (mute? callStats.fabricEvent.audioMute : - callStats.fabricEvent.audioUnmute); + event = (mute? fabricEvent.audioMute : fabricEvent.audioUnmute); } - callStats.sendFabricEvent(this.peerconnection, event, this.confID); + + CallStats._reportEvent.call(cs, event); }); +/** + * Reports an error to callstats. + * + * @param type the type of the error, which will be one of the wrtcFuncNames + * @param e the error + * @param pc the peerconnection + * @private + */ +CallStats._reportEvent = function (event) { + if (callStats) { + callStats.sendFabricEvent(this.peerconnection, event, this.confID); + } else { + CallStats.reportsQueue.push({ + type: reportType.EVENT, + data: {event: event} + }); + } +}; + /** * Notifies CallStats for connection setup errors */ @@ -5682,7 +5742,10 @@ CallStats._reportError = function (type, e, pc) { if (callStats) { callStats.reportError(pc, this.confID, type, e); } else { - CallStats.pendingErrors.push({ type: type, error: e, pc: pc}); + CallStats.reportsQueue.push({ + type: reportType.ERROR, + data: { type: type, error: e, pc: pc} + }); } // else just ignore it }; @@ -5755,7 +5818,7 @@ CallStats.sendAddIceCandidateFailed = _try_catch(function (e, pc, cs) { module.exports = CallStats; }).call(this,"/modules/statistics/CallStats.js") -},{"jitsi-meet-logger":51,"jssha":52,"socket.io-client":87}],23:[function(require,module,exports){ +},{"jitsi-meet-logger":50,"jssha":51,"socket.io-client":86}],23:[function(require,module,exports){ /* global config */ /** * Provides statistics for the local stream. @@ -6612,7 +6675,7 @@ StatsCollector.prototype.processAudioLevelReport = function () { }; }).call(this,"/modules/statistics/RTPStatsCollector.js") -},{"../../service/statistics/Events":139,"../RTC/RTCBrowserType":17,"jitsi-meet-logger":51}],25:[function(require,module,exports){ +},{"../../service/statistics/Events":141,"../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"); @@ -6797,8 +6860,8 @@ Statistics.prototype.sendSetupFailedEvent = function () { * @param type {String} "audio"/"video" */ Statistics.prototype.sendMuteEvent = function (muted, type) { - if(this.callStatsIntegrationEnabled && this.callstats) - this.callstats.sendMuteEvent(muted, type); + if(this.callStatsIntegrationEnabled) + CallStats.sendMuteEvent(muted, type, this.callstats); } /** @@ -6892,7 +6955,7 @@ Statistics.LOCAL_JID = require("../../service/statistics/constants").LOCAL_JID; module.exports = Statistics; -},{"../../service/statistics/Events":139,"../../service/statistics/constants":140,"../util/ScriptUtil":27,"./CallStats":22,"./LocalStatsCollector.js":23,"./RTPStatsCollector.js":24,"events":47}],26:[function(require,module,exports){ +},{"../../service/statistics/Events":141,"../../service/statistics/constants":142,"../util/ScriptUtil":27,"./CallStats":22,"./LocalStatsCollector.js":23,"./RTPStatsCollector.js":24,"events":46}],26:[function(require,module,exports){ /** /** * @const @@ -8029,33 +8092,12 @@ ChatRoom.prototype.addStream = function (stream, callback) { ChatRoom.prototype.setVideoMute = function (mute, callback, options) { var self = this; - var localCallback = function (mute) { - self.sendVideoInfoPresence(mute); - if(callback) - callback(mute); - }; - - if(this.session) - { - this.session.setVideoMute( - mute, localCallback, options); - } - else { - localCallback(mute); - } - + this.sendVideoInfoPresence(mute); + if(callback) + callback(mute); }; ChatRoom.prototype.setAudioMute = function (mute, callback) { - //This will be for remote streams only -// if (this.forceMuted && !mute) { -// logger.info("Asking focus for unmute"); -// this.connection.moderate.setMute(this.connection.emuc.myroomjid, mute); -// // FIXME: wait for result before resetting muted status -// this.forceMuted = false; -// } - - return this.sendAudioInfoPresence(mute, callback); }; @@ -8256,7 +8298,7 @@ ChatRoom.prototype.onMute = function (iq) { module.exports = ChatRoom; }).call(this,"/modules/xmpp/ChatRoom.js") -},{"../../service/xmpp/XMPPEvents":141,"./moderator":36,"./recording":37,"events":47,"jitsi-meet-logger":51}],30:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"./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 @@ -8392,7 +8434,7 @@ JingleSession.prototype.setAnswer = function(jingle) {}; module.exports = JingleSession; }).call(this,"/modules/xmpp/JingleSession.js") -},{"jitsi-meet-logger":51}],31:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],31:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -9777,90 +9819,6 @@ JingleSessionPC.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) { } }; -/** - * Mutes/unmutes the (local) video i.e. enables/disables all video tracks. - * - * @param mute true to mute the (local) video i.e. to disable all video - * tracks; otherwise, false - * @param callback a function to be invoked with mute after all video - * tracks have been enabled/disabled. The function may, optionally, return - * another function which is to be invoked after the whole mute/unmute operation - * has completed successfully. - * @param options an object which specifies optional arguments such as the - * boolean key byUser with default value true which - * specifies whether the method was initiated in response to a user command (in - * contrast to an automatic decision made by the application logic) - */ -JingleSessionPC.prototype.setVideoMute = function (mute, callback, options) { - var byUser; - - if (options) { - byUser = options.byUser; - if (typeof byUser === 'undefined') { - byUser = true; - } - } else { - byUser = true; - } - // The user's command to mute the (local) video takes precedence over any - // automatic decision made by the application logic. - if (byUser) { - this.videoMuteByUser = mute; - } else if (this.videoMuteByUser) { - return; - } - - this.hardMuteVideo(mute); - - var self = this; - var oldSdp = null; - if(self.peerconnection) { - if(self.peerconnection.localDescription) { - oldSdp = new SDP(self.peerconnection.localDescription.sdp); - } - } - - this.modifySourcesQueue.push(function() { - logger.log('modify sources done'); - - callback(mute); - - var newSdp = new SDP(self.peerconnection.localDescription.sdp); - logger.log("SDPs", oldSdp, newSdp); - self.notifyMySSRCUpdate(oldSdp, newSdp); - }); -}; - -JingleSessionPC.prototype.hardMuteVideo = function (muted) { - this.pendingop = muted ? 'mute' : 'unmute'; -}; - -JingleSessionPC.prototype.sendMute = function (muted, content) { - var info = $iq({to: this.peerjid, - type: 'set'}) - .c('jingle', {xmlns: 'urn:xmpp:jingle:1', - action: 'session-info', - initiator: this.initiator, - sid: this.sid }); - info.c(muted ? 'mute' : 'unmute', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'}); - info.attrs({'creator': this.me == this.initiator ? 'creator' : 'responder'}); - if (content) { - info.attrs({'name': content}); - } - this.connection.send(info); -}; - -JingleSessionPC.prototype.sendRinging = function () { - var info = $iq({to: this.peerjid, - type: 'set'}) - .c('jingle', {xmlns: 'urn:xmpp:jingle:1', - action: 'session-info', - initiator: this.initiator, - sid: this.sid }); - info.c('ringing', {xmlns: 'urn:xmpp:jingle:apps:rtp:info:1'}); - this.connection.send(info); -}; - JingleSessionPC.prototype.getStats = function (interval) { var self = this; var recv = {audio: 0, video: 0}; @@ -10055,7 +10013,7 @@ JingleSessionPC.prototype.getIceConnectionState = function () { module.exports = JingleSessionPC; }).call(this,"/modules/xmpp/JingleSessionPC.js") -},{"../../service/xmpp/XMPPEvents":141,"../RTC/RTC":16,"../RTC/RTCBrowserType":17,"./JingleSession":30,"./SDP":32,"./SDPDiffer":33,"./SDPUtil":34,"./TraceablePeerConnection":35,"async":45,"jitsi-meet-logger":51,"sdp-transform":84}],32:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"../RTC/RTC":16,"../RTC/RTCBrowserType":17,"./JingleSession":30,"./SDP":32,"./SDPDiffer":33,"./SDPUtil":34,"./TraceablePeerConnection":35,"async":45,"jitsi-meet-logger":50,"sdp-transform":83}],32:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -10706,7 +10664,7 @@ SDP.prototype.jingle2media = function (content) { module.exports = SDP; }).call(this,"/modules/xmpp/SDP.js") -},{"./SDPUtil":34,"jitsi-meet-logger":51}],33:[function(require,module,exports){ +},{"./SDPUtil":34,"jitsi-meet-logger":50}],33:[function(require,module,exports){ var SDPUtil = require("./SDPUtil"); function SDPDiffer(mySDP, otherSDP) @@ -11243,7 +11201,7 @@ SDPUtil = { module.exports = SDPUtil; }).call(this,"/modules/xmpp/SDPUtil.js") -},{"../RTC/RTCBrowserType":17,"jitsi-meet-logger":51}],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'); @@ -11254,6 +11212,7 @@ var XMPPEvents = require("../../service/xmpp/XMPPEvents"); function TraceablePeerConnection(ice_config, constraints, session) { var self = this; this.session = session; + this.recvOnlySSRCs = {}; var RTCPeerConnectionType = null; if (RTCBrowserType.isFirefox()) { RTCPeerConnectionType = mozRTCPeerConnection; @@ -11385,7 +11344,12 @@ var dumpSDP = function(description) { return 'type: ' + description.type + '\r\n' + description.sdp; }; -var insertRecvOnlySSRC = function (desc) { +/** + * Injects receive only SSRC in the sdp if there are not other SSRCs. + * @param desc the SDP that will be modified. + * @returns the modified SDP. + */ +TraceablePeerConnection.prototype.insertRecvOnlySSRC = function (desc) { if (typeof desc !== 'object' || desc === null || typeof desc.sdp !== 'string') { console.warn('An empty description was passed as an argument.'); @@ -11411,14 +11375,16 @@ var insertRecvOnlySSRC = function (desc) { modded = true; if (!Array.isArray(bLine.ssrcs) || bLine.ssrcs.length === 0) { - var ssrc = RandomUtil.randomInt(1, 0xffffffff); + var ssrc = this.recvOnlySSRCs[bLine.type] + = this.recvOnlySSRCs[bLine.type] || + RandomUtil.randomInt(1, 0xffffffff); bLine.ssrcs = [{ id: ssrc, attribute: 'cname', value: ['recvonly-', ssrc].join('') }]; } - }); + }.bind(this)); return (!modded) ? desc : new RTCSessionDescription({ type: desc.type, @@ -11656,8 +11622,9 @@ TraceablePeerConnection.prototype.createOffer if (RTCBrowserType.isChrome()) { - offer = insertRecvOnlySSRC(offer); - self.trace('createOfferOnSuccess::mungeLocalVideoSSRC', dumpSDP(offer)); + offer = self.insertRecvOnlySSRC(offer); + self.trace('createAnswerOnSuccess::mungeLocalVideoSSRC', + dumpSDP(offer)); } if (!self.session.room.options.disableSimulcast @@ -11694,8 +11661,9 @@ TraceablePeerConnection.prototype.createAnswer if (RTCBrowserType.isChrome()) { - answer = insertRecvOnlySSRC(answer); - self.trace('createAnswerOnSuccess::mungeLocalVideoSSRC', dumpSDP(answer)); + answer = self.insertRecvOnlySSRC(answer); + self.trace('createAnswerOnSuccess::mungeLocalVideoSSRC', + dumpSDP(answer)); } if (!self.session.room.options.disableSimulcast @@ -11750,7 +11718,7 @@ TraceablePeerConnection.prototype.getStats = function(callback, errback) { module.exports = TraceablePeerConnection; }).call(this,"/modules/xmpp/TraceablePeerConnection.js") -},{"../../service/xmpp/XMPPEvents":141,"../RTC/RTC":16,"../RTC/RTCBrowserType.js":17,"../util/RandomUtil":26,"jitsi-meet-logger":51,"sdp-interop":70,"sdp-simulcast":77,"sdp-transform":84}],36:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"../RTC/RTC":16,"../RTC/RTCBrowserType.js":17,"../util/RandomUtil":26,"jitsi-meet-logger":50,"sdp-interop":69,"sdp-simulcast":76,"sdp-transform":83}],36:[function(require,module,exports){ (function (__filename){ /* global $, $iq, Promise, Strophe */ @@ -12206,7 +12174,7 @@ Moderator.prototype.logout = function (callback) { module.exports = Moderator; }).call(this,"/modules/xmpp/moderator.js") -},{"../../service/authentication/AuthenticationEvents":137,"../../service/xmpp/XMPPEvents":141,"jitsi-meet-logger":51}],37:[function(require,module,exports){ +},{"../../service/authentication/AuthenticationEvents":139,"../../service/xmpp/XMPPEvents":143,"jitsi-meet-logger":50}],37:[function(require,module,exports){ (function (__filename){ /* global $, $iq, config, connection, focusMucJid, messageHandler, Toolbar, Util, Promise */ @@ -12433,7 +12401,7 @@ Recording.prototype.getURL = function () { module.exports = Recording; }).call(this,"/modules/xmpp/recording.js") -},{"../../service/xmpp/XMPPEvents":141,"jitsi-meet-logger":51}],38:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"jitsi-meet-logger":50}],38:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ /* a simple MUC connection plugin @@ -12546,7 +12514,7 @@ module.exports = function(XMPP) { }; }).call(this,"/modules/xmpp/strophe.emuc.js") -},{"./ChatRoom":29,"jitsi-meet-logger":51}],39:[function(require,module,exports){ +},{"./ChatRoom":29,"jitsi-meet-logger":50}],39:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ @@ -12843,7 +12811,7 @@ module.exports = function(XMPP, eventEmitter) { }).call(this,"/modules/xmpp/strophe.jingle.js") -},{"../../service/xmpp/XMPPEvents":141,"../RTC/RTCBrowserType":17,"./JingleSessionPC":31,"jitsi-meet-logger":51}],40:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"../RTC/RTCBrowserType":17,"./JingleSessionPC":31,"jitsi-meet-logger":50}],40:[function(require,module,exports){ /* global Strophe */ module.exports = function () { @@ -12991,7 +12959,7 @@ module.exports = function (XMPP, eventEmitter) { }; }).call(this,"/modules/xmpp/strophe.ping.js") -},{"../../service/xmpp/XMPPEvents":141,"jitsi-meet-logger":51}],42:[function(require,module,exports){ +},{"../../service/xmpp/XMPPEvents":143,"jitsi-meet-logger":50}],42:[function(require,module,exports){ (function (__filename){ /* jshint -W117 */ var logger = require("jitsi-meet-logger").getLogger(__filename); @@ -13108,7 +13076,7 @@ module.exports = function() { }; }).call(this,"/modules/xmpp/strophe.rayo.js") -},{"jitsi-meet-logger":51}],43:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],43:[function(require,module,exports){ (function (__filename){ /* global Strophe */ /** @@ -13157,7 +13125,7 @@ module.exports = function () { }; }).call(this,"/modules/xmpp/strophe.util.js") -},{"jitsi-meet-logger":51}],44:[function(require,module,exports){ +},{"jitsi-meet-logger":50}],44:[function(require,module,exports){ (function (__filename){ /* global $, APP, config, Strophe */ @@ -13501,7 +13469,7 @@ XMPP.prototype.disconnect = function (ev) { module.exports = XMPP; }).call(this,"/modules/xmpp/xmpp.js") -},{"../../JitsiConnectionErrors":5,"../../JitsiConnectionEvents":6,"../../service/RTC/RTCEvents":135,"../../service/xmpp/XMPPEvents":141,"../RTC/RTC":16,"./strophe.emuc":38,"./strophe.jingle":39,"./strophe.logger":40,"./strophe.ping":41,"./strophe.rayo":42,"./strophe.util":43,"events":47,"jitsi-meet-logger":51,"pako":53}],45:[function(require,module,exports){ +},{"../../JitsiConnectionErrors":5,"../../JitsiConnectionEvents":6,"../../service/RTC/RTCEvents":137,"../../service/xmpp/XMPPEvents":143,"../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 @@ -14628,9 +14596,7 @@ module.exports = XMPP; }()); }).call(this,require('_process')) -},{"_process":48}],46:[function(require,module,exports){ - -},{}],47:[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 @@ -14933,7 +14899,7 @@ function isUndefined(arg) { return arg === void 0; } -},{}],48:[function(require,module,exports){ +},{}],47:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; @@ -15026,7 +14992,7 @@ process.chdir = function (dir) { }; process.umask = function() { return 0; }; -},{}],49:[function(require,module,exports){ +},{}],48:[function(require,module,exports){ (function (process,global){ /*! * @overview es6-promise - a tiny implementation of Promises/A+. @@ -15997,7 +15963,7 @@ process.umask = function() { return 0; }; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"_process":48}],50:[function(require,module,exports){ +},{"_process":47}],49:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16141,7 +16107,7 @@ Logger.levels = { ERROR: "error" }; -},{}],51:[function(require,module,exports){ +},{}],50:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16226,7 +16192,7 @@ module.exports = { levels: Logger.levels }; -},{"./Logger":50}],52:[function(require,module,exports){ +},{"./Logger":49}],51:[function(require,module,exports){ /* A JavaScript implementation of the SHA family of hashes, as defined in FIPS PUB 180-2 as well as the corresponding HMAC implementation @@ -16262,7 +16228,7 @@ d[2]),new e(355462360,d[3]),new e(1731405415,d[4]),new e(41048885895,d[5]),new e new e(a[m*x+p],a[m*x+p+1]):y(E(A[m-2]),A[m-7],D(A[m-15]),A[m-16]),v=C(u,G(l),H(l,r,t),k[m],A[m]),z=q(F(c),I(c,g,f)),u=t,t=r,r=l,l=q(h,v),h=f,f=g,g=c,c=q(v,z);d[0]=q(c,d[0]);d[1]=q(g,d[1]);d[2]=q(f,d[2]);d[3]=q(h,d[3]);d[4]=q(l,d[4]);d[5]=q(r,d[5]);d[6]=q(t,d[6]);d[7]=q(u,d[7])}if("SHA-224"===b)a=[d[0],d[1],d[2],d[3],d[4],d[5],d[6]];else if("SHA-256"===b)a=d;else if("SHA-384"===b)a=[d[0].a,d[0].b,d[1].a,d[1].b,d[2].a,d[2].b,d[3].a,d[3].b,d[4].a,d[4].b,d[5].a,d[5].b];else if("SHA-512"===b)a=[d[0].a, d[0].b,d[1].a,d[1].b,d[2].a,d[2].b,d[3].a,d[3].b,d[4].a,d[4].b,d[5].a,d[5].b,d[6].a,d[6].b,d[7].a,d[7].b];else throw"Unexpected error in SHA-2 implementation";return a}"function"===typeof define&&typeof define.amd?define(function(){return z}):"undefined"!==typeof exports?"undefined"!==typeof module&&module.exports?module.exports=exports=z:exports=z:T.jsSHA=z})(this); -},{}],53:[function(require,module,exports){ +},{}],52:[function(require,module,exports){ // Top level file is just a mixin of submodules & constants 'use strict'; @@ -16278,7 +16244,7 @@ assign(pako, deflate, inflate, constants); module.exports = pako; -},{"./lib/deflate":54,"./lib/inflate":55,"./lib/utils/common":56,"./lib/zlib/constants":59}],54:[function(require,module,exports){ +},{"./lib/deflate":53,"./lib/inflate":54,"./lib/utils/common":55,"./lib/zlib/constants":58}],53:[function(require,module,exports){ 'use strict'; @@ -16656,7 +16622,7 @@ exports.deflate = deflate; exports.deflateRaw = deflateRaw; exports.gzip = gzip; -},{"./utils/common":56,"./utils/strings":57,"./zlib/deflate.js":61,"./zlib/messages":66,"./zlib/zstream":68}],55:[function(require,module,exports){ +},{"./utils/common":55,"./utils/strings":56,"./zlib/deflate.js":60,"./zlib/messages":65,"./zlib/zstream":67}],54:[function(require,module,exports){ 'use strict'; @@ -17058,7 +17024,7 @@ exports.inflate = inflate; exports.inflateRaw = inflateRaw; exports.ungzip = inflate; -},{"./utils/common":56,"./utils/strings":57,"./zlib/constants":59,"./zlib/gzheader":62,"./zlib/inflate.js":64,"./zlib/messages":66,"./zlib/zstream":68}],56:[function(require,module,exports){ +},{"./utils/common":55,"./utils/strings":56,"./zlib/constants":58,"./zlib/gzheader":61,"./zlib/inflate.js":63,"./zlib/messages":65,"./zlib/zstream":67}],55:[function(require,module,exports){ 'use strict'; @@ -17162,7 +17128,7 @@ exports.setTyped = function (on) { exports.setTyped(TYPED_OK); -},{}],57:[function(require,module,exports){ +},{}],56:[function(require,module,exports){ // String encode/decode helpers 'use strict'; @@ -17349,7 +17315,7 @@ exports.utf8border = function(buf, max) { return (pos + _utf8len[buf[pos]] > max) ? pos : max; }; -},{"./common":56}],58:[function(require,module,exports){ +},{"./common":55}],57:[function(require,module,exports){ 'use strict'; // Note: adler32 takes 12% for level 0 and 2% for level 6. @@ -17383,7 +17349,7 @@ function adler32(adler, buf, len, pos) { module.exports = adler32; -},{}],59:[function(require,module,exports){ +},{}],58:[function(require,module,exports){ module.exports = { /* Allowed flush values; see deflate() and inflate() below for details */ @@ -17432,7 +17398,7 @@ module.exports = { //Z_NULL: null // Use -1 or null inline, depending on var type }; -},{}],60:[function(require,module,exports){ +},{}],59:[function(require,module,exports){ 'use strict'; // Note: we can't get significant speed boost here. @@ -17475,7 +17441,7 @@ function crc32(crc, buf, len, pos) { module.exports = crc32; -},{}],61:[function(require,module,exports){ +},{}],60:[function(require,module,exports){ 'use strict'; var utils = require('../utils/common'); @@ -19242,7 +19208,7 @@ exports.deflatePrime = deflatePrime; exports.deflateTune = deflateTune; */ -},{"../utils/common":56,"./adler32":58,"./crc32":60,"./messages":66,"./trees":67}],62:[function(require,module,exports){ +},{"../utils/common":55,"./adler32":57,"./crc32":59,"./messages":65,"./trees":66}],61:[function(require,module,exports){ 'use strict'; @@ -19284,7 +19250,7 @@ function GZheader() { module.exports = GZheader; -},{}],63:[function(require,module,exports){ +},{}],62:[function(require,module,exports){ 'use strict'; // See state defs from inflate.js @@ -19612,7 +19578,7 @@ module.exports = function inflate_fast(strm, start) { return; }; -},{}],64:[function(require,module,exports){ +},{}],63:[function(require,module,exports){ 'use strict'; @@ -21117,7 +21083,7 @@ exports.inflateSyncPoint = inflateSyncPoint; exports.inflateUndermine = inflateUndermine; */ -},{"../utils/common":56,"./adler32":58,"./crc32":60,"./inffast":63,"./inftrees":65}],65:[function(require,module,exports){ +},{"../utils/common":55,"./adler32":57,"./crc32":59,"./inffast":62,"./inftrees":64}],64:[function(require,module,exports){ 'use strict'; @@ -21446,7 +21412,7 @@ module.exports = function inflate_table(type, lens, lens_index, codes, table, ta return 0; }; -},{"../utils/common":56}],66:[function(require,module,exports){ +},{"../utils/common":55}],65:[function(require,module,exports){ 'use strict'; module.exports = { @@ -21461,7 +21427,7 @@ module.exports = { '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */ }; -},{}],67:[function(require,module,exports){ +},{}],66:[function(require,module,exports){ 'use strict'; @@ -22662,7 +22628,7 @@ exports._tr_flush_block = _tr_flush_block; exports._tr_tally = _tr_tally; exports._tr_align = _tr_align; -},{"../utils/common":56}],68:[function(require,module,exports){ +},{"../utils/common":55}],67:[function(require,module,exports){ 'use strict'; @@ -22693,7 +22659,7 @@ function ZStream() { module.exports = ZStream; -},{}],69:[function(require,module,exports){ +},{}],68:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22734,7 +22700,7 @@ module.exports = function arrayEquals(array) { }; -},{}],70:[function(require,module,exports){ +},{}],69:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22752,7 +22718,7 @@ module.exports = function arrayEquals(array) { exports.Interop = require('./interop'); -},{"./interop":71}],71:[function(require,module,exports){ +},{"./interop":70}],70:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23475,7 +23441,7 @@ Interop.prototype.toUnifiedPlan = function(desc) { //#endregion }; -},{"./array-equals":69,"./transform":72}],72:[function(require,module,exports){ +},{"./array-equals":68,"./transform":71}],71:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23589,7 +23555,7 @@ exports.parse = function(sdp) { }; -},{"sdp-transform":74}],73:[function(require,module,exports){ +},{"sdp-transform":73}],72:[function(require,module,exports){ var grammar = module.exports = { v: [{ name: 'version', @@ -23848,7 +23814,7 @@ Object.keys(grammar).forEach(function (key) { }); }); -},{}],74:[function(require,module,exports){ +},{}],73:[function(require,module,exports){ var parser = require('./parser'); var writer = require('./writer'); @@ -23858,7 +23824,7 @@ exports.parseFmtpConfig = parser.parseFmtpConfig; exports.parsePayloads = parser.parsePayloads; exports.parseRemoteCandidates = parser.parseRemoteCandidates; -},{"./parser":75,"./writer":76}],75:[function(require,module,exports){ +},{"./parser":74,"./writer":75}],74:[function(require,module,exports){ var toIntIfInt = function (v) { return String(Number(v)) === v ? Number(v) : v; }; @@ -23953,7 +23919,7 @@ exports.parseRemoteCandidates = function (str) { return candidates; }; -},{"./grammar":73}],76:[function(require,module,exports){ +},{"./grammar":72}],75:[function(require,module,exports){ var grammar = require('./grammar'); // customized util.format - discards excess arguments and can void middle ones @@ -24069,7 +24035,7 @@ module.exports = function (session, opts) { return sdp.join('\r\n') + '\r\n'; }; -},{"./grammar":73}],77:[function(require,module,exports){ +},{"./grammar":72}],76:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24493,7 +24459,7 @@ Simulcast.prototype.mungeLocalDescription = function (desc) { module.exports = Simulcast; -},{"./transform-utils":78,"sdp-transform":80}],78:[function(require,module,exports){ +},{"./transform-utils":77,"sdp-transform":79}],77:[function(require,module,exports){ /* Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24559,23 +24525,27 @@ exports.parseSsrcs = function (mLine) { }; -},{}],79:[function(require,module,exports){ +},{}],78:[function(require,module,exports){ +arguments[4][72][0].apply(exports,arguments) +},{"dup":72}],79:[function(require,module,exports){ arguments[4][73][0].apply(exports,arguments) -},{"dup":73}],80:[function(require,module,exports){ +},{"./parser":80,"./writer":81,"dup":73}],80:[function(require,module,exports){ arguments[4][74][0].apply(exports,arguments) -},{"./parser":81,"./writer":82,"dup":74}],81:[function(require,module,exports){ +},{"./grammar":78,"dup":74}],81:[function(require,module,exports){ arguments[4][75][0].apply(exports,arguments) -},{"./grammar":79,"dup":75}],82:[function(require,module,exports){ -arguments[4][76][0].apply(exports,arguments) -},{"./grammar":79,"dup":76}],83:[function(require,module,exports){ +},{"./grammar":78,"dup":75}],82:[function(require,module,exports){ +arguments[4][72][0].apply(exports,arguments) +},{"dup":72}],83:[function(require,module,exports){ arguments[4][73][0].apply(exports,arguments) -},{"dup":73}],84:[function(require,module,exports){ +},{"./parser":84,"./writer":85,"dup":73}],84:[function(require,module,exports){ arguments[4][74][0].apply(exports,arguments) -},{"./parser":85,"./writer":86,"dup":74}],85:[function(require,module,exports){ +},{"./grammar":82,"dup":74}],85:[function(require,module,exports){ arguments[4][75][0].apply(exports,arguments) -},{"./grammar":83,"dup":75}],86:[function(require,module,exports){ -arguments[4][76][0].apply(exports,arguments) -},{"./grammar":83,"dup":76}],87:[function(require,module,exports){ +},{"./grammar":82,"dup":75}],86:[function(require,module,exports){ + +module.exports = require('./lib/'); + +},{"./lib/":87}],87:[function(require,module,exports){ /** * Module dependencies. @@ -24622,14 +24592,9 @@ function lookup(uri, opts) { var parsed = url(uri); var source = parsed.source; var id = parsed.id; - var path = parsed.path; - var sameNamespace = cache[id] && path in cache[id].nsps; - var newConnection = opts.forceNew || opts['force new connection'] || - false === opts.multiplex || sameNamespace; - var io; - if (newConnection) { + if (opts.forceNew || opts['force new connection'] || false === opts.multiplex) { debug('ignoring socket cache for %s', source); io = Manager(source, opts); } else { @@ -24669,28 +24634,24 @@ exports.connect = lookup; exports.Manager = require('./manager'); exports.Socket = require('./socket'); -},{"./manager":88,"./socket":90,"./url":91,"debug":95,"socket.io-parser":128}],88:[function(require,module,exports){ +},{"./manager":88,"./socket":90,"./url":91,"debug":95,"socket.io-parser":131}],88:[function(require,module,exports){ /** * Module dependencies. */ +var url = require('./url'); var eio = require('engine.io-client'); var Socket = require('./socket'); var Emitter = require('component-emitter'); var parser = require('socket.io-parser'); var on = require('./on'); var bind = require('component-bind'); +var object = require('object-component'); var debug = require('debug')('socket.io-client:manager'); var indexOf = require('indexof'); var Backoff = require('backo2'); -/** - * IE6+ hasOwnProperty - */ - -var has = Object.prototype.hasOwnProperty; - /** * Module exports */ @@ -24730,8 +24691,7 @@ function Manager(uri, opts){ this.timeout(null == opts.timeout ? 20000 : opts.timeout); this.readyState = 'closed'; this.uri = uri; - this.connecting = []; - this.lastPing = null; + this.connected = []; this.encoding = false; this.packetBuffer = []; this.encoder = new parser.Encoder(); @@ -24749,9 +24709,7 @@ function Manager(uri, opts){ Manager.prototype.emitAll = function() { this.emit.apply(this, arguments); for (var nsp in this.nsps) { - if (has.call(this.nsps, nsp)) { - this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); - } + this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); } }; @@ -24763,9 +24721,7 @@ Manager.prototype.emitAll = function() { Manager.prototype.updateSocketIds = function(){ for (var nsp in this.nsps) { - if (has.call(this.nsps, nsp)) { - this.nsps[nsp].id = this.engine.id; - } + this.nsps[nsp].id = this.engine.id; } }; @@ -24957,32 +24913,9 @@ Manager.prototype.onopen = function(){ // add new subs var socket = this.engine; this.subs.push(on(socket, 'data', bind(this, 'ondata'))); - this.subs.push(on(socket, 'ping', bind(this, 'onping'))); - this.subs.push(on(socket, 'pong', bind(this, 'onpong'))); + this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); this.subs.push(on(socket, 'error', bind(this, 'onerror'))); this.subs.push(on(socket, 'close', bind(this, 'onclose'))); - this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); -}; - -/** - * Called upon a ping. - * - * @api private - */ - -Manager.prototype.onping = function(){ - this.lastPing = new Date; - this.emitAll('ping'); -}; - -/** - * Called upon a packet. - * - * @api private - */ - -Manager.prototype.onpong = function(){ - this.emitAll('pong', new Date - this.lastPing); }; /** @@ -25029,23 +24962,13 @@ Manager.prototype.socket = function(nsp){ socket = new Socket(this, nsp); this.nsps[nsp] = socket; var self = this; - socket.on('connecting', onConnecting); socket.on('connect', function(){ socket.id = self.engine.id; + if (!~indexOf(self.connected, socket)) { + self.connected.push(socket); + } }); - - if (this.autoConnect) { - // manually call here since connecting evnet is fired before listening - onConnecting(); - } } - - function onConnecting() { - if (!~indexOf(self.connecting, socket)) { - self.connecting.push(socket); - } - } - return socket; }; @@ -25056,9 +24979,9 @@ Manager.prototype.socket = function(nsp){ */ Manager.prototype.destroy = function(socket){ - var index = indexOf(this.connecting, socket); - if (~index) this.connecting.splice(index, 1); - if (this.connecting.length) return; + var index = indexOf(this.connected, socket); + if (~index) this.connected.splice(index, 1); + if (this.connected.length) return; this.close(); }; @@ -25079,7 +25002,7 @@ Manager.prototype.packet = function(packet){ self.encoding = true; this.encoder.encode(packet, function(encodedPackets) { for (var i = 0; i < encodedPackets.length; i++) { - self.engine.write(encodedPackets[i], packet.options); + self.engine.write(encodedPackets[i]); } self.encoding = false; self.processPacketQueue(); @@ -25110,14 +25033,11 @@ Manager.prototype.processPacketQueue = function() { */ Manager.prototype.cleanup = function(){ - debug('cleanup'); - var sub; while (sub = this.subs.shift()) sub.destroy(); this.packetBuffer = []; this.encoding = false; - this.lastPing = null; this.decoder.destroy(); }; @@ -25130,17 +25050,10 @@ Manager.prototype.cleanup = function(){ Manager.prototype.close = Manager.prototype.disconnect = function(){ - debug('disconnect'); this.skipReconnect = true; - this.reconnecting = false; - if ('opening' == this.readyState) { - // `onclose` will not fire because - // an open event never happened - this.cleanup(); - } this.backoff.reset(); this.readyState = 'closed'; - if (this.engine) this.engine.close(); + this.engine && this.engine.close(); }; /** @@ -25150,13 +25063,11 @@ Manager.prototype.disconnect = function(){ */ Manager.prototype.onclose = function(reason){ - debug('onclose'); - + debug('close'); this.cleanup(); this.backoff.reset(); this.readyState = 'closed'; this.emit('close', reason); - if (this._reconnection && !this.skipReconnect) { this.reconnect(); } @@ -25228,7 +25139,7 @@ Manager.prototype.onreconnect = function(){ this.emitAll('reconnect', attempt); }; -},{"./on":89,"./socket":90,"backo2":92,"component-bind":93,"component-emitter":94,"debug":95,"engine.io-client":98,"indexof":125,"socket.io-parser":128}],89:[function(require,module,exports){ +},{"./on":89,"./socket":90,"./url":91,"backo2":92,"component-bind":93,"component-emitter":94,"debug":95,"engine.io-client":96,"indexof":127,"object-component":128,"socket.io-parser":131}],89:[function(require,module,exports){ /** * Module exports. @@ -25285,16 +25196,13 @@ var events = { connect: 1, connect_error: 1, connect_timeout: 1, - connecting: 1, disconnect: 1, error: 1, reconnect: 1, reconnect_attempt: 1, reconnect_failed: 1, reconnect_error: 1, - reconnecting: 1, - ping: 1, - pong: 1 + reconnecting: 1 }; /** @@ -25315,11 +25223,11 @@ function Socket(io, nsp){ this.json = this; // compat this.ids = 0; this.acks = {}; + if (this.io.autoConnect) this.open(); this.receiveBuffer = []; this.sendBuffer = []; this.connected = false; this.disconnected = true; - if (this.io.autoConnect) this.open(); } /** @@ -25358,7 +25266,6 @@ Socket.prototype.connect = function(){ this.subEvents(); this.io.open(); // ensure open if ('open' == this.io.readyState) this.onopen(); - this.emit('connecting'); return this; }; @@ -25396,9 +25303,6 @@ Socket.prototype.emit = function(ev){ if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary var packet = { type: parserType, data: args }; - packet.options = {}; - packet.options.compress = !this.flags || false !== this.flags.compress; - // event ack callback if ('function' == typeof args[args.length - 1]) { debug('emitting packet with ack id %d', this.ids); @@ -25412,8 +25316,6 @@ Socket.prototype.emit = function(ev){ this.sendBuffer.push(packet); } - delete this.flags; - return this; }; @@ -25556,14 +25458,10 @@ Socket.prototype.ack = function(id){ */ Socket.prototype.onack = function(packet){ - var ack = this.acks[packet.id]; - if ('function' == typeof ack) { - debug('calling ack %s with %j', packet.id, packet.data); - ack.apply(this, packet.data); - delete this.acks[packet.id]; - } else { - debug('bad ack %s', packet.id); - } + debug('calling ack %s with %j', packet.id, packet.data); + var fn = this.acks[packet.id]; + fn.apply(this, packet.data); + delete this.acks[packet.id]; }; /** @@ -25654,21 +25552,7 @@ Socket.prototype.disconnect = function(){ return this; }; -/** - * Sets the compress flag. - * - * @param {Boolean} if `true`, compresses the sending data - * @return {Socket} self - * @api public - */ - -Socket.prototype.compress = function(compress){ - this.flags = this.flags || {}; - this.flags.compress = compress; - return this; -}; - -},{"./on":89,"component-bind":93,"component-emitter":94,"debug":95,"has-binary":123,"socket.io-parser":128,"to-array":133}],91:[function(require,module,exports){ +},{"./on":89,"component-bind":93,"component-emitter":94,"debug":95,"has-binary":125,"socket.io-parser":131,"to-array":135}],91:[function(require,module,exports){ (function (global){ /** @@ -25706,7 +25590,7 @@ function url(uri, loc){ if ('/' == uri.charAt(1)) { uri = loc.protocol + uri; } else { - uri = loc.host + uri; + uri = loc.hostname + uri; } } @@ -25736,19 +25620,16 @@ function url(uri, loc){ obj.path = obj.path || '/'; - var ipv6 = obj.host.indexOf(':') !== -1; - var host = ipv6 ? '[' + obj.host + ']' : obj.host; - // define unique id - obj.id = obj.protocol + '://' + host + ':' + obj.port; + obj.id = obj.protocol + '://' + obj.host + ':' + obj.port; // define href - obj.href = obj.protocol + '://' + host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); + obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); return obj; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"debug":95,"parseuri":126}],92:[function(require,module,exports){ +},{"debug":95,"parseuri":129}],92:[function(require,module,exports){ /** * Expose `Backoff`. @@ -25905,7 +25786,7 @@ function mixin(obj) { Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; - (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + (this._callbacks[event] = this._callbacks[event] || []) .push(fn); return this; }; @@ -25921,8 +25802,11 @@ Emitter.prototype.addEventListener = function(event, fn){ */ Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; + function on() { - this.off(event, on); + self.off(event, on); fn.apply(this, arguments); } @@ -25954,12 +25838,12 @@ Emitter.prototype.removeEventListener = function(event, fn){ } // specific event - var callbacks = this._callbacks['$' + event]; + var callbacks = this._callbacks[event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { - delete this._callbacks['$' + event]; + delete this._callbacks[event]; return this; } @@ -25986,7 +25870,7 @@ Emitter.prototype.removeEventListener = function(event, fn){ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks['$' + event]; + , callbacks = this._callbacks[event]; if (callbacks) { callbacks = callbacks.slice(0); @@ -26008,7 +25892,7 @@ Emitter.prototype.emit = function(event){ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; - return this._callbacks['$' + event] || []; + return this._callbacks[event] || []; }; /** @@ -26026,325 +25910,75 @@ Emitter.prototype.hasListeners = function(event){ },{}],95:[function(require,module,exports){ /** - * This is the web browser implementation of `debug()`. - * * Expose `debug()` as the module. */ -exports = module.exports = require('./debug'); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); +module.exports = debug; /** - * Colors. - */ - -exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // is webkit? http://stackoverflow.com/a/16459606/376773 - return ('WebkitAppearance' in document.documentElement.style) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (window.console && (console.firebug || (console.exception && console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - return JSON.stringify(v); -}; - - -/** - * Colorize log arguments if enabled. + * Create a debugger with the given `name`. * + * @param {String} name + * @return {Type} * @api public */ -function formatArgs() { - var args = arguments; - var useColors = this.useColors; +function debug(name) { + if (!debug.enabled(name)) return function(){}; - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); + return function(fmt){ + fmt = coerce(fmt); - if (!useColors) return args; + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; - var c = 'color: ' + this.color; - args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); - return args; -} - -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - return r; -} - -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ - -exports.enable(load()); - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - -function localstorage(){ - try { - return window.localStorage; - } catch (e) {} -} - -},{"./debug":96}],96:[function(require,module,exports){ - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = debug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = require('ms'); - -/** - * The currently active debug mode names, and names to skip. - */ - -exports.names = []; -exports.skips = []; - -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lowercased letter, i.e. "n". - */ - -exports.formatters = {}; - -/** - * Previously assigned color. - */ - -var prevColor = 0; - -/** - * Previous log timestamp. - */ - -var prevTime; - -/** - * Select a color. - * - * @return {Number} - * @api private - */ - -function selectColor() { - return exports.colors[prevColor++ % exports.colors.length]; -} - -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - -function debug(namespace) { - - // define the `disabled` version - function disabled() { + // 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); } - disabled.enabled = false; - - // define the `enabled` version - function enabled() { - - var self = enabled; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // add the `color` if not set - if (null == self.useColors) self.useColors = exports.useColors(); - if (null == self.color && self.useColors) self.color = selectColor(); - - var args = Array.prototype.slice.call(arguments); - - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %o - args = ['%o'].concat(args); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - if ('function' === typeof exports.formatArgs) { - args = exports.formatArgs.apply(self, args); - } - var logFn = enabled.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } - enabled.enabled = true; - - var fn = exports.enabled(namespace) ? enabled : disabled; - - fn.namespace = namespace; - - return fn; } /** - * Enables a debug mode by namespaces. This can include modes + * 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} namespaces + * @param {String} name * @api public */ -function enable(namespaces) { - exports.save(namespaces); +debug.enable = function(name) { + try { + localStorage.debug = name; + } catch(e){} - var split = (namespaces || '').split(/[\s,]+/); - var len = split.length; + var split = (name || '').split(/[\s,]+/) + , len = split.length; for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); + 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. @@ -26352,9 +25986,28 @@ function enable(namespaces) { * @api public */ -function disable() { - exports.enable(''); -} +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. @@ -26364,27 +26017,22 @@ function disable() { * @api public */ -function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { return false; } } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { return true; } } return false; -} +}; /** * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private */ function coerce(val) { @@ -26392,138 +26040,17 @@ function coerce(val) { return val; } -},{"ms":97}],97:[function(require,module,exports){ -/** - * Helpers. - */ +// persist -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; +try { + if (window.localStorage) debug.enable(localStorage.debug); +} catch(e){} -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} options - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options){ - options = options || {}; - if ('string' == typeof val) return parse(val); - return options.long - ? long(val) - : short(val); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = '' + str; - if (str.length > 10000) return; - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); - if (!match) return; - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function short(ms) { - if (ms >= d) return Math.round(ms / d) + 'd'; - if (ms >= h) return Math.round(ms / h) + 'h'; - if (ms >= m) return Math.round(ms / m) + 'm'; - if (ms >= s) return Math.round(ms / s) + 's'; - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function long(ms) { - return plural(ms, d, 'day') - || plural(ms, h, 'hour') - || plural(ms, m, 'minute') - || plural(ms, s, 'second') - || ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, n, name) { - if (ms < n) return; - if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; - return Math.ceil(ms / n) + ' ' + name + 's'; -} - -},{}],98:[function(require,module,exports){ +},{}],96:[function(require,module,exports){ module.exports = require('./lib/'); -},{"./lib/":99}],99:[function(require,module,exports){ +},{"./lib/":97}],97:[function(require,module,exports){ module.exports = require('./socket'); @@ -26535,7 +26062,7 @@ module.exports = require('./socket'); */ module.exports.parser = require('engine.io-parser'); -},{"./socket":100,"engine.io-parser":110}],100:[function(require,module,exports){ +},{"./socket":98,"engine.io-parser":110}],98:[function(require,module,exports){ (function (global){ /** * Module dependencies. @@ -26584,20 +26111,24 @@ function Socket(uri, opts){ if (uri) { uri = parseuri(uri); - opts.hostname = uri.host; + opts.host = uri.host; opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; opts.port = uri.port; if (uri.query) opts.query = uri.query; - } else if (opts.host) { - opts.hostname = parseuri(opts.host).host; } this.secure = null != opts.secure ? opts.secure : (global.location && 'https:' == location.protocol); - if (opts.hostname && !opts.port) { - // if no port is specified manually, use the protocol default - opts.port = this.secure ? '443' : '80'; + 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; @@ -26619,16 +26150,11 @@ function Socket(uri, opts){ 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; - this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; - - if (true === this.perMessageDeflate) this.perMessageDeflate = {}; - if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { - this.perMessageDeflate.threshold = 1024; - } // SSL options for Node.js client this.pfx = opts.pfx || null; @@ -26637,15 +26163,7 @@ function Socket(uri, opts){ this.cert = opts.cert || null; this.ca = opts.ca || null; this.ciphers = opts.ciphers || null; - this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? null : opts.rejectUnauthorized; - - // other options for Node.js client - var freeGlobal = typeof global == 'object' && global; - if (freeGlobal.global === freeGlobal) { - if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { - this.extraHeaders = opts.extraHeaders; - } - } + this.rejectUnauthorized = opts.rejectUnauthorized || null; this.open(); } @@ -26718,9 +26236,7 @@ Socket.prototype.createTransport = function (name) { cert: this.cert, ca: this.ca, ciphers: this.ciphers, - rejectUnauthorized: this.rejectUnauthorized, - perMessageDeflate: this.perMessageDeflate, - extraHeaders: this.extraHeaders + rejectUnauthorized: this.rejectUnauthorized }); return transport; @@ -26745,7 +26261,7 @@ Socket.prototype.open = function () { var transport; if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { transport = 'websocket'; - } else if (0 === this.transports.length) { + } else if (0 == this.transports.length) { // Emit error on next tick so it can be listened to var self = this; setTimeout(function() { @@ -26758,6 +26274,7 @@ Socket.prototype.open = function () { this.readyState = 'opening'; // Retry with the next transport if the transport is disabled (jsonp: false) + var transport; try { transport = this.createTransport(transport); } catch (e) { @@ -26967,13 +26484,12 @@ Socket.prototype.onPacket = function (packet) { case 'pong': this.setPing(); - this.emit('pong'); break; case 'error': var err = new Error('server error'); err.code = packet.data; - this.onError(err); + this.emit('error', err); break; case 'message': @@ -27045,14 +26561,11 @@ Socket.prototype.setPing = function () { /** * Sends a ping packet. * -* @api private +* @api public */ Socket.prototype.ping = function () { - var self = this; - this.sendPacket('ping', function(){ - self.emit('ping'); - }); + this.sendPacket('ping'); }; /** @@ -27062,14 +26575,21 @@ Socket.prototype.ping = function () { */ 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 (0 === this.writeBuffer.length) { + if (this.writeBuffer.length == 0) { this.emit('drain'); } else { this.flush(); @@ -27099,14 +26619,13 @@ Socket.prototype.flush = function () { * * @param {String} message. * @param {Function} callback function. - * @param {Object} options. * @return {Socket} for chaining. * @api public */ Socket.prototype.write = -Socket.prototype.send = function (msg, options, fn) { - this.sendPacket('message', msg, options, fn); +Socket.prototype.send = function (msg, fn) { + this.sendPacket('message', msg, fn); return this; }; @@ -27115,37 +26634,19 @@ Socket.prototype.send = function (msg, options, fn) { * * @param {String} packet type. * @param {String} data. - * @param {Object} options. * @param {Function} callback function. * @api private */ -Socket.prototype.sendPacket = function (type, data, options, fn) { - if('function' == typeof data) { - fn = data; - data = undefined; - } - - if ('function' == typeof options) { - fn = options; - options = null; - } - +Socket.prototype.sendPacket = function (type, data, fn) { if ('closing' == this.readyState || 'closed' == this.readyState) { return; } - options = options || {}; - options.compress = false !== options.compress; - - var packet = { - type: type, - data: data, - options: options - }; + var packet = { type: type, data: data }; this.emit('packetCreate', packet); this.writeBuffer.push(packet); - if (fn) this.once('flush', fn); + this.callbackBuffer.push(fn); this.flush(); }; @@ -27161,6 +26662,24 @@ Socket.prototype.close = function () { 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) { @@ -27176,24 +26695,6 @@ Socket.prototype.close = function () { } } - 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); - } - return this; }; @@ -27225,6 +26726,14 @@ Socket.prototype.onClose = function (reason, desc) { 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'); @@ -27242,11 +26751,6 @@ Socket.prototype.onClose = function (reason, desc) { // emit close event this.emit('close', reason, desc); - - // clean buffers after, so users can still - // grab the buffers on `close` event - self.writeBuffer = []; - self.prevBufferLen = 0; } }; @@ -27267,7 +26771,7 @@ Socket.prototype.filterUpgrades = function (upgrades) { }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./transport":101,"./transports":102,"component-emitter":108,"debug":95,"engine.io-parser":110,"indexof":125,"parsejson":120,"parseqs":121,"parseuri":126}],101:[function(require,module,exports){ +},{"./transport":99,"./transports":100,"component-emitter":94,"debug":107,"engine.io-parser":110,"indexof":127,"parsejson":121,"parseqs":122,"parseuri":123}],99:[function(require,module,exports){ /** * Module dependencies. */ @@ -27309,9 +26813,6 @@ function Transport (opts) { this.ca = opts.ca; this.ciphers = opts.ciphers; this.rejectUnauthorized = opts.rejectUnauthorized; - - // other options for Node.js client - this.extraHeaders = opts.extraHeaders; } /** @@ -27320,6 +26821,13 @@ function Transport (opts) { Emitter(Transport.prototype); +/** + * A counter used to prevent collisions in the timestamps used + * for cache busting. + */ + +Transport.timestamps = 0; + /** * Emits an error. * @@ -27424,13 +26932,13 @@ Transport.prototype.onClose = function () { this.emit('close'); }; -},{"component-emitter":108,"engine.io-parser":110}],102:[function(require,module,exports){ +},{"component-emitter":94,"engine.io-parser":110}],100:[function(require,module,exports){ (function (global){ /** * Module dependencies */ -var XMLHttpRequest = require('xmlhttprequest-ssl'); +var XMLHttpRequest = require('xmlhttprequest'); var XHR = require('./polling-xhr'); var JSONP = require('./polling-jsonp'); var websocket = require('./websocket'); @@ -27481,7 +26989,7 @@ function polling(opts){ } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./polling-jsonp":103,"./polling-xhr":104,"./websocket":106,"xmlhttprequest-ssl":107}],103:[function(require,module,exports){ +},{"./polling-jsonp":101,"./polling-xhr":102,"./websocket":104,"xmlhttprequest":105}],101:[function(require,module,exports){ (function (global){ /** @@ -27617,12 +27125,7 @@ JSONPPolling.prototype.doPoll = function () { }; var insertAt = document.getElementsByTagName('script')[0]; - if (insertAt) { - insertAt.parentNode.insertBefore(script, insertAt); - } - else { - (document.head || document.body).appendChild(script); - } + insertAt.parentNode.insertBefore(script, insertAt); this.script = script; var isUAgecko = 'undefined' != typeof navigator && /gecko/i.test(navigator.userAgent); @@ -27723,13 +27226,13 @@ JSONPPolling.prototype.doWrite = function (data, fn) { }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./polling":105,"component-inherit":109}],104:[function(require,module,exports){ +},{"./polling":103,"component-inherit":106}],102:[function(require,module,exports){ (function (global){ /** * Module requirements. */ -var XMLHttpRequest = require('xmlhttprequest-ssl'); +var XMLHttpRequest = require('xmlhttprequest'); var Polling = require('./polling'); var Emitter = require('component-emitter'); var inherit = require('component-inherit'); @@ -27770,8 +27273,6 @@ function XHR(opts){ this.xd = opts.hostname != global.location.hostname || port != opts.port; this.xs = opts.secure != isSSL; - } else { - this.extraHeaders = opts.extraHeaders; } } @@ -27812,9 +27313,6 @@ XHR.prototype.request = function(opts){ opts.ciphers = this.ciphers; opts.rejectUnauthorized = this.rejectUnauthorized; - // other options for Node.js client - opts.extraHeaders = this.extraHeaders; - return new Request(opts); }; @@ -27884,9 +27382,6 @@ function Request(opts){ this.ciphers = opts.ciphers; this.rejectUnauthorized = opts.rejectUnauthorized; - // other options for Node.js client - this.extraHeaders = opts.extraHeaders; - this.create(); } @@ -27920,16 +27415,6 @@ Request.prototype.create = function(){ try { debug('xhr open %s: %s', this.method, this.uri); xhr.open(this.method, this.uri, this.async); - try { - if (this.extraHeaders) { - xhr.setDisableHeaderCheck(true); - for (var i in this.extraHeaders) { - if (this.extraHeaders.hasOwnProperty(i)) { - xhr.setRequestHeader(i, this.extraHeaders[i]); - } - } - } - } catch (e) {} if (this.supportsBinary) { // This has to be done after open because Firefox is stupid // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension @@ -28073,17 +27558,7 @@ Request.prototype.onLoad = function(){ if (!this.supportsBinary) { data = this.xhr.responseText; } else { - try { - data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response)); - } catch (e) { - var ui8Arr = new Uint8Array(this.xhr.response); - var dataArray = []; - for (var idx = 0, length = ui8Arr.length; idx < length; idx++) { - dataArray.push(ui8Arr[idx]); - } - - data = String.fromCharCode.apply(null, dataArray); - } + data = 'ok'; } } } catch (e) { @@ -28139,7 +27614,7 @@ function unloadHandler() { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./polling":105,"component-emitter":108,"component-inherit":109,"debug":95,"xmlhttprequest-ssl":107}],105:[function(require,module,exports){ +},{"./polling":103,"component-emitter":94,"component-inherit":106,"debug":107,"xmlhttprequest":105}],103:[function(require,module,exports){ /** * Module dependencies. */ @@ -28148,7 +27623,6 @@ var Transport = require('../transport'); var parseqs = require('parseqs'); var parser = require('engine.io-parser'); var inherit = require('component-inherit'); -var yeast = require('yeast'); var debug = require('debug')('engine.io-client:polling'); /** @@ -28162,7 +27636,7 @@ module.exports = Polling; */ var hasXHR2 = (function() { - var XMLHttpRequest = require('xmlhttprequest-ssl'); + var XMLHttpRequest = require('xmlhttprequest'); var xhr = new XMLHttpRequest({ xdomain: false }); return null != xhr.responseType; })(); @@ -28364,7 +27838,7 @@ Polling.prototype.uri = function(){ // cache busting is forced if (false !== this.timestampRequests) { - query[this.timestampParam] = yeast(); + query[this.timestampParam] = +new Date + '-' + Transport.timestamps++; } if (!this.supportsBinary && !query.sid) { @@ -28384,12 +27858,10 @@ Polling.prototype.uri = function(){ query = '?' + query; } - var ipv6 = this.hostname.indexOf(':') !== -1; - return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; + return schema + '://' + this.hostname + port + this.path + query; }; -},{"../transport":101,"component-inherit":109,"debug":95,"engine.io-parser":110,"parseqs":121,"xmlhttprequest-ssl":107,"yeast":122}],106:[function(require,module,exports){ -(function (global){ +},{"../transport":99,"component-inherit":106,"debug":107,"engine.io-parser":110,"parseqs":122,"xmlhttprequest":105}],104:[function(require,module,exports){ /** * Module dependencies. */ @@ -28398,17 +27870,15 @@ var Transport = require('../transport'); var parser = require('engine.io-parser'); var parseqs = require('parseqs'); var inherit = require('component-inherit'); -var yeast = require('yeast'); var debug = require('debug')('engine.io-client:websocket'); -var BrowserWebSocket = global.WebSocket || global.MozWebSocket; /** - * Get either the `WebSocket` or `MozWebSocket` globals - * in the browser or the WebSocket-compatible interface - * exposed by `ws` for Node environment. + * `ws` exposes a WebSocket-compatible interface in + * Node, or the `WebSocket` or `MozWebSocket` globals + * in the browser. */ -var WebSocket = BrowserWebSocket || (typeof window !== 'undefined' ? null : require('ws')); +var WebSocket = require('ws'); /** * Module exports. @@ -28428,7 +27898,6 @@ function WS(opts){ if (forceBase64) { this.supportsBinary = false; } - this.perMessageDeflate = opts.perMessageDeflate; Transport.call(this, opts); } @@ -28467,10 +27936,7 @@ WS.prototype.doOpen = function(){ var self = this; var uri = this.uri(); var protocols = void(0); - var opts = { - agent: this.agent, - perMessageDeflate: this.perMessageDeflate - }; + var opts = { agent: this.agent }; // SSL options for Node.js client opts.pfx = this.pfx; @@ -28480,23 +27946,14 @@ WS.prototype.doOpen = function(){ opts.ca = this.ca; opts.ciphers = this.ciphers; opts.rejectUnauthorized = this.rejectUnauthorized; - if (this.extraHeaders) { - opts.headers = this.extraHeaders; - } - this.ws = BrowserWebSocket ? new WebSocket(uri) : new WebSocket(uri, protocols, opts); + this.ws = new WebSocket(uri, protocols, opts); if (this.ws.binaryType === undefined) { this.supportsBinary = false; } - if (this.ws.supports && this.ws.supports.binary) { - this.supportsBinary = true; - this.ws.binaryType = 'buffer'; - } else { - this.ws.binaryType = 'arraybuffer'; - } - + this.ws.binaryType = 'arraybuffer'; this.addEventListeners(); }; @@ -28550,57 +28007,28 @@ if ('undefined' != typeof navigator WS.prototype.write = function(packets){ var self = this; this.writable = false; - // encodePacket efficient as it uses WS framing // no need for encodePayload - var total = packets.length; - for (var i = 0, l = total; i < l; i++) { - (function(packet) { - parser.encodePacket(packet, self.supportsBinary, function(data) { - if (!BrowserWebSocket) { - // always create a new object (GH-437) - var opts = {}; - if (packet.options) { - opts.compress = packet.options.compress; - } - - if (self.perMessageDeflate) { - var len = 'string' == typeof data ? global.Buffer.byteLength(data) : data.length; - if (len < self.perMessageDeflate.threshold) { - opts.compress = false; - } - } - } - - //Sometimes the websocket has already been closed but the browser didn't - //have a chance of informing us about it yet, in that case send will - //throw an error - try { - if (BrowserWebSocket) { - // TypeError is thrown when passing the second argument on Safari - self.ws.send(data); - } else { - self.ws.send(data, opts); - } - } catch (e){ - debug('websocket closed before onclose event'); - } - - --total || done(); - }); - })(packets[i]); + for (var i = 0, l = packets.length; i < l; i++) { + parser.encodePacket(packets[i], this.supportsBinary, function(data) { + //Sometimes the websocket has already been closed but the browser didn't + //have a chance of informing us about it yet, in that case send will + //throw an error + try { + self.ws.send(data); + } catch (e){ + debug('websocket closed before onclose event'); + } + }); } - function done(){ - self.emit('flush'); - - // fake drain - // defer to next tick to allow Socket to clear writeBuffer - setTimeout(function(){ - self.writable = true; - self.emit('drain'); - }, 0); + function ondrain() { + self.writable = true; + self.emit('drain'); } + // fake drain + // defer to next tick to allow Socket to clear writeBuffer + setTimeout(ondrain, 0); }; /** @@ -28644,7 +28072,7 @@ WS.prototype.uri = function(){ // append timestamp to URI if (this.timestampRequests) { - query[this.timestampParam] = yeast(); + query[this.timestampParam] = +new Date; } // communicate binary support capabilities @@ -28659,8 +28087,7 @@ WS.prototype.uri = function(){ query = '?' + query; } - var ipv6 = this.hostname.indexOf(':') !== -1; - return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query; + return schema + '://' + this.hostname + port + this.path + query; }; /** @@ -28674,8 +28101,7 @@ WS.prototype.check = function(){ return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name); }; -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../transport":101,"component-inherit":109,"debug":95,"engine.io-parser":110,"parseqs":121,"ws":46,"yeast":122}],107:[function(require,module,exports){ +},{"../transport":99,"component-inherit":106,"debug":107,"engine.io-parser":110,"parseqs":122,"ws":124}],105:[function(require,module,exports){ // browser shim for xmlhttprequest module var hasCORS = require('has-cors'); @@ -28713,173 +28139,7 @@ module.exports = function(opts) { } } -},{"has-cors":119}],108:[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; -}; - -},{}],109:[function(require,module,exports){ +},{"has-cors":119}],106:[function(require,module,exports){ module.exports = function(a, b){ var fn = function(){}; @@ -28887,6 +28147,467 @@ module.exports = function(a, b){ a.prototype = new fn; a.prototype.constructor = a; }; +},{}],107:[function(require,module,exports){ + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = require('./debug'); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // is webkit? http://stackoverflow.com/a/16459606/376773 + return ('WebkitAppearance' in document.documentElement.style) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (window.console && (console.firebug || (console.exception && console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + return JSON.stringify(v); +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs() { + var args = arguments; + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return args; + + var c = 'color: ' + this.color; + args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); + return args; +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // This hackery is required for IE8, + // where the `console.log` function doesn't have 'apply' + return 'object' == typeof console + && 'function' == typeof console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + localStorage.removeItem('debug'); + } else { + localStorage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = localStorage.debug; + } catch(e) {} + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +},{"./debug":108}],108:[function(require,module,exports){ + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = debug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = require('ms'); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lowercased letter, i.e. "n". + */ + +exports.formatters = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function selectColor() { + return exports.colors[prevColor++ % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function debug(namespace) { + + // define the `disabled` version + function disabled() { + } + disabled.enabled = false; + + // define the `enabled` version + function enabled() { + + var self = enabled; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // add the `color` if not set + if (null == self.useColors) self.useColors = exports.useColors(); + if (null == self.color && self.useColors) self.color = selectColor(); + + var args = Array.prototype.slice.call(arguments); + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %o + args = ['%o'].concat(args); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + if ('function' === typeof exports.formatArgs) { + args = exports.formatArgs.apply(self, args); + } + var logFn = enabled.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + enabled.enabled = true; + + var fn = exports.enabled(namespace) ? enabled : disabled; + + fn.namespace = namespace; + + return fn; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + var split = (namespaces || '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + +},{"ms":109}],109:[function(require,module,exports){ +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} options + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options){ + options = options || {}; + if ('string' == typeof val) return parse(val); + return options.long + ? long(val) + : short(val); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); + if (!match) return; + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 's': + return n * s; + case 'ms': + return n; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function short(ms) { + if (ms >= d) return Math.round(ms / d) + 'd'; + if (ms >= h) return Math.round(ms / h) + 'h'; + if (ms >= m) return Math.round(ms / m) + 'm'; + if (ms >= s) return Math.round(ms / s) + 's'; + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function long(ms) { + return plural(ms, d, 'day') + || plural(ms, h, 'hour') + || plural(ms, m, 'minute') + || plural(ms, s, 'second') + || ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) return; + if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; + return Math.ceil(ms / n) + ' ' + name + 's'; +} + },{}],110:[function(require,module,exports){ (function (global){ /** @@ -29076,7 +28797,7 @@ function encodeBlob(packet, supportsBinary, callback) { exports.encodeBase64Packet = function(packet, callback) { var message = 'b' + exports.packets[packet.type]; - if (Blob && packet.data instanceof global.Blob) { + if (Blob && packet.data instanceof Blob) { var fr = new FileReader(); fr.onload = function() { var b64 = fr.result.split(',')[1]; @@ -29645,22 +29366,8 @@ var BlobBuilder = global.BlobBuilder var blobSupported = (function() { try { - var a = new Blob(['hi']); - return a.size === 2; - } catch(e) { - return false; - } -})(); - -/** - * Check if Blob constructor supports ArrayBufferViews - * Fails in Safari 6, so we need to map to ArrayBuffers there. - */ - -var blobSupportsArrayBufferView = blobSupported && (function() { - try { - var b = new Blob([new Uint8Array([1,2])]); - return b.size === 2; + var b = new Blob(['hi']); + return b.size == 2; } catch(e) { return false; } @@ -29674,52 +29381,19 @@ var blobBuilderSupported = BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob; -/** - * Helper function that maps ArrayBufferViews to ArrayBuffers - * Used by BlobBuilder constructor and old browsers that didn't - * support it in the Blob constructor. - */ - -function mapArrayBufferViews(ary) { - for (var i = 0; i < ary.length; i++) { - var chunk = ary[i]; - if (chunk.buffer instanceof ArrayBuffer) { - var buf = chunk.buffer; - - // if this is a subarray, make a copy so we only - // include the subarray region from the underlying buffer - if (chunk.byteLength !== buf.byteLength) { - var copy = new Uint8Array(chunk.byteLength); - copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); - buf = copy.buffer; - } - - ary[i] = buf; - } - } -} - function BlobBuilderConstructor(ary, options) { options = options || {}; var bb = new BlobBuilder(); - mapArrayBufferViews(ary); - for (var i = 0; i < ary.length; i++) { bb.append(ary[i]); } - return (options.type) ? bb.getBlob(options.type) : bb.getBlob(); }; -function BlobConstructor(ary, options) { - mapArrayBufferViews(ary); - return new Blob(ary, options || {}); -}; - module.exports = (function() { if (blobSupported) { - return blobSupportsArrayBufferView ? global.Blob : BlobConstructor; + return global.Blob; } else if (blobBuilderSupported) { return BlobBuilderConstructor; } else { @@ -29777,7 +29451,7 @@ function hasBinary(data) { } for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { + if (obj.hasOwnProperty(key) && _hasBinary(obj[key])) { return true; } } @@ -29797,7 +29471,7 @@ module.exports = Array.isArray || function (arr) { },{}],118:[function(require,module,exports){ (function (global){ -/*! https://mths.be/utf8js v2.0.0 by @mathias */ +/*! http://mths.be/utf8js v2.0.0 by @mathias */ ;(function(root) { // Detect free variables `exports` @@ -29818,7 +29492,7 @@ module.exports = Array.isArray || function (arr) { var stringFromCharCode = String.fromCharCode; - // Taken from https://mths.be/punycode + // Taken from http://mths.be/punycode function ucs2decode(string) { var output = []; var counter = 0; @@ -29845,7 +29519,7 @@ module.exports = Array.isArray || function (arr) { return output; } - // Taken from https://mths.be/punycode + // Taken from http://mths.be/punycode function ucs2encode(array) { var length = array.length; var index = -1; @@ -29863,14 +29537,6 @@ module.exports = Array.isArray || function (arr) { return output; } - function checkScalarValue(codePoint) { - if (codePoint >= 0xD800 && codePoint <= 0xDFFF) { - throw Error( - 'Lone surrogate U+' + codePoint.toString(16).toUpperCase() + - ' is not a scalar value' - ); - } - } /*--------------------------------------------------------------------------*/ function createByte(codePoint, shift) { @@ -29886,7 +29552,6 @@ module.exports = Array.isArray || function (arr) { symbol = stringFromCharCode(((codePoint >> 6) & 0x1F) | 0xC0); } else if ((codePoint & 0xFFFF0000) == 0) { // 3-byte sequence - checkScalarValue(codePoint); symbol = stringFromCharCode(((codePoint >> 12) & 0x0F) | 0xE0); symbol += createByte(codePoint, 6); } @@ -29901,6 +29566,11 @@ module.exports = Array.isArray || function (arr) { function utf8encode(string) { var codePoints = ucs2decode(string); + + // console.log(JSON.stringify(codePoints.map(function(x) { + // return 'U+' + x.toString(16).toUpperCase(); + // }))); + var length = codePoints.length; var index = -1; var codePoint; @@ -29971,7 +29641,6 @@ module.exports = Array.isArray || function (arr) { byte3 = readContinuationByte(); codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; if (codePoint >= 0x0800) { - checkScalarValue(codePoint); return codePoint; } else { throw Error('Invalid continuation byte'); @@ -30045,6 +29714,12 @@ module.exports = Array.isArray || function (arr) { }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],119:[function(require,module,exports){ +/** + * Module dependencies. + */ + +var global = require('global'); + /** * Module exports. * @@ -30054,15 +29729,25 @@ module.exports = Array.isArray || function (arr) { */ try { - module.exports = typeof XMLHttpRequest !== 'undefined' && - 'withCredentials' in new XMLHttpRequest(); + module.exports = 'XMLHttpRequest' in global && + 'withCredentials' in new global.XMLHttpRequest(); } catch (err) { // if XMLHttp support is disabled in IE then it will throw // when trying to create module.exports = false; } -},{}],120:[function(require,module,exports){ +},{"global":120}],120:[function(require,module,exports){ + +/** + * Returns `this`. Execute this without a "context" (i.e. without it being + * attached to an object of the left-hand side), and `this` points to the + * "global" scope of the current JS execution. + */ + +module.exports = (function () { return this; })(); + +},{}],121:[function(require,module,exports){ (function (global){ /** * JSON parse. @@ -30097,7 +29782,7 @@ module.exports = function parsejson(data) { } }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],121:[function(require,module,exports){ +},{}],122:[function(require,module,exports){ /** * Compiles a querystring * Returns string representation of the object @@ -30136,153 +29821,7 @@ exports.decode = function(qs){ return qry; }; -},{}],122:[function(require,module,exports){ -'use strict'; - -var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split('') - , length = 64 - , map = {} - , seed = 0 - , i = 0 - , prev; - -/** - * Return a string representing the specified number. - * - * @param {Number} num The number to convert. - * @returns {String} The string representation of the number. - * @api public - */ -function encode(num) { - var encoded = ''; - - do { - encoded = alphabet[num % length] + encoded; - num = Math.floor(num / length); - } while (num > 0); - - return encoded; -} - -/** - * Return the integer value specified by the given string. - * - * @param {String} str The string to convert. - * @returns {Number} The integer value represented by the string. - * @api public - */ -function decode(str) { - var decoded = 0; - - for (i = 0; i < str.length; i++) { - decoded = decoded * length + map[str.charAt(i)]; - } - - return decoded; -} - -/** - * Yeast: A tiny growing id generator. - * - * @returns {String} A unique id. - * @api public - */ -function yeast() { - var now = encode(+new Date()); - - if (now !== prev) return seed = 0, prev = now; - return now +'.'+ encode(seed++); -} - -// -// Map each character to its index. -// -for (; i < length; i++) map[alphabet[i]] = i; - -// -// Expose the `yeast`, `encode` and `decode` functions. -// -yeast.encode = encode; -yeast.decode = decode; -module.exports = yeast; - },{}],123:[function(require,module,exports){ -(function (global){ - -/* - * Module requirements. - */ - -var isArray = require('isarray'); - -/** - * Module exports. - */ - -module.exports = hasBinary; - -/** - * Checks for binary data. - * - * Right now only Buffer and ArrayBuffer are supported.. - * - * @param {Object} anything - * @api public - */ - -function hasBinary(data) { - - function _hasBinary(obj) { - if (!obj) return false; - - if ( (global.Buffer && global.Buffer.isBuffer && global.Buffer.isBuffer(obj)) || - (global.ArrayBuffer && obj instanceof ArrayBuffer) || - (global.Blob && obj instanceof Blob) || - (global.File && obj instanceof File) - ) { - return true; - } - - if (isArray(obj)) { - for (var i = 0; i < obj.length; i++) { - if (_hasBinary(obj[i])) { - return true; - } - } - } else if (obj && 'object' == typeof obj) { - // see: https://github.com/Automattic/has-binary/pull/4 - if (obj.toJSON && 'function' == typeof obj.toJSON) { - obj = obj.toJSON(); - } - - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { - return true; - } - } - } - - return false; - } - - return _hasBinary(data); -} - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"isarray":124}],124:[function(require,module,exports){ -arguments[4][117][0].apply(exports,arguments) -},{"dup":117}],125:[function(require,module,exports){ - -var indexOf = [].indexOf; - -module.exports = function(arr, obj){ - if (indexOf) return arr.indexOf(obj); - for (var i = 0; i < arr.length; ++i) { - if (arr[i] === obj) return i; - } - return -1; -}; -},{}],126:[function(require,module,exports){ /** * Parses an URI * @@ -30323,7 +29862,239 @@ module.exports = function parseuri(str) { return uri; }; -},{}],127:[function(require,module,exports){ +},{}],124:[function(require,module,exports){ + +/** + * Module dependencies. + */ + +var global = (function() { return this; })(); + +/** + * WebSocket constructor. + */ + +var WebSocket = global.WebSocket || global.MozWebSocket; + +/** + * Module exports. + */ + +module.exports = WebSocket ? ws : null; + +/** + * WebSocket constructor. + * + * The third `opts` options object gets ignored in web browsers, since it's + * non-standard, and throws a TypeError if passed to the constructor. + * See: https://github.com/einaros/ws/issues/227 + * + * @param {String} uri + * @param {Array} protocols (optional) + * @param {Object) opts (optional) + * @api public + */ + +function ws(uri, protocols, opts) { + var instance; + if (protocols) { + instance = new WebSocket(uri, protocols); + } else { + instance = new WebSocket(uri); + } + return instance; +} + +if (WebSocket) ws.prototype = WebSocket.prototype; + +},{}],125:[function(require,module,exports){ +(function (global){ + +/* + * Module requirements. + */ + +var isArray = require('isarray'); + +/** + * Module exports. + */ + +module.exports = hasBinary; + +/** + * Checks for binary data. + * + * Right now only Buffer and ArrayBuffer are supported.. + * + * @param {Object} anything + * @api public + */ + +function hasBinary(data) { + + function _hasBinary(obj) { + if (!obj) return false; + + if ( (global.Buffer && global.Buffer.isBuffer(obj)) || + (global.ArrayBuffer && obj instanceof ArrayBuffer) || + (global.Blob && obj instanceof Blob) || + (global.File && obj instanceof File) + ) { + return true; + } + + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + if (_hasBinary(obj[i])) { + return true; + } + } + } else if (obj && 'object' == typeof obj) { + if (obj.toJSON) { + obj = obj.toJSON(); + } + + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) { + return true; + } + } + } + + return false; + } + + return _hasBinary(data); +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"isarray":126}],126:[function(require,module,exports){ +arguments[4][117][0].apply(exports,arguments) +},{"dup":117}],127:[function(require,module,exports){ + +var indexOf = [].indexOf; + +module.exports = function(arr, obj){ + if (indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; +}; +},{}],128:[function(require,module,exports){ + +/** + * HOP ref. + */ + +var has = Object.prototype.hasOwnProperty; + +/** + * Return own keys in `obj`. + * + * @param {Object} obj + * @return {Array} + * @api public + */ + +exports.keys = Object.keys || function(obj){ + var keys = []; + for (var key in obj) { + if (has.call(obj, key)) { + keys.push(key); + } + } + return keys; +}; + +/** + * Return own values in `obj`. + * + * @param {Object} obj + * @return {Array} + * @api public + */ + +exports.values = function(obj){ + var vals = []; + for (var key in obj) { + if (has.call(obj, key)) { + vals.push(obj[key]); + } + } + return vals; +}; + +/** + * Merge `b` into `a`. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api public + */ + +exports.merge = function(a, b){ + for (var key in b) { + if (has.call(b, key)) { + a[key] = b[key]; + } + } + return a; +}; + +/** + * Return length of `obj`. + * + * @param {Object} obj + * @return {Number} + * @api public + */ + +exports.length = function(obj){ + return exports.keys(obj).length; +}; + +/** + * Check if `obj` is empty. + * + * @param {Object} obj + * @return {Boolean} + * @api public + */ + +exports.isEmpty = function(obj){ + return 0 == exports.length(obj); +}; +},{}],129:[function(require,module,exports){ +/** + * Parses an URI + * + * @author Steven Levithan (MIT license) + * @api private + */ + +var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + +var parts = [ + 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host' + , 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' +]; + +module.exports = function parseuri(str) { + var m = re.exec(str || '') + , uri = {} + , i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + return uri; +}; + +},{}],130:[function(require,module,exports){ (function (global){ /*global Blob,File*/ @@ -30468,7 +30239,7 @@ exports.removeBlobs = function(data, callback) { }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./is-buffer":129,"isarray":131}],128:[function(require,module,exports){ +},{"./is-buffer":132,"isarray":133}],131:[function(require,module,exports){ /** * Module dependencies. @@ -30870,7 +30641,7 @@ function error(data){ }; } -},{"./binary":127,"./is-buffer":129,"component-emitter":130,"debug":95,"isarray":131,"json3":132}],129:[function(require,module,exports){ +},{"./binary":130,"./is-buffer":132,"component-emitter":94,"debug":95,"isarray":133,"json3":134}],132:[function(require,module,exports){ (function (global){ module.exports = isBuf; @@ -30887,905 +30658,861 @@ function isBuf(obj) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],130:[function(require,module,exports){ -arguments[4][108][0].apply(exports,arguments) -},{"dup":108}],131:[function(require,module,exports){ +},{}],133:[function(require,module,exports){ arguments[4][117][0].apply(exports,arguments) -},{"dup":117}],132:[function(require,module,exports){ -(function (global){ -/*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ -;(function () { +},{"dup":117}],134:[function(require,module,exports){ +/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */ +;(function (window) { + // Convenience aliases. + var getClass = {}.toString, isProperty, forEach, undef; + // Detect the `define` function exposed by asynchronous module loaders. The // strict `define` check is necessary for compatibility with `r.js`. var isLoader = typeof define === "function" && define.amd; - // A set of types used to distinguish objects from primitives. - var objectTypes = { - "function": true, - "object": true - }; + // Detect native implementations. + var nativeJSON = typeof JSON == "object" && JSON; - // Detect the `exports` object exposed by CommonJS implementations. - var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + // Set up the JSON 3 namespace, preferring the CommonJS `exports` object if + // available. + var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports; - // Use the `global` object exposed by Node (including Browserify via - // `insert-module-globals`), Narwhal, and Ringo as the default context, - // and the `window` object in browsers. Rhino exports a `global` function - // instead. - var root = objectTypes[typeof window] && window || this, - freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global; - - if (freeGlobal && (freeGlobal["global"] === freeGlobal || freeGlobal["window"] === freeGlobal || freeGlobal["self"] === freeGlobal)) { - root = freeGlobal; + if (JSON3 && nativeJSON) { + // Explicitly delegate to the native `stringify` and `parse` + // implementations in CommonJS environments. + JSON3.stringify = nativeJSON.stringify; + JSON3.parse = nativeJSON.parse; + } else { + // Export for web browsers, JavaScript engines, and asynchronous module + // loaders, using the global `JSON` object if available. + JSON3 = window.JSON = nativeJSON || {}; } - // Public: Initializes JSON 3 using the given `context` object, attaching the - // `stringify` and `parse` functions to the specified `exports` object. - function runInContext(context, exports) { - context || (context = root["Object"]()); - exports || (exports = root["Object"]()); + // Test the `Date#getUTC*` methods. Based on work by @Yaffle. + var isExtended = new Date(-3509827334573292); + try { + // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical + // results for certain dates in Opera >= 10.53. + isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && + // Safari < 2.0.2 stores the internal millisecond time value correctly, + // but clips the values returned by the date methods to the range of + // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). + isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; + } catch (exception) {} - // Native constructor aliases. - var Number = context["Number"] || root["Number"], - String = context["String"] || root["String"], - Object = context["Object"] || root["Object"], - Date = context["Date"] || root["Date"], - SyntaxError = context["SyntaxError"] || root["SyntaxError"], - TypeError = context["TypeError"] || root["TypeError"], - Math = context["Math"] || root["Math"], - nativeJSON = context["JSON"] || root["JSON"]; - - // Delegate to the native `stringify` and `parse` implementations. - if (typeof nativeJSON == "object" && nativeJSON) { - exports.stringify = nativeJSON.stringify; - exports.parse = nativeJSON.parse; + // Internal: Determines whether the native `JSON.stringify` and `parse` + // implementations are spec-compliant. Based on work by Ken Snyder. + function has(name) { + if (has[name] !== undef) { + // Return cached feature test result. + return has[name]; } - // Convenience aliases. - var objectProto = Object.prototype, - getClass = objectProto.toString, - isProperty, forEach, undef; - - // Test the `Date#getUTC*` methods. Based on work by @Yaffle. - var isExtended = new Date(-3509827334573292); - try { - // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical - // results for certain dates in Opera >= 10.53. - isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && - // Safari < 2.0.2 stores the internal millisecond time value correctly, - // but clips the values returned by the date methods to the range of - // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). - isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; - } catch (exception) {} - - // Internal: Determines whether the native `JSON.stringify` and `parse` - // implementations are spec-compliant. Based on work by Ken Snyder. - function has(name) { - if (has[name] !== undef) { - // Return cached feature test result. - return has[name]; - } - var isSupported; - if (name == "bug-string-char-index") { - // IE <= 7 doesn't support accessing string characters using square - // bracket notation. IE 8 only supports this for primitives. - isSupported = "a"[0] != "a"; - } else if (name == "json") { - // Indicates whether both `JSON.stringify` and `JSON.parse` are - // supported. - isSupported = has("json-stringify") && has("json-parse"); - } else { - var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; - // Test `JSON.stringify`. - if (name == "json-stringify") { - var stringify = exports.stringify, stringifySupported = typeof stringify == "function" && isExtended; - if (stringifySupported) { - // A test function object with a custom `toJSON` method. - (value = function () { - return 1; - }).toJSON = value; - try { - stringifySupported = - // Firefox 3.1b1 and b2 serialize string, number, and boolean - // primitives as object literals. - stringify(0) === "0" && - // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object - // literals. - stringify(new Number()) === "0" && - stringify(new String()) == '""' && - // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or - // does not define a canonical JSON representation (this applies to - // objects with `toJSON` properties as well, *unless* they are nested - // within an object or array). - stringify(getClass) === undef && - // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and - // FF 3.1b3 pass this test. - stringify(undef) === undef && - // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, - // respectively, if the value is omitted entirely. - stringify() === undef && - // FF 3.1b1, 2 throw an error if the given value is not a number, - // string, array, object, Boolean, or `null` literal. This applies to - // objects with custom `toJSON` methods as well, unless they are nested - // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` - // methods entirely. - stringify(value) === "1" && - stringify([value]) == "[1]" && - // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of - // `"[null]"`. - stringify([undef]) == "[null]" && - // YUI 3.0.0b1 fails to serialize `null` literals. - stringify(null) == "null" && - // FF 3.1b1, 2 halts serialization if an array contains a function: - // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 - // elides non-JSON values from objects and arrays, unless they - // define custom `toJSON` methods. - stringify([undef, getClass, null]) == "[null,null,null]" && - // Simple serialization test. FF 3.1b1 uses Unicode escape sequences - // where character escape codes are expected (e.g., `\b` => `\u0008`). - stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && - // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. - stringify(null, value) === "1" && - stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && - // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly - // serialize extended years. - stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && - // The milliseconds are optional in ES 5, but required in 5.1. - stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && - // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative - // four-digit years instead of six-digit years. Credits: @Yaffle. - stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && - // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond - // values less than 1000. Credits: @Yaffle. - stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; - } catch (exception) { - stringifySupported = false; - } + var isSupported; + if (name == "bug-string-char-index") { + // IE <= 7 doesn't support accessing string characters using square + // bracket notation. IE 8 only supports this for primitives. + isSupported = "a"[0] != "a"; + } else if (name == "json") { + // Indicates whether both `JSON.stringify` and `JSON.parse` are + // supported. + isSupported = has("json-stringify") && has("json-parse"); + } else { + var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; + // Test `JSON.stringify`. + if (name == "json-stringify") { + var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended; + if (stringifySupported) { + // A test function object with a custom `toJSON` method. + (value = function () { + return 1; + }).toJSON = value; + try { + stringifySupported = + // Firefox 3.1b1 and b2 serialize string, number, and boolean + // primitives as object literals. + stringify(0) === "0" && + // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object + // literals. + stringify(new Number()) === "0" && + stringify(new String()) == '""' && + // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or + // does not define a canonical JSON representation (this applies to + // objects with `toJSON` properties as well, *unless* they are nested + // within an object or array). + stringify(getClass) === undef && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + stringify(undef) === undef && + // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, + // respectively, if the value is omitted entirely. + stringify() === undef && + // FF 3.1b1, 2 throw an error if the given value is not a number, + // string, array, object, Boolean, or `null` literal. This applies to + // objects with custom `toJSON` methods as well, unless they are nested + // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` + // methods entirely. + stringify(value) === "1" && + stringify([value]) == "[1]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + stringify([undef]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + stringify(null) == "null" && + // FF 3.1b1, 2 halts serialization if an array contains a function: + // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 + // elides non-JSON values from objects and arrays, unless they + // define custom `toJSON` methods. + stringify([undef, getClass, null]) == "[null,null,null]" && + // Simple serialization test. FF 3.1b1 uses Unicode escape sequences + // where character escape codes are expected (e.g., `\b` => `\u0008`). + stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && + // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. + stringify(null, value) === "1" && + stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && + // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly + // serialize extended years. + stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && + // The milliseconds are optional in ES 5, but required in 5.1. + stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && + // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative + // four-digit years instead of six-digit years. Credits: @Yaffle. + stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && + // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond + // values less than 1000. Credits: @Yaffle. + stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; + } catch (exception) { + stringifySupported = false; } - isSupported = stringifySupported; } - // Test `JSON.parse`. - if (name == "json-parse") { - var parse = exports.parse; - if (typeof parse == "function") { - try { - // FF 3.1b1, b2 will throw an exception if a bare literal is provided. - // Conforming implementations should also coerce the initial argument to - // a string prior to parsing. - if (parse("0") === 0 && !parse(false)) { - // Simple parsing test. - value = parse(serialized); - var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + isSupported = stringifySupported; + } + // Test `JSON.parse`. + if (name == "json-parse") { + var parse = JSON3.parse; + if (typeof parse == "function") { + try { + // FF 3.1b1, b2 will throw an exception if a bare literal is provided. + // Conforming implementations should also coerce the initial argument to + // a string prior to parsing. + if (parse("0") === 0 && !parse(false)) { + // Simple parsing test. + value = parse(serialized); + var parseSupported = value["a"].length == 5 && value["a"][0] === 1; + if (parseSupported) { + try { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !parse('"\t"'); + } catch (exception) {} if (parseSupported) { try { - // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. - parseSupported = !parse('"\t"'); + // FF 4.0 and 4.0.1 allow leading `+` signs and leading + // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow + // certain octal literals. + parseSupported = parse("01") !== 1; + } catch (exception) {} + } + if (parseSupported) { + try { + // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal + // points. These environments, along with FF 3.1b1 and 2, + // also allow trailing commas in JSON objects and arrays. + parseSupported = parse("1.") !== 1; } catch (exception) {} - if (parseSupported) { - try { - // FF 4.0 and 4.0.1 allow leading `+` signs and leading - // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow - // certain octal literals. - parseSupported = parse("01") !== 1; - } catch (exception) {} - } - if (parseSupported) { - try { - // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal - // points. These environments, along with FF 3.1b1 and 2, - // also allow trailing commas in JSON objects and arrays. - parseSupported = parse("1.") !== 1; - } catch (exception) {} - } } } - } catch (exception) { - parseSupported = false; } - } - isSupported = parseSupported; - } - } - return has[name] = !!isSupported; - } - - if (!has("json")) { - // Common `[[Class]]` name aliases. - var functionClass = "[object Function]", - dateClass = "[object Date]", - numberClass = "[object Number]", - stringClass = "[object String]", - arrayClass = "[object Array]", - booleanClass = "[object Boolean]"; - - // Detect incomplete support for accessing string characters by index. - var charIndexBuggy = has("bug-string-char-index"); - - // Define additional utility methods if the `Date` methods are buggy. - if (!isExtended) { - var floor = Math.floor; - // A mapping between the months of the year and the number of days between - // January 1st and the first of the respective month. - var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; - // Internal: Calculates the number of days between the Unix epoch and the - // first day of the given month. - var getDay = function (year, month) { - return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); - }; - } - - // Internal: Determines if a property is a direct property of the given - // object. Delegates to the native `Object#hasOwnProperty` method. - if (!(isProperty = objectProto.hasOwnProperty)) { - isProperty = function (property) { - var members = {}, constructor; - if ((members.__proto__ = null, members.__proto__ = { - // The *proto* property cannot be set multiple times in recent - // versions of Firefox and SeaMonkey. - "toString": 1 - }, members).toString != getClass) { - // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but - // supports the mutable *proto* property. - isProperty = function (property) { - // Capture and break the object's prototype chain (see section 8.6.2 - // of the ES 5.1 spec). The parenthesized expression prevents an - // unsafe transformation by the Closure Compiler. - var original = this.__proto__, result = property in (this.__proto__ = null, this); - // Restore the original prototype chain. - this.__proto__ = original; - return result; - }; - } else { - // Capture a reference to the top-level `Object` constructor. - constructor = members.constructor; - // Use the `constructor` property to simulate `Object#hasOwnProperty` in - // other environments. - isProperty = function (property) { - var parent = (this.constructor || constructor).prototype; - return property in this && !(property in parent && this[property] === parent[property]); - }; - } - members = null; - return isProperty.call(this, property); - }; - } - - // Internal: Normalizes the `for...in` iteration algorithm across - // environments. Each enumerated key is yielded to a `callback` function. - forEach = function (object, callback) { - var size = 0, Properties, members, property; - - // Tests for bugs in the current environment's `for...in` algorithm. The - // `valueOf` property inherits the non-enumerable flag from - // `Object.prototype` in older versions of IE, Netscape, and Mozilla. - (Properties = function () { - this.valueOf = 0; - }).prototype.valueOf = 0; - - // Iterate over a new instance of the `Properties` class. - members = new Properties(); - for (property in members) { - // Ignore all properties inherited from `Object.prototype`. - if (isProperty.call(members, property)) { - size++; + } catch (exception) { + parseSupported = false; } } - Properties = members = null; - - // Normalize the iteration algorithm. - if (!size) { - // A list of non-enumerable properties inherited from `Object.prototype`. - members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; - // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable - // properties. - forEach = function (object, callback) { - var isFunction = getClass.call(object) == functionClass, property, length; - var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty; - for (property in object) { - // Gecko <= 1.0 enumerates the `prototype` property of functions under - // certain conditions; IE does not. - if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { - callback(property); - } - } - // Manually invoke the callback for each non-enumerable property. - for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); - }; - } else if (size == 2) { - // Safari <= 2.0.4 enumerates shadowed properties twice. - forEach = function (object, callback) { - // Create a set of iterated properties. - var members = {}, isFunction = getClass.call(object) == functionClass, property; - for (property in object) { - // Store each property name to prevent double enumeration. The - // `prototype` property of functions is not enumerated due to cross- - // environment inconsistencies. - if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { - callback(property); - } - } - }; - } else { - // No bugs detected; use the standard `for...in` algorithm. - forEach = function (object, callback) { - var isFunction = getClass.call(object) == functionClass, property, isConstructor; - for (property in object) { - if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { - callback(property); - } - } - // Manually invoke the callback for the `constructor` property due to - // cross-environment inconsistencies. - if (isConstructor || isProperty.call(object, (property = "constructor"))) { - callback(property); - } - }; - } - return forEach(object, callback); - }; - - // Public: Serializes a JavaScript `value` as a JSON string. The optional - // `filter` argument may specify either a function that alters how object and - // array members are serialized, or an array of strings and numbers that - // indicates which properties should be serialized. The optional `width` - // argument may be either a string or number that specifies the indentation - // level of the output. - if (!has("json-stringify")) { - // Internal: A map of control characters and their escaped equivalents. - var Escapes = { - 92: "\\\\", - 34: '\\"', - 8: "\\b", - 12: "\\f", - 10: "\\n", - 13: "\\r", - 9: "\\t" - }; - - // Internal: Converts `value` into a zero-padded string such that its - // length is at least equal to `width`. The `width` must be <= 6. - var leadingZeroes = "000000"; - var toPaddedString = function (width, value) { - // The `|| 0` expression is necessary to work around a bug in - // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. - return (leadingZeroes + (value || 0)).slice(-width); - }; - - // Internal: Double-quotes a string `value`, replacing all ASCII control - // characters (characters with code unit values between 0 and 31) with - // their escaped equivalents. This is an implementation of the - // `Quote(value)` operation defined in ES 5.1 section 15.12.3. - var unicodePrefix = "\\u00"; - var quote = function (value) { - var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10; - var symbols = useCharIndex && (charIndexBuggy ? value.split("") : value); - for (; index < length; index++) { - var charCode = value.charCodeAt(index); - // If the character is a control character, append its Unicode or - // shorthand escape sequence; otherwise, append the character as-is. - switch (charCode) { - case 8: case 9: case 10: case 12: case 13: case 34: case 92: - result += Escapes[charCode]; - break; - default: - if (charCode < 32) { - result += unicodePrefix + toPaddedString(2, charCode.toString(16)); - break; - } - result += useCharIndex ? symbols[index] : value.charAt(index); - } - } - return result + '"'; - }; - - // Internal: Recursively serializes an object. Implements the - // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. - var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { - var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; - try { - // Necessary for host object support. - value = object[property]; - } catch (exception) {} - if (typeof value == "object" && value) { - className = getClass.call(value); - if (className == dateClass && !isProperty.call(value, "toJSON")) { - if (value > -1 / 0 && value < 1 / 0) { - // Dates are serialized according to the `Date#toJSON` method - // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 - // for the ISO 8601 date time string format. - if (getDay) { - // Manually compute the year, month, date, hours, minutes, - // seconds, and milliseconds if the `getUTC*` methods are - // buggy. Adapted from @Yaffle's `date-shim` project. - date = floor(value / 864e5); - for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); - for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); - date = 1 + date - getDay(year, month); - // The `time` value specifies the time within the day (see ES - // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used - // to compute `A modulo B`, as the `%` operator does not - // correspond to the `modulo` operation for negative numbers. - time = (value % 864e5 + 864e5) % 864e5; - // The hours, minutes, seconds, and milliseconds are obtained by - // decomposing the time within the day. See section 15.9.1.10. - hours = floor(time / 36e5) % 24; - minutes = floor(time / 6e4) % 60; - seconds = floor(time / 1e3) % 60; - milliseconds = time % 1e3; - } else { - year = value.getUTCFullYear(); - month = value.getUTCMonth(); - date = value.getUTCDate(); - hours = value.getUTCHours(); - minutes = value.getUTCMinutes(); - seconds = value.getUTCSeconds(); - milliseconds = value.getUTCMilliseconds(); - } - // Serialize extended years correctly. - value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + - "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + - // Months, dates, hours, minutes, and seconds should have two - // digits; milliseconds should have three. - "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + - // Milliseconds are optional in ES 5.0, but required in 5.1. - "." + toPaddedString(3, milliseconds) + "Z"; - } else { - value = null; - } - } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { - // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the - // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 - // ignores all `toJSON` methods on these objects unless they are - // defined directly on an instance. - value = value.toJSON(property); - } - } - if (callback) { - // If a replacement function was provided, call it to obtain the value - // for serialization. - value = callback.call(object, property, value); - } - if (value === null) { - return "null"; - } - className = getClass.call(value); - if (className == booleanClass) { - // Booleans are represented literally. - return "" + value; - } else if (className == numberClass) { - // JSON numbers must be finite. `Infinity` and `NaN` are serialized as - // `"null"`. - return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; - } else if (className == stringClass) { - // Strings are double-quoted and escaped. - return quote("" + value); - } - // Recursively serialize objects and arrays. - if (typeof value == "object") { - // Check for cyclic structures. This is a linear search; performance - // is inversely proportional to the number of unique nested objects. - for (length = stack.length; length--;) { - if (stack[length] === value) { - // Cyclic structures cannot be serialized by `JSON.stringify`. - throw TypeError(); - } - } - // Add the object to the stack of traversed objects. - stack.push(value); - results = []; - // Save the current indentation level and indent one additional level. - prefix = indentation; - indentation += whitespace; - if (className == arrayClass) { - // Recursively serialize array elements. - for (index = 0, length = value.length; index < length; index++) { - element = serialize(index, value, callback, properties, whitespace, indentation, stack); - results.push(element === undef ? "null" : element); - } - result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; - } else { - // Recursively serialize object members. Members are selected from - // either a user-specified list of property names, or the object - // itself. - forEach(properties || value, function (property) { - var element = serialize(property, value, callback, properties, whitespace, indentation, stack); - if (element !== undef) { - // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} - // is not the empty string, let `member` {quote(property) + ":"} - // be the concatenation of `member` and the `space` character." - // The "`space` character" refers to the literal space - // character, not the `space` {width} argument provided to - // `JSON.stringify`. - results.push(quote(property) + ":" + (whitespace ? " " : "") + element); - } - }); - result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; - } - // Remove the object from the traversed object stack. - stack.pop(); - return result; - } - }; - - // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. - exports.stringify = function (source, filter, width) { - var whitespace, callback, properties, className; - if (objectTypes[typeof filter] && filter) { - if ((className = getClass.call(filter)) == functionClass) { - callback = filter; - } else if (className == arrayClass) { - // Convert the property names array into a makeshift set. - properties = {}; - for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); - } - } - if (width) { - if ((className = getClass.call(width)) == numberClass) { - // Convert the `width` to an integer and create a string containing - // `width` number of space characters. - if ((width -= width % 1) > 0) { - for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); - } - } else if (className == stringClass) { - whitespace = width.length <= 10 ? width : width.slice(0, 10); - } - } - // Opera <= 7.54u2 discards the values associated with empty string keys - // (`""`) only if they are used directly within an object member list - // (e.g., `!("" in { "": 1})`). - return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); - }; - } - - // Public: Parses a JSON source string. - if (!has("json-parse")) { - var fromCharCode = String.fromCharCode; - - // Internal: A map of escaped control characters and their unescaped - // equivalents. - var Unescapes = { - 92: "\\", - 34: '"', - 47: "/", - 98: "\b", - 116: "\t", - 110: "\n", - 102: "\f", - 114: "\r" - }; - - // Internal: Stores the parser state. - var Index, Source; - - // Internal: Resets the parser state and throws a `SyntaxError`. - var abort = function () { - Index = Source = null; - throw SyntaxError(); - }; - - // Internal: Returns the next token, or `"$"` if the parser has reached - // the end of the source string. A token may be a string, number, `null` - // literal, or Boolean literal. - var lex = function () { - var source = Source, length = source.length, value, begin, position, isSigned, charCode; - while (Index < length) { - charCode = source.charCodeAt(Index); - switch (charCode) { - case 9: case 10: case 13: case 32: - // Skip whitespace tokens, including tabs, carriage returns, line - // feeds, and space characters. - Index++; - break; - case 123: case 125: case 91: case 93: case 58: case 44: - // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at - // the current position. - value = charIndexBuggy ? source.charAt(Index) : source[Index]; - Index++; - return value; - case 34: - // `"` delimits a JSON string; advance to the next character and - // begin parsing the string. String tokens are prefixed with the - // sentinel `@` character to distinguish them from punctuators and - // end-of-string tokens. - for (value = "@", Index++; Index < length;) { - charCode = source.charCodeAt(Index); - if (charCode < 32) { - // Unescaped ASCII control characters (those with a code unit - // less than the space character) are not permitted. - abort(); - } else if (charCode == 92) { - // A reverse solidus (`\`) marks the beginning of an escaped - // control character (including `"`, `\`, and `/`) or Unicode - // escape sequence. - charCode = source.charCodeAt(++Index); - switch (charCode) { - case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: - // Revive escaped control characters. - value += Unescapes[charCode]; - Index++; - break; - case 117: - // `\u` marks the beginning of a Unicode escape sequence. - // Advance to the first character and validate the - // four-digit code point. - begin = ++Index; - for (position = Index + 4; Index < position; Index++) { - charCode = source.charCodeAt(Index); - // A valid sequence comprises four hexdigits (case- - // insensitive) that form a single hexadecimal value. - if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { - // Invalid Unicode escape sequence. - abort(); - } - } - // Revive the escaped character. - value += fromCharCode("0x" + source.slice(begin, Index)); - break; - default: - // Invalid escape sequence. - abort(); - } - } else { - if (charCode == 34) { - // An unescaped double-quote character marks the end of the - // string. - break; - } - charCode = source.charCodeAt(Index); - begin = Index; - // Optimize for the common case where a string is valid. - while (charCode >= 32 && charCode != 92 && charCode != 34) { - charCode = source.charCodeAt(++Index); - } - // Append the string as-is. - value += source.slice(begin, Index); - } - } - if (source.charCodeAt(Index) == 34) { - // Advance to the next character and return the revived string. - Index++; - return value; - } - // Unterminated string. - abort(); - default: - // Parse numbers and literals. - begin = Index; - // Advance past the negative sign, if one is specified. - if (charCode == 45) { - isSigned = true; - charCode = source.charCodeAt(++Index); - } - // Parse an integer or floating-point value. - if (charCode >= 48 && charCode <= 57) { - // Leading zeroes are interpreted as octal literals. - if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { - // Illegal octal literal. - abort(); - } - isSigned = false; - // Parse the integer component. - for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); - // Floats cannot contain a leading decimal point; however, this - // case is already accounted for by the parser. - if (source.charCodeAt(Index) == 46) { - position = ++Index; - // Parse the decimal component. - for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); - if (position == Index) { - // Illegal trailing decimal. - abort(); - } - Index = position; - } - // Parse exponents. The `e` denoting the exponent is - // case-insensitive. - charCode = source.charCodeAt(Index); - if (charCode == 101 || charCode == 69) { - charCode = source.charCodeAt(++Index); - // Skip past the sign following the exponent, if one is - // specified. - if (charCode == 43 || charCode == 45) { - Index++; - } - // Parse the exponential component. - for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); - if (position == Index) { - // Illegal empty exponent. - abort(); - } - Index = position; - } - // Coerce the parsed value to a JavaScript number. - return +source.slice(begin, Index); - } - // A negative sign may only precede numbers. - if (isSigned) { - abort(); - } - // `true`, `false`, and `null` literals. - if (source.slice(Index, Index + 4) == "true") { - Index += 4; - return true; - } else if (source.slice(Index, Index + 5) == "false") { - Index += 5; - return false; - } else if (source.slice(Index, Index + 4) == "null") { - Index += 4; - return null; - } - // Unrecognized token. - abort(); - } - } - // Return the sentinel `$` character if the parser has reached the end - // of the source string. - return "$"; - }; - - // Internal: Parses a JSON `value` token. - var get = function (value) { - var results, hasMembers; - if (value == "$") { - // Unexpected end of input. - abort(); - } - if (typeof value == "string") { - if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { - // Remove the sentinel `@` character. - return value.slice(1); - } - // Parse object and array literals. - if (value == "[") { - // Parses a JSON array, returning a new JavaScript array. - results = []; - for (;; hasMembers || (hasMembers = true)) { - value = lex(); - // A closing square bracket marks the end of the array literal. - if (value == "]") { - break; - } - // If the array literal contains elements, the current token - // should be a comma separating the previous element from the - // next. - if (hasMembers) { - if (value == ",") { - value = lex(); - if (value == "]") { - // Unexpected trailing `,` in array literal. - abort(); - } - } else { - // A `,` must separate each array element. - abort(); - } - } - // Elisions and leading commas are not permitted. - if (value == ",") { - abort(); - } - results.push(get(value)); - } - return results; - } else if (value == "{") { - // Parses a JSON object, returning a new JavaScript object. - results = {}; - for (;; hasMembers || (hasMembers = true)) { - value = lex(); - // A closing curly brace marks the end of the object literal. - if (value == "}") { - break; - } - // If the object literal contains members, the current token - // should be a comma separator. - if (hasMembers) { - if (value == ",") { - value = lex(); - if (value == "}") { - // Unexpected trailing `,` in object literal. - abort(); - } - } else { - // A `,` must separate each object member. - abort(); - } - } - // Leading commas are not permitted, object property names must be - // double-quoted strings, and a `:` must separate each property - // name and value. - if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { - abort(); - } - results[value.slice(1)] = get(lex()); - } - return results; - } - // Unexpected token encountered. - abort(); - } - return value; - }; - - // Internal: Updates a traversed object member. - var update = function (source, property, callback) { - var element = walk(source, property, callback); - if (element === undef) { - delete source[property]; - } else { - source[property] = element; - } - }; - - // Internal: Recursively traverses a parsed JSON object, invoking the - // `callback` function for each value. This is an implementation of the - // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. - var walk = function (source, property, callback) { - var value = source[property], length; - if (typeof value == "object" && value) { - // `forEach` can't be used to traverse an array in Opera <= 8.54 - // because its `Object#hasOwnProperty` implementation returns `false` - // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). - if (getClass.call(value) == arrayClass) { - for (length = value.length; length--;) { - update(value, length, callback); - } - } else { - forEach(value, function (property) { - update(value, property, callback); - }); - } - } - return callback.call(source, property, value); - }; - - // Public: `JSON.parse`. See ES 5.1 section 15.12.2. - exports.parse = function (source, callback) { - var result, value; - Index = 0; - Source = "" + source; - result = get(lex()); - // If a JSON string contains multiple tokens, it is invalid. - if (lex() != "$") { - abort(); - } - // Reset the parser state. - Index = Source = null; - return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; - }; + isSupported = parseSupported; } } - - exports["runInContext"] = runInContext; - return exports; + return has[name] = !!isSupported; } - if (freeExports && !isLoader) { - // Export for CommonJS environments. - runInContext(root, freeExports); - } else { - // Export for web browsers and JavaScript engines. - var nativeJSON = root.JSON, - previousJSON = root["JSON3"], - isRestored = false; + if (!has("json")) { + // Common `[[Class]]` name aliases. + var functionClass = "[object Function]"; + var dateClass = "[object Date]"; + var numberClass = "[object Number]"; + var stringClass = "[object String]"; + var arrayClass = "[object Array]"; + var booleanClass = "[object Boolean]"; - var JSON3 = runInContext(root, (root["JSON3"] = { - // Public: Restores the original value of the global `JSON` object and - // returns a reference to the `JSON3` object. - "noConflict": function () { - if (!isRestored) { - isRestored = true; - root.JSON = nativeJSON; - root["JSON3"] = previousJSON; - nativeJSON = previousJSON = null; + // Detect incomplete support for accessing string characters by index. + var charIndexBuggy = has("bug-string-char-index"); + + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + var floor = Math.floor; + // A mapping between the months of the year and the number of days between + // January 1st and the first of the respective month. + var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + var getDay = function (year, month) { + return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); + }; + } + + // Internal: Determines if a property is a direct property of the given + // object. Delegates to the native `Object#hasOwnProperty` method. + if (!(isProperty = {}.hasOwnProperty)) { + isProperty = function (property) { + var members = {}, constructor; + if ((members.__proto__ = null, members.__proto__ = { + // The *proto* property cannot be set multiple times in recent + // versions of Firefox and SeaMonkey. + "toString": 1 + }, members).toString != getClass) { + // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but + // supports the mutable *proto* property. + isProperty = function (property) { + // Capture and break the object's prototype chain (see section 8.6.2 + // of the ES 5.1 spec). The parenthesized expression prevents an + // unsafe transformation by the Closure Compiler. + var original = this.__proto__, result = property in (this.__proto__ = null, this); + // Restore the original prototype chain. + this.__proto__ = original; + return result; + }; + } else { + // Capture a reference to the top-level `Object` constructor. + constructor = members.constructor; + // Use the `constructor` property to simulate `Object#hasOwnProperty` in + // other environments. + isProperty = function (property) { + var parent = (this.constructor || constructor).prototype; + return property in this && !(property in parent && this[property] === parent[property]); + }; } - return JSON3; - } - })); + members = null; + return isProperty.call(this, property); + }; + } - root.JSON = { - "parse": JSON3.parse, - "stringify": JSON3.stringify + // Internal: A set of primitive types used by `isHostType`. + var PrimitiveTypes = { + 'boolean': 1, + 'number': 1, + 'string': 1, + 'undefined': 1 }; + + // Internal: Determines if the given object `property` value is a + // non-primitive. + var isHostType = function (object, property) { + var type = typeof object[property]; + return type == 'object' ? !!object[property] : !PrimitiveTypes[type]; + }; + + // Internal: Normalizes the `for...in` iteration algorithm across + // environments. Each enumerated key is yielded to a `callback` function. + forEach = function (object, callback) { + var size = 0, Properties, members, property; + + // Tests for bugs in the current environment's `for...in` algorithm. The + // `valueOf` property inherits the non-enumerable flag from + // `Object.prototype` in older versions of IE, Netscape, and Mozilla. + (Properties = function () { + this.valueOf = 0; + }).prototype.valueOf = 0; + + // Iterate over a new instance of the `Properties` class. + members = new Properties(); + for (property in members) { + // Ignore all properties inherited from `Object.prototype`. + if (isProperty.call(members, property)) { + size++; + } + } + Properties = members = null; + + // Normalize the iteration algorithm. + if (!size) { + // A list of non-enumerable properties inherited from `Object.prototype`. + members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; + // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable + // properties. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, length; + var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { + callback(property); + } + } + // Manually invoke the callback for each non-enumerable property. + for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); + }; + } else if (size == 2) { + // Safari <= 2.0.4 enumerates shadowed properties twice. + forEach = function (object, callback) { + // Create a set of iterated properties. + var members = {}, isFunction = getClass.call(object) == functionClass, property; + for (property in object) { + // Store each property name to prevent double enumeration. The + // `prototype` property of functions is not enumerated due to cross- + // environment inconsistencies. + if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { + callback(property); + } + } + }; + } else { + // No bugs detected; use the standard `for...in` algorithm. + forEach = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, isConstructor; + for (property in object) { + if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { + callback(property); + } + } + // Manually invoke the callback for the `constructor` property due to + // cross-environment inconsistencies. + if (isConstructor || isProperty.call(object, (property = "constructor"))) { + callback(property); + } + }; + } + return forEach(object, callback); + }; + + // Public: Serializes a JavaScript `value` as a JSON string. The optional + // `filter` argument may specify either a function that alters how object and + // array members are serialized, or an array of strings and numbers that + // indicates which properties should be serialized. The optional `width` + // argument may be either a string or number that specifies the indentation + // level of the output. + if (!has("json-stringify")) { + // Internal: A map of control characters and their escaped equivalents. + var Escapes = { + 92: "\\\\", + 34: '\\"', + 8: "\\b", + 12: "\\f", + 10: "\\n", + 13: "\\r", + 9: "\\t" + }; + + // Internal: Converts `value` into a zero-padded string such that its + // length is at least equal to `width`. The `width` must be <= 6. + var leadingZeroes = "000000"; + var toPaddedString = function (width, value) { + // The `|| 0` expression is necessary to work around a bug in + // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. + return (leadingZeroes + (value || 0)).slice(-width); + }; + + // Internal: Double-quotes a string `value`, replacing all ASCII control + // characters (characters with code unit values between 0 and 31) with + // their escaped equivalents. This is an implementation of the + // `Quote(value)` operation defined in ES 5.1 section 15.12.3. + var unicodePrefix = "\\u00"; + var quote = function (value) { + var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols; + if (isLarge) { + symbols = value.split(""); + } + for (; index < length; index++) { + var charCode = value.charCodeAt(index); + // If the character is a control character, append its Unicode or + // shorthand escape sequence; otherwise, append the character as-is. + switch (charCode) { + case 8: case 9: case 10: case 12: case 13: case 34: case 92: + result += Escapes[charCode]; + break; + default: + if (charCode < 32) { + result += unicodePrefix + toPaddedString(2, charCode.toString(16)); + break; + } + result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index]; + } + } + return result + '"'; + }; + + // Internal: Recursively serializes an object. Implements the + // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. + var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { + var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; + try { + // Necessary for host object support. + value = object[property]; + } catch (exception) {} + if (typeof value == "object" && value) { + className = getClass.call(value); + if (className == dateClass && !isProperty.call(value, "toJSON")) { + if (value > -1 / 0 && value < 1 / 0) { + // Dates are serialized according to the `Date#toJSON` method + // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 + // for the ISO 8601 date time string format. + if (getDay) { + // Manually compute the year, month, date, hours, minutes, + // seconds, and milliseconds if the `getUTC*` methods are + // buggy. Adapted from @Yaffle's `date-shim` project. + date = floor(value / 864e5); + for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); + for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); + date = 1 + date - getDay(year, month); + // The `time` value specifies the time within the day (see ES + // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used + // to compute `A modulo B`, as the `%` operator does not + // correspond to the `modulo` operation for negative numbers. + time = (value % 864e5 + 864e5) % 864e5; + // The hours, minutes, seconds, and milliseconds are obtained by + // decomposing the time within the day. See section 15.9.1.10. + hours = floor(time / 36e5) % 24; + minutes = floor(time / 6e4) % 60; + seconds = floor(time / 1e3) % 60; + milliseconds = time % 1e3; + } else { + year = value.getUTCFullYear(); + month = value.getUTCMonth(); + date = value.getUTCDate(); + hours = value.getUTCHours(); + minutes = value.getUTCMinutes(); + seconds = value.getUTCSeconds(); + milliseconds = value.getUTCMilliseconds(); + } + // Serialize extended years correctly. + value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + + // Months, dates, hours, minutes, and seconds should have two + // digits; milliseconds should have three. + "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + + // Milliseconds are optional in ES 5.0, but required in 5.1. + "." + toPaddedString(3, milliseconds) + "Z"; + } else { + value = null; + } + } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { + // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the + // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 + // ignores all `toJSON` methods on these objects unless they are + // defined directly on an instance. + value = value.toJSON(property); + } + } + if (callback) { + // If a replacement function was provided, call it to obtain the value + // for serialization. + value = callback.call(object, property, value); + } + if (value === null) { + return "null"; + } + className = getClass.call(value); + if (className == booleanClass) { + // Booleans are represented literally. + return "" + value; + } else if (className == numberClass) { + // JSON numbers must be finite. `Infinity` and `NaN` are serialized as + // `"null"`. + return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; + } else if (className == stringClass) { + // Strings are double-quoted and escaped. + return quote("" + value); + } + // Recursively serialize objects and arrays. + if (typeof value == "object") { + // Check for cyclic structures. This is a linear search; performance + // is inversely proportional to the number of unique nested objects. + for (length = stack.length; length--;) { + if (stack[length] === value) { + // Cyclic structures cannot be serialized by `JSON.stringify`. + throw TypeError(); + } + } + // Add the object to the stack of traversed objects. + stack.push(value); + results = []; + // Save the current indentation level and indent one additional level. + prefix = indentation; + indentation += whitespace; + if (className == arrayClass) { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; index++) { + element = serialize(index, value, callback, properties, whitespace, indentation, stack); + results.push(element === undef ? "null" : element); + } + result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; + } else { + // Recursively serialize object members. Members are selected from + // either a user-specified list of property names, or the object + // itself. + forEach(properties || value, function (property) { + var element = serialize(property, value, callback, properties, whitespace, indentation, stack); + if (element !== undef) { + // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} + // is not the empty string, let `member` {quote(property) + ":"} + // be the concatenation of `member` and the `space` character." + // The "`space` character" refers to the literal space + // character, not the `space` {width} argument provided to + // `JSON.stringify`. + results.push(quote(property) + ":" + (whitespace ? " " : "") + element); + } + }); + result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; + } + // Remove the object from the traversed object stack. + stack.pop(); + return result; + } + }; + + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + JSON3.stringify = function (source, filter, width) { + var whitespace, callback, properties, className; + if (typeof filter == "function" || typeof filter == "object" && filter) { + if ((className = getClass.call(filter)) == functionClass) { + callback = filter; + } else if (className == arrayClass) { + // Convert the property names array into a makeshift set. + properties = {}; + for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); + } + } + if (width) { + if ((className = getClass.call(width)) == numberClass) { + // Convert the `width` to an integer and create a string containing + // `width` number of space characters. + if ((width -= width % 1) > 0) { + for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); + } + } else if (className == stringClass) { + whitespace = width.length <= 10 ? width : width.slice(0, 10); + } + } + // Opera <= 7.54u2 discards the values associated with empty string keys + // (`""`) only if they are used directly within an object member list + // (e.g., `!("" in { "": 1})`). + return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); + }; + } + + // Public: Parses a JSON source string. + if (!has("json-parse")) { + var fromCharCode = String.fromCharCode; + + // Internal: A map of escaped control characters and their unescaped + // equivalents. + var Unescapes = { + 92: "\\", + 34: '"', + 47: "/", + 98: "\b", + 116: "\t", + 110: "\n", + 102: "\f", + 114: "\r" + }; + + // Internal: Stores the parser state. + var Index, Source; + + // Internal: Resets the parser state and throws a `SyntaxError`. + var abort = function() { + Index = Source = null; + throw SyntaxError(); + }; + + // Internal: Returns the next token, or `"$"` if the parser has reached + // the end of the source string. A token may be a string, number, `null` + // literal, or Boolean literal. + var lex = function () { + var source = Source, length = source.length, value, begin, position, isSigned, charCode; + while (Index < length) { + charCode = source.charCodeAt(Index); + switch (charCode) { + case 9: case 10: case 13: case 32: + // Skip whitespace tokens, including tabs, carriage returns, line + // feeds, and space characters. + Index++; + break; + case 123: case 125: case 91: case 93: case 58: case 44: + // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at + // the current position. + value = charIndexBuggy ? source.charAt(Index) : source[Index]; + Index++; + return value; + case 34: + // `"` delimits a JSON string; advance to the next character and + // begin parsing the string. String tokens are prefixed with the + // sentinel `@` character to distinguish them from punctuators and + // end-of-string tokens. + for (value = "@", Index++; Index < length;) { + charCode = source.charCodeAt(Index); + if (charCode < 32) { + // Unescaped ASCII control characters (those with a code unit + // less than the space character) are not permitted. + abort(); + } else if (charCode == 92) { + // A reverse solidus (`\`) marks the beginning of an escaped + // control character (including `"`, `\`, and `/`) or Unicode + // escape sequence. + charCode = source.charCodeAt(++Index); + switch (charCode) { + case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: + // Revive escaped control characters. + value += Unescapes[charCode]; + Index++; + break; + case 117: + // `\u` marks the beginning of a Unicode escape sequence. + // Advance to the first character and validate the + // four-digit code point. + begin = ++Index; + for (position = Index + 4; Index < position; Index++) { + charCode = source.charCodeAt(Index); + // A valid sequence comprises four hexdigits (case- + // insensitive) that form a single hexadecimal value. + if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { + // Invalid Unicode escape sequence. + abort(); + } + } + // Revive the escaped character. + value += fromCharCode("0x" + source.slice(begin, Index)); + break; + default: + // Invalid escape sequence. + abort(); + } + } else { + if (charCode == 34) { + // An unescaped double-quote character marks the end of the + // string. + break; + } + charCode = source.charCodeAt(Index); + begin = Index; + // Optimize for the common case where a string is valid. + while (charCode >= 32 && charCode != 92 && charCode != 34) { + charCode = source.charCodeAt(++Index); + } + // Append the string as-is. + value += source.slice(begin, Index); + } + } + if (source.charCodeAt(Index) == 34) { + // Advance to the next character and return the revived string. + Index++; + return value; + } + // Unterminated string. + abort(); + default: + // Parse numbers and literals. + begin = Index; + // Advance past the negative sign, if one is specified. + if (charCode == 45) { + isSigned = true; + charCode = source.charCodeAt(++Index); + } + // Parse an integer or floating-point value. + if (charCode >= 48 && charCode <= 57) { + // Leading zeroes are interpreted as octal literals. + if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { + // Illegal octal literal. + abort(); + } + isSigned = false; + // Parse the integer component. + for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); + // Floats cannot contain a leading decimal point; however, this + // case is already accounted for by the parser. + if (source.charCodeAt(Index) == 46) { + position = ++Index; + // Parse the decimal component. + for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal trailing decimal. + abort(); + } + Index = position; + } + // Parse exponents. The `e` denoting the exponent is + // case-insensitive. + charCode = source.charCodeAt(Index); + if (charCode == 101 || charCode == 69) { + charCode = source.charCodeAt(++Index); + // Skip past the sign following the exponent, if one is + // specified. + if (charCode == 43 || charCode == 45) { + Index++; + } + // Parse the exponential component. + for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); + if (position == Index) { + // Illegal empty exponent. + abort(); + } + Index = position; + } + // Coerce the parsed value to a JavaScript number. + return +source.slice(begin, Index); + } + // A negative sign may only precede numbers. + if (isSigned) { + abort(); + } + // `true`, `false`, and `null` literals. + if (source.slice(Index, Index + 4) == "true") { + Index += 4; + return true; + } else if (source.slice(Index, Index + 5) == "false") { + Index += 5; + return false; + } else if (source.slice(Index, Index + 4) == "null") { + Index += 4; + return null; + } + // Unrecognized token. + abort(); + } + } + // Return the sentinel `$` character if the parser has reached the end + // of the source string. + return "$"; + }; + + // Internal: Parses a JSON `value` token. + var get = function (value) { + var results, hasMembers; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { + // Remove the sentinel `@` character. + return value.slice(1); + } + // Parse object and array literals. + if (value == "[") { + // Parses a JSON array, returning a new JavaScript array. + results = []; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing square bracket marks the end of the array literal. + if (value == "]") { + break; + } + // If the array literal contains elements, the current token + // should be a comma separating the previous element from the + // next. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "]") { + // Unexpected trailing `,` in array literal. + abort(); + } + } else { + // A `,` must separate each array element. + abort(); + } + } + // Elisions and leading commas are not permitted. + if (value == ",") { + abort(); + } + results.push(get(value)); + } + return results; + } else if (value == "{") { + // Parses a JSON object, returning a new JavaScript object. + results = {}; + for (;; hasMembers || (hasMembers = true)) { + value = lex(); + // A closing curly brace marks the end of the object literal. + if (value == "}") { + break; + } + // If the object literal contains members, the current token + // should be a comma separator. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "}") { + // Unexpected trailing `,` in object literal. + abort(); + } + } else { + // A `,` must separate each object member. + abort(); + } + } + // Leading commas are not permitted, object property names must be + // double-quoted strings, and a `:` must separate each property + // name and value. + if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); + } + return results; + } + // Unexpected token encountered. + abort(); + } + return value; + }; + + // Internal: Updates a traversed object member. + var update = function(source, property, callback) { + var element = walk(source, property, callback); + if (element === undef) { + delete source[property]; + } else { + source[property] = element; + } + }; + + // Internal: Recursively traverses a parsed JSON object, invoking the + // `callback` function for each value. This is an implementation of the + // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. + var walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + // `forEach` can't be used to traverse an array in Opera <= 8.54 + // because its `Object#hasOwnProperty` implementation returns `false` + // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). + if (getClass.call(value) == arrayClass) { + for (length = value.length; length--;) { + update(value, length, callback); + } + } else { + forEach(value, function (property) { + update(value, property, callback); + }); + } + } + return callback.call(source, property, value); + }; + + // Public: `JSON.parse`. See ES 5.1 section 15.12.2. + JSON3.parse = function (source, callback) { + var result, value; + Index = 0; + Source = "" + source; + result = get(lex()); + // If a JSON string contains multiple tokens, it is invalid. + if (lex() != "$") { + abort(); + } + // Reset the parser state. + Index = Source = null; + return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; + } } // Export for asynchronous module loaders. @@ -31794,10 +31521,9 @@ arguments[4][117][0].apply(exports,arguments) return JSON3; }); } -}).call(this); +}(this)); -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],133:[function(require,module,exports){ +},{}],135:[function(require,module,exports){ module.exports = toArray function toArray(list, index) { @@ -31812,14 +31538,14 @@ function toArray(list, index) { return array } -},{}],134:[function(require,module,exports){ +},{}],136:[function(require,module,exports){ var MediaStreamType = { VIDEO_TYPE: "Video", AUDIO_TYPE: "Audio" }; module.exports = MediaStreamType; -},{}],135:[function(require,module,exports){ +},{}],137:[function(require,module,exports){ var RTCEvents = { RTC_READY: "rtc.ready", DATA_CHANNEL_OPEN: "rtc.data_channel_open", @@ -31831,7 +31557,7 @@ var RTCEvents = { }; module.exports = RTCEvents; -},{}],136:[function(require,module,exports){ +},{}],138:[function(require,module,exports){ var Resolutions = { "1080": { width: 1920, @@ -31885,7 +31611,7 @@ var Resolutions = { } }; module.exports = Resolutions; -},{}],137:[function(require,module,exports){ +},{}],139:[function(require,module,exports){ var AuthenticationEvents = { /** * Event callback arguments: @@ -31899,7 +31625,7 @@ var AuthenticationEvents = { }; module.exports = AuthenticationEvents; -},{}],138:[function(require,module,exports){ +},{}],140:[function(require,module,exports){ var DesktopSharingEventTypes = { /** * An event which indicates that the jidesha extension for Firefox is @@ -31910,7 +31636,7 @@ var DesktopSharingEventTypes = { module.exports = DesktopSharingEventTypes; -},{}],139:[function(require,module,exports){ +},{}],141:[function(require,module,exports){ module.exports = { /** * An event carrying connection statistics. @@ -31926,12 +31652,12 @@ module.exports = { STOP: "statistics.stop" }; -},{}],140:[function(require,module,exports){ +},{}],142:[function(require,module,exports){ var Constants = { LOCAL_JID: 'local' }; module.exports = Constants; -},{}],141:[function(require,module,exports){ +},{}],143:[function(require,module,exports){ var XMPPEvents = { // Designates an event indicating that the connection to the XMPP server // failed. diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index ae1852b60..7ec01cd0b 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -249,7 +249,6 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) { UIUtils.prependChild(this.container, streamElement); let sel = $(`#${newElementId}`); - sel.hide(); // If the container is currently visible we attach the stream. if (!isVideo || (this.container.offsetParent !== null && isVideo)) { @@ -258,6 +257,11 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) { stream.attach(sel); } + // hide element only after stream was (maybe) attached + // because Temasys plugin requires video element + // to be visible to attach the stream + sel.hide(); + // reselect if (RTCBrowserType.isTemasysPluginUsed()) { sel = $(`#${newElementId}`);