From 58e9f4930a1c158fc766e6878f60ddd91d84ad57 Mon Sep 17 00:00:00 2001 From: Contra Date: Mon, 18 Feb 2013 19:50:29 -0700 Subject: [PATCH] firefox support done --- Makefile | 4 +- component.json | 3 +- dist/Call.js | 182 ++++++++++ dist/RTC.js | 125 +++++++ dist/holla.js | 233 ++++++++++++ examples/holla.js | 905 ++++++++++++++++++++++++++-------------------- holla.js | 905 ++++++++++++++++++++++++++-------------------- holla.min.js | 4 +- index.js | 2 +- lib/Call.coffee | 2 +- lib/RTC.coffee | 38 +- lib/holla.coffee | 6 +- 12 files changed, 1599 insertions(+), 810 deletions(-) create mode 100644 dist/Call.js create mode 100644 dist/RTC.js create mode 100644 dist/holla.js diff --git a/Makefile b/Makefile index c731fbf..ecb597f 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ build: components lib @rm -rf dist @mkdir -p dist - @coffee -b -o dist -c lib/holla.coffee lib/Call.coffee lib/RTC.coffee + @coffee -o dist -c lib/holla.coffee lib/Call.coffee lib/RTC.coffee @component build --standalone holla @mv build/build.js holla.js - @rm -rf build dist + @rm -rf build @node_modules/.bin/uglifyjs -nc --unsafe -mt -o holla.min.js holla.js @echo "File size (minified): " && cat holla.min.js | wc -c @echo "File size (gzipped): " && cat holla.min.js | gzip -9f | wc -c diff --git a/component.json b/component.json index ec224c6..5a2884b 100644 --- a/component.json +++ b/component.json @@ -8,6 +8,7 @@ "main": "dist/holla.js", "scripts": [ "dist/holla.js", - "dist/Call.js" + "dist/Call.js", + "dist/RTC.js" ] } \ No newline at end of file diff --git a/dist/Call.js b/dist/Call.js new file mode 100644 index 0000000..d5ffa5e --- /dev/null +++ b/dist/Call.js @@ -0,0 +1,182 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Call, EventEmitter, RTC, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + RTC = require('./RTC'); + + EventEmitter = require('emitter'); + + Call = (function(_super) { + + __extends(Call, _super); + + function Call(parent, user, isCaller) { + var _this = this; + this.parent = parent; + this.user = user; + this.isCaller = isCaller; + this.startTime = new Date; + this.socket = this.parent.ssocket; + this.pc = this.createConnection(); + if (this.isCaller) { + this.socket.write({ + type: "offer", + to: this.user + }); + } + this.emit("calling"); + this.parent.on("answer." + this.user, function(accepted) { + if (!accepted) { + return _this.emit("rejected"); + } + _this.emit("answered"); + return _this.initSDP(); + }); + this.parent.on("candidate." + this.user, function(candidate) { + return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); + }); + this.parent.on("sdp." + this.user, function(stuff) { + _this.pc.setRemoteDescription(new RTC.SessionDescription(stuff)); + return _this.emit("sdp"); + }); + this.parent.on("hangup." + this.user, function() { + return _this.emit("hangup"); + }); + this.parent.on("chat." + this.user, function(msg) { + return _this.emit("chat", msg); + }); + } + + Call.prototype.createConnection = function() { + var pc, + _this = this; + pc = new RTC.PeerConnection(RTC.PeerConnConfig); + pc.onconnecting = function() { + _this.emit('connecting'); + }; + pc.onopen = function() { + _this.emit('connected'); + }; + pc.onicecandidate = function(evt) { + if (evt.candidate) { + _this.socket.write({ + type: "candidate", + to: _this.user, + args: { + candidate: evt.candidate + } + }); + } + }; + pc.onaddstream = function(evt) { + _this.remoteStream = evt.stream; + _this._ready = true; + _this.emit("ready", _this.remoteStream); + }; + pc.onremovestream = function(evt) { + console.log("removestream", evt); + }; + return pc; + }; + + Call.prototype.addStream = function(s) { + this.pc.addStream(s); + return this; + }; + + Call.prototype.ready = function(fn) { + if (this._ready) { + fn(this.remoteStream); + } else { + this.once('ready', fn); + } + return this; + }; + + Call.prototype.duration = function() { + var e, s; + if (this.endTime != null) { + s = this.endTime.getTime(); + } + if (s == null) { + s = Date.now(); + } + e = this.startTime.getTime(); + return (s - e) / 1000; + }; + + Call.prototype.chat = function(msg) { + this.parent.chat(this.user, msg); + return this; + }; + + Call.prototype.answer = function() { + this.startTime = new Date; + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: true + } + }); + this.initSDP(); + return this; + }; + + Call.prototype.decline = function() { + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: false + } + }); + return this; + }; + + Call.prototype.end = function() { + this.endTime = new Date; + this.pc.close(); + this.socket.write({ + type: "hangup", + to: this.user + }); + this.emit("hangup"); + return this; + }; + + Call.prototype.initSDP = function() { + var done, err, + _this = this; + done = function(desc) { + desc.sdp = RTC.processSDP(desc.sdp); + _this.pc.setLocalDescription(desc); + return _this.socket.write({ + type: "sdp", + to: _this.user, + args: desc + }); + }; + err = function(e) { + return console.log(e); + }; + if (this.isCaller) { + return this.pc.createOffer(done, err); + } + if (this.pc.remoteDescription) { + return this.pc.createAnswer(done, err); + } + return this.once("sdp", function() { + return _this.pc.createAnswer(done, err); + }); + }; + + return Call; + + })(EventEmitter); + + module.exports = Call; + +}).call(this); diff --git a/dist/RTC.js b/dist/RTC.js new file mode 100644 index 0000000..d20a4ea --- /dev/null +++ b/dist/RTC.js @@ -0,0 +1,125 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var IceCandidate, MediaStream, PeerConnection, SessionDescription, URL, attachStream, browser, getUserMedia, processSDP, shim, supported, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; + + IceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + + SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; + + MediaStream = window.webkitMediaStream || window.MediaStream; + + getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia).bind(navigator); + + URL = window.URL || window.webkitURL || window.msURL || window.oURL; + + browser = (navigator.mozGetUserMedia ? 'firefox' : 'chrome'); + + supported = (PeerConnection != null) && (getUserMedia != null); + + processSDP = function(sdp) { + var addCrypto, line, out, _i, _len, _ref; + if (browser !== 'mozilla') { + return sdp; + } + addCrypto = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + out = []; + _ref = sdp.split('\r\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + out.push(line); + if ((__indexOf.call(search, 'm=') >= 0) !== -1) { + out.push(addCrypto); + } + } + return out.join('\r\n'); + }; + + attachStream = function(uri, el) { + var e, srcAttr, _i, _len; + srcAttr = (browser === 'mozilla' ? 'mozSrcObject' : 'src'); + if (typeof el === "string") { + return attachStream(uri, document.getElementById(el)); + } else if (el.jquery) { + el.attr(srcAttr, uri); + for (_i = 0, _len = el.length; _i < _len; _i++) { + e = el[_i]; + e.play(); + } + } else { + el[srcAttr] = uri; + el.play(); + } + return el; + }; + + shim = function() { + var PeerConnConfig, out; + if (!supported) { + return; + } + if (browser === 'mozilla') { + PeerConnConfig = { + iceServers: [ + { + url: "stun:23.21.150.121" + } + ], + optional: [] + }; + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } else { + PeerConnConfig = { + iceServers: [ + { + url: "stun:stun.l.google.com:19302" + } + ], + optional: [ + { + DtlsSrtpKeyAgreement: true + } + ] + }; + if (MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return this.videoTracks; + }; + MediaStream.prototype.getAudioTracks = function() { + return this.audioTracks; + }; + } + if (PeerConnection.prototype.getLocalStreams) { + PeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + PeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + } + } + out = { + PeerConnection: PeerConnection, + IceCandidate: IceCandidate, + SessionDescription: SessionDescription, + MediaStream: MediaStream, + getUserMedia: getUserMedia, + URL: URL, + attachStream: attachStream, + processSDP: processSDP, + PeerConnConfig: PeerConnConfig, + supported: supported + }; + return out; + }; + + module.exports = shim(); + +}).call(this); diff --git a/dist/holla.js b/dist/holla.js new file mode 100644 index 0000000..46faf9a --- /dev/null +++ b/dist/holla.js @@ -0,0 +1,233 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var Call, ProtoSock, RTC, client, holla; + + Call = require('./Call'); + + RTC = require('./RTC'); + + ProtoSock = require('protosock'); + + client = { + options: { + namespace: 'holla', + resource: 'default', + debug: false + }, + register: function(name, cb) { + var _this = this; + this.ssocket.write({ + type: "register", + args: { + name: name + } + }); + return this.once("register", function(worked) { + if (worked) { + _this.user = name; + _this.emit("authorized"); + } + _this.authorized = worked; + return typeof cb === "function" ? cb(worked) : void 0; + }); + }, + call: function(user) { + return new Call(this, user, true); + }, + chat: function(user, msg) { + this.ssocket.write({ + type: "chat", + to: user, + args: { + message: msg + } + }); + return this; + }, + ready: function(fn) { + if (this.authorized) { + fn(); + } else { + this.once('authorized', fn); + } + return this; + }, + validate: function(socket, msg, done) { + if (this.options.debug) { + console.log(msg); + } + if (typeof msg !== 'object') { + return done(false); + } + if (typeof msg.type !== 'string') { + return done(false); + } + if (msg.type === "register") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.result !== 'boolean') { + return done(false); + } + } else if (msg.type === "offer") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "answer") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.accepted !== 'boolean') { + return done(false); + } + } else if (msg.type === "sdp") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (!msg.args.sdp) { + return done(false); + } + if (!msg.args.type) { + return done(false); + } + } else if (msg.type === "candidate") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.candidate !== 'object') { + return done(false); + } + } else if (msg.type === "chat") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.message !== 'string') { + return done(false); + } + } else if (msg.type === "hangup") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "presence") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.name !== 'string') { + return done(false); + } + if (typeof msg.args.online !== 'boolean') { + return done(false); + } + } else { + return done(false); + } + return done(true); + }, + error: function(socket, err) { + return this.emit('error', err, socket); + }, + message: function(socket, msg) { + var c; + switch (msg.type) { + case "register": + return this.emit("register", msg.args.result); + case "offer": + c = new Call(this, msg.from, false); + return this.emit("call", c); + case "presence": + this.emit("presence", msg.args); + return this.emit("presence." + msg.args.name, msg.args.online); + case "chat": + this.emit("chat", { + from: msg.from, + message: msg.args.message + }); + return this.emit("chat." + msg.from, msg.args.message); + case "hangup": + this.emit("hangup", { + from: msg.from + }); + return this.emit("hangup." + msg.from); + case "answer": + this.emit("answer", { + from: msg.from, + accepted: msg.args.accepted + }); + return this.emit("answer." + msg.from, msg.args.accepted); + case "candidate": + this.emit("candidate", { + from: msg.from, + candidate: msg.args.candidate + }); + return this.emit("candidate." + msg.from, msg.args.candidate); + case "sdp": + this.emit("sdp", { + from: msg.from, + sdp: msg.args.sdp, + type: msg.args.type + }); + return this.emit("sdp." + msg.from, msg.args); + } + } + }; + + holla = { + createClient: ProtoSock.createClientWrapper(client), + Call: Call, + supported: RTC.supported, + config: RTC.PeerConnConfig, + streamToBlob: function(s) { + return RTC.URL.createObjectURL(s); + }, + pipe: function(stream, el) { + var uri; + uri = holla.streamToBlob(stream); + return RTC.attachStream(uri, el); + }, + createStream: function(opt, cb) { + var err, succ; + if (RTC.getUserMedia == null) { + return cb("Missing getUserMedia"); + } + err = cb; + succ = function(s) { + return cb(null, s); + }; + RTC.getUserMedia(opt, succ, err); + return holla; + }, + createFullStream: function(cb) { + return holla.createStream({ + video: true, + audio: true + }, cb); + }, + createVideoStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); + }, + createAudioStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); + } + }; + + module.exports = holla; + +}).call(this); diff --git a/examples/holla.js b/examples/holla.js index 1830699..280eafc 100644 --- a/examples/holla.js +++ b/examples/holla.js @@ -456,9 +456,7 @@ debug.skips = []; */ debug.enable = function(name) { - try { - localStorage.debug = name; - } catch(e){} + localStorage.debug = name; var split = (name || '').split(/[\s,]+/) , len = split.length; @@ -528,7 +526,6 @@ debug.enabled = function(name) { // persist if (window.localStorage) debug.enable(localStorage.debug); - }); require.register("LearnBoost-engine.io-client/lib/index.js", function(exports, require, module){ @@ -605,6 +602,7 @@ function Socket(uri, opts){ location.port : (this.secure ? 443 : 80)); this.query = opts.query || {}; + this.query.uid = rnd(); this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; @@ -1058,6 +1056,17 @@ Socket.prototype.filterUpgrades = function (upgrades) { } return filteredUpgrades; }; + +/** + * Generates a random uid. + * + * @api private + */ + +function rnd () { + return String(Math.random()).substr(5) + String(Math.random()).substr(5); +} + }); require.register("LearnBoost-engine.io-client/lib/transport.js", function(exports, require, module){ @@ -2224,7 +2233,6 @@ JSONPPolling.prototype.doClose = function () { */ JSONPPolling.prototype.doPoll = function () { - var self = this; var script = document.createElement('script'); if (this.script) { @@ -2234,15 +2242,11 @@ JSONPPolling.prototype.doPoll = function () { script.async = true; script.src = this.uri(); - script.onerror = function(e){ - self.onError('jsonp poll error',e); - } var insertAt = document.getElementsByTagName('script')[0]; insertAt.parentNode.insertBefore(script, insertAt); this.script = script; - if (util.ua.gecko) { setTimeout(function () { var iframe = document.createElement('iframe'); @@ -2951,7 +2955,7 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ __extends(Client, _super); function Client(plugin, options) { - var eiopts, k, v, _base, _base1, _base2, _ref, _ref1, _ref2; + var eiopts, k, v, _base, _base1, _ref, _ref1; if (options == null) { options = {}; } @@ -2979,9 +2983,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if ((_ref1 = (_base1 = this.options).reconnectLimit) == null) { _base1.reconnectLimit = Infinity; } - if ((_ref2 = (_base2 = this.options).reconnectTimeout) == null) { - _base2.reconnectTimeout = Infinity; - } this.isServer = false; this.isClient = true; this.isBrowser = typeof window !== "undefined" && window !== null; @@ -3014,13 +3015,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ return this; }; - Client.prototype.destroy = function() { - this.options.reconnect = false; - this.ssocket.disconnect(); - this.emit("destroyed"); - return this; - }; - Client.prototype.handleConnection = function() { this.emit('connected'); return this.connect(this.ssocket); @@ -3069,7 +3063,7 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ }; Client.prototype.reconnect = function(cb) { - var attempts, connect, done, err, maxAttempts, start, timeout, + var attempts, connect, done, err, maxAttempts, _this = this; if (this.ssocket.reconnecting) { return cb("Already reconnecting"); @@ -3078,13 +3072,10 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if (this.ssocket.readyState === 'open') { this.ssocket.disconnect(); } - start = Date.now(); maxAttempts = this.options.reconnectLimit; - timeout = this.options.reconnectTimeout; attempts = 0; done = function() { _this.ssocket.reconnecting = false; - _this.emit("reconnected"); return cb(); }; err = function(e) { @@ -3099,9 +3090,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if (attempts >= maxAttempts) { return err("Exceeded max attempts"); } - if ((Date.now() - start) > timeout) { - return err("Timeout on reconnect"); - } attempts++; _this.ssocket.open(); return setTimeout(connect, getDelay(attempts)); @@ -3271,416 +3259,551 @@ Emitter.prototype.hasListeners = function(event){ }); require.register("holla/dist/holla.js", function(exports, require, module){ // Generated by CoffeeScript 1.4.0 -var Call, ProtoSock, RTC, client, holla; - -Call = require('./Call'); - -ProtoSock = require('protosock'); - -RTC = require('./RTC'); - -client = { - options: { - namespace: 'holla', - resource: 'default', - debug: false - }, - register: function(name, cb) { - var _this = this; - this.ssocket.write({ - type: "register", - args: { - name: name - } - }); - return this.once("register", function(worked) { - if (worked) { - _this.user = name; - _this.emit("authorized"); - } - _this.authorized = worked; - return typeof cb === "function" ? cb(worked) : void 0; - }); - }, - call: function(user) { - return new Call(this, user, true); - }, - chat: function(user, msg) { - this.ssocket.write({ - type: "chat", - to: user, - args: { - message: msg - } - }); - return this; - }, - ready: function(fn) { - if (this.authorized) { - fn(); - } else { - this.once('authorized', fn); - } - return this; - }, - validate: function(socket, msg, done) { - if (this.options.debug) { - console.log(msg); - } - if (typeof msg !== 'object') { - return done(false); - } - if (typeof msg.type !== 'string') { - return done(false); - } - if (msg.type === "register") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.args.result !== 'boolean') { - return done(false); - } - } else if (msg.type === "offer") { - if (typeof msg.from !== 'string') { - return done(false); - } - } else if (msg.type === "answer") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (typeof msg.args.accepted !== 'boolean') { - return done(false); - } - } else if (msg.type === "sdp") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (!msg.args.sdp) { - return done(false); - } - if (!msg.args.type) { - return done(false); - } - } else if (msg.type === "candidate") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (typeof msg.args.candidate !== 'object') { - return done(false); - } - } else if (msg.type === "chat") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); +(function() { + var Call, ProtoSock, RTC, client, holla; + + Call = require('./Call'); + + RTC = require('./RTC'); + + ProtoSock = require('protosock'); + + client = { + options: { + namespace: 'holla', + resource: 'default', + debug: false + }, + register: function(name, cb) { + var _this = this; + this.ssocket.write({ + type: "register", + args: { + name: name + } + }); + return this.once("register", function(worked) { + if (worked) { + _this.user = name; + _this.emit("authorized"); + } + _this.authorized = worked; + return typeof cb === "function" ? cb(worked) : void 0; + }); + }, + call: function(user) { + return new Call(this, user, true); + }, + chat: function(user, msg) { + this.ssocket.write({ + type: "chat", + to: user, + args: { + message: msg + } + }); + return this; + }, + ready: function(fn) { + if (this.authorized) { + fn(); + } else { + this.once('authorized', fn); } - if (typeof msg.args.message !== 'string') { - return done(false); + return this; + }, + validate: function(socket, msg, done) { + if (this.options.debug) { + console.log(msg); } - } else if (msg.type === "hangup") { - if (typeof msg.from !== 'string') { + if (typeof msg !== 'object') { return done(false); } - } else if (msg.type === "presence") { - if (typeof msg.args !== 'object') { + if (typeof msg.type !== 'string') { return done(false); } - if (typeof msg.args.name !== 'string') { + if (msg.type === "register") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.result !== 'boolean') { + return done(false); + } + } else if (msg.type === "offer") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "answer") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.accepted !== 'boolean') { + return done(false); + } + } else if (msg.type === "sdp") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (!msg.args.sdp) { + return done(false); + } + if (!msg.args.type) { + return done(false); + } + } else if (msg.type === "candidate") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.candidate !== 'object') { + return done(false); + } + } else if (msg.type === "chat") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.message !== 'string') { + return done(false); + } + } else if (msg.type === "hangup") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "presence") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.name !== 'string') { + return done(false); + } + if (typeof msg.args.online !== 'boolean') { + return done(false); + } + } else { return done(false); } - if (typeof msg.args.online !== 'boolean') { - return done(false); + return done(true); + }, + error: function(socket, err) { + return this.emit('error', err, socket); + }, + message: function(socket, msg) { + var c; + switch (msg.type) { + case "register": + return this.emit("register", msg.args.result); + case "offer": + c = new Call(this, msg.from, false); + return this.emit("call", c); + case "presence": + this.emit("presence", msg.args); + return this.emit("presence." + msg.args.name, msg.args.online); + case "chat": + this.emit("chat", { + from: msg.from, + message: msg.args.message + }); + return this.emit("chat." + msg.from, msg.args.message); + case "hangup": + this.emit("hangup", { + from: msg.from + }); + return this.emit("hangup." + msg.from); + case "answer": + this.emit("answer", { + from: msg.from, + accepted: msg.args.accepted + }); + return this.emit("answer." + msg.from, msg.args.accepted); + case "candidate": + this.emit("candidate", { + from: msg.from, + candidate: msg.args.candidate + }); + return this.emit("candidate." + msg.from, msg.args.candidate); + case "sdp": + this.emit("sdp", { + from: msg.from, + sdp: msg.args.sdp, + type: msg.args.type + }); + return this.emit("sdp." + msg.from, msg.args); } - } else { - return done(false); - } - return done(true); - }, - error: function(socket, err) { - return this.emit('error', err, socket); - }, - message: function(socket, msg) { - var c; - switch (msg.type) { - case "register": - return this.emit("register", msg.args.result); - case "offer": - c = new Call(this, msg.from, false); - return this.emit("call", c); - case "presence": - this.emit("presence", msg.args); - return this.emit("presence." + msg.args.name, msg.args.online); - case "chat": - this.emit("chat", { - from: msg.from, - message: msg.args.message - }); - return this.emit("chat." + msg.from, msg.args.message); - case "hangup": - this.emit("hangup", { - from: msg.from - }); - return this.emit("hangup." + msg.from); - case "answer": - this.emit("answer", { - from: msg.from, - accepted: msg.args.accepted - }); - return this.emit("answer." + msg.from, msg.args.accepted); - case "candidate": - this.emit("candidate", { - from: msg.from, - candidate: msg.args.candidate - }); - return this.emit("candidate." + msg.from, msg.args.candidate); - case "sdp": - this.emit("sdp", { - from: msg.from, - sdp: msg.args.sdp, - type: msg.args.type - }); - return this.emit("sdp." + msg.from, msg.args); } - } -}; + }; -holla = { - createClient: ProtoSock.createClientWrapper(client), - Call: Call, - supported: supported, - streamToBlob: function(s) { - return RTC.URL.createObjectURL(s); - }, - pipe: function(stream, el) { - var uri; - uri = holla.streamToBlob(stream); - return RTC.attachStream(uri, el); - }, - createStream: function(opt, cb) { - var err, succ; - if (RTC.getUserMedia == null) { - return cb("Missing getUserMedia"); + holla = { + createClient: ProtoSock.createClientWrapper(client), + Call: Call, + supported: RTC.supported, + config: RTC.PeerConnConfig, + streamToBlob: function(s) { + return RTC.URL.createObjectURL(s); + }, + pipe: function(stream, el) { + var uri; + uri = holla.streamToBlob(stream); + return RTC.attachStream(uri, el); + }, + createStream: function(opt, cb) { + var err, succ; + if (RTC.getUserMedia == null) { + return cb("Missing getUserMedia"); + } + err = cb; + succ = function(s) { + return cb(null, s); + }; + RTC.getUserMedia(opt, succ, err); + return holla; + }, + createFullStream: function(cb) { + return holla.createStream({ + video: true, + audio: true + }, cb); + }, + createVideoStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); + }, + createAudioStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); } - err = cb; - succ = function(s) { - return cb(null, s); - }; - RTC.getUserMedia(opt, succ, err); - return holla; - }, - createFullStream: function(cb) { - return holla.createStream({ - video: true, - audio: true - }, cb); - }, - createVideoStream: function(cb) { - return holla.createStream({ - video: true, - audio: false - }, cb); - }, - createAudioStream: function(cb) { - return holla.createStream({ - video: true, - audio: false - }, cb); - } -}; - -module.exports = holla; + }; + + module.exports = holla; + +}).call(this); }); require.register("holla/dist/Call.js", function(exports, require, module){ // Generated by CoffeeScript 1.4.0 -var Call, EventEmitter, RTC, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +(function() { + var Call, EventEmitter, RTC, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; -RTC = require('./RTC'); + RTC = require('./RTC'); -EventEmitter = require('emitter'); + EventEmitter = require('emitter'); -Call = (function(_super) { + Call = (function(_super) { - __extends(Call, _super); + __extends(Call, _super); - function Call(parent, user, isCaller) { - var _this = this; - this.parent = parent; - this.user = user; - this.isCaller = isCaller; - this.startTime = new Date; - this.socket = this.parent.ssocket; - this.pc = this.createConnection(); - if (this.isCaller) { - this.socket.write({ - type: "offer", - to: this.user + function Call(parent, user, isCaller) { + var _this = this; + this.parent = parent; + this.user = user; + this.isCaller = isCaller; + this.startTime = new Date; + this.socket = this.parent.ssocket; + this.pc = this.createConnection(); + if (this.isCaller) { + this.socket.write({ + type: "offer", + to: this.user + }); + } + this.emit("calling"); + this.parent.on("answer." + this.user, function(accepted) { + if (!accepted) { + return _this.emit("rejected"); + } + _this.emit("answered"); + return _this.initSDP(); + }); + this.parent.on("candidate." + this.user, function(candidate) { + return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); + }); + this.parent.on("sdp." + this.user, function(stuff) { + _this.pc.setRemoteDescription(new RTC.SessionDescription(stuff)); + return _this.emit("sdp"); + }); + this.parent.on("hangup." + this.user, function() { + return _this.emit("hangup"); + }); + this.parent.on("chat." + this.user, function(msg) { + return _this.emit("chat", msg); }); } - this.emit("calling"); - this.parent.on("answer." + this.user, function(accepted) { - if (!accepted) { - return _this.emit("rejected"); + + Call.prototype.createConnection = function() { + var pc, + _this = this; + pc = new RTC.PeerConnection(RTC.PeerConnConfig); + pc.onconnecting = function() { + _this.emit('connecting'); + }; + pc.onopen = function() { + _this.emit('connected'); + }; + pc.onicecandidate = function(evt) { + if (evt.candidate) { + _this.socket.write({ + type: "candidate", + to: _this.user, + args: { + candidate: evt.candidate + } + }); + } + }; + pc.onaddstream = function(evt) { + _this.remoteStream = evt.stream; + _this._ready = true; + _this.emit("ready", _this.remoteStream); + }; + pc.onremovestream = function(evt) { + console.log("removestream", evt); + }; + return pc; + }; + + Call.prototype.addStream = function(s) { + this.pc.addStream(s); + return this; + }; + + Call.prototype.ready = function(fn) { + if (this._ready) { + fn(this.remoteStream); + } else { + this.once('ready', fn); } - _this.emit("answered"); - return _this.initSDP(); - }); - this.parent.on("candidate." + this.user, function(candidate) { - return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); - }); - this.parent.on("sdp." + this.user, function(stuff) { - _this.pc.setRemoteDescription(new RTC.SessionDescription(stuff)); - return _this.emit("sdp"); - }); - this.parent.on("hangup." + this.user, function() { - return _this.emit("hangup"); - }); - this.parent.on("chat." + this.user, function(msg) { - return _this.emit("chat", msg); - }); - } + return this; + }; + + Call.prototype.duration = function() { + var e, s; + if (this.endTime != null) { + s = this.endTime.getTime(); + } + if (s == null) { + s = Date.now(); + } + e = this.startTime.getTime(); + return (s - e) / 1000; + }; + + Call.prototype.chat = function(msg) { + this.parent.chat(this.user, msg); + return this; + }; + + Call.prototype.answer = function() { + this.startTime = new Date; + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: true + } + }); + this.initSDP(); + return this; + }; - Call.prototype.createConnection = function() { - var pc, - _this = this; - pc = new RTC.PeerConnection(PeerConnConfig); - pc.onconnecting = function() { - _this.emit('connecting'); + Call.prototype.decline = function() { + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: false + } + }); + return this; }; - pc.onopen = function() { - _this.emit('connected'); + + Call.prototype.end = function() { + this.endTime = new Date; + this.pc.close(); + this.socket.write({ + type: "hangup", + to: this.user + }); + this.emit("hangup"); + return this; }; - pc.onicecandidate = function(evt) { - if (evt.candidate) { - _this.socket.write({ - type: "candidate", + + Call.prototype.initSDP = function() { + var done, err, + _this = this; + done = function(desc) { + desc.sdp = RTC.processSDP(desc.sdp); + _this.pc.setLocalDescription(desc); + return _this.socket.write({ + type: "sdp", to: _this.user, - args: { - candidate: evt.candidate - } + args: desc }); + }; + err = function(e) { + return console.log(e); + }; + if (this.isCaller) { + return this.pc.createOffer(done, err); } + if (this.pc.remoteDescription) { + return this.pc.createAnswer(done, err); + } + return this.once("sdp", function() { + return _this.pc.createAnswer(done, err); + }); }; - pc.onaddstream = function(evt) { - _this.remoteStream = evt.stream; - _this._ready = true; - _this.emit("ready", _this.remoteStream); - }; - pc.onremovestream = function(evt) { - console.log("removestream", evt); - }; - return pc; - }; - Call.prototype.addStream = function(s) { - this.pc.addStream(s); - return this; - }; + return Call; - Call.prototype.ready = function(fn) { - if (this._ready) { - fn(this.remoteStream); - } else { - this.once('ready', fn); - } - return this; - }; + })(EventEmitter); - Call.prototype.duration = function() { - var e, s; - if (this.endTime != null) { - s = this.endTime.getTime(); - } - if (s == null) { - s = Date.now(); - } - e = this.startTime.getTime(); - return (s - e) / 1000; - }; + module.exports = Call; - Call.prototype.chat = function(msg) { - this.parent.chat(this.user, msg); - return this; - }; +}).call(this); - Call.prototype.answer = function() { - this.startTime = new Date; - this.socket.write({ - type: "answer", - to: this.user, - args: { - accepted: true - } - }); - this.initSDP(); - return this; - }; +}); +require.register("holla/dist/RTC.js", function(exports, require, module){ +// Generated by CoffeeScript 1.4.0 +(function() { + var IceCandidate, MediaStream, PeerConnection, SessionDescription, URL, attachStream, browser, getUserMedia, processSDP, shim, supported, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; + + IceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + + SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; + + MediaStream = window.webkitMediaStream || window.MediaStream; + + getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia).bind(navigator); + + URL = window.URL || window.webkitURL || window.msURL || window.oURL; + + browser = (navigator.mozGetUserMedia ? 'firefox' : 'chrome'); - Call.prototype.decline = function() { - this.socket.write({ - type: "answer", - to: this.user, - args: { - accepted: false + supported = (PeerConnection != null) && (getUserMedia != null); + + processSDP = function(sdp) { + var addCrypto, line, out, _i, _len, _ref; + if (browser !== 'mozilla') { + return sdp; + } + addCrypto = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + out = []; + _ref = sdp.split('\r\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + out.push(line); + if ((__indexOf.call(search, 'm=') >= 0) !== -1) { + out.push(addCrypto); } - }); - return this; + } + return out.join('\r\n'); }; - Call.prototype.end = function() { - this.endTime = new Date; - this.pc.close(); - this.socket.write({ - type: "hangup", - to: this.user - }); - this.emit("hangup"); - return this; + attachStream = function(uri, el) { + var e, srcAttr, _i, _len; + srcAttr = (browser === 'mozilla' ? 'mozSrcObject' : 'src'); + if (typeof el === "string") { + return attachStream(uri, document.getElementById(el)); + } else if (el.jquery) { + el.attr(srcAttr, uri); + for (_i = 0, _len = el.length; _i < _len; _i++) { + e = el[_i]; + e.play(); + } + } else { + el[srcAttr] = uri; + el.play(); + } + return el; }; - Call.prototype.initSDP = function() { - var done, err, - _this = this; - done = function(desc) { - desc.sdp = RTC.processSDP(desc.sdp); - _this.pc.setLocalDescription(desc); - return _this.socket.write({ - type: "sdp", - to: _this.user, - args: desc - }); - }; - err = function(e) { - return console.log(e); - }; - if (this.isCaller) { - return this.pc.createOffer(done, err); + shim = function() { + var PeerConnConfig, out; + if (!supported) { + return; } - if (this.pc.remoteDescription) { - return this.pc.createAnswer(done, err); + if (browser === 'mozilla') { + PeerConnConfig = { + iceServers: [ + { + url: "stun:23.21.150.121" + } + ], + optional: [] + }; + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } else { + PeerConnConfig = { + iceServers: [ + { + url: "stun:stun.l.google.com:19302" + } + ], + optional: [ + { + DtlsSrtpKeyAgreement: true + } + ] + }; + if (MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return this.videoTracks; + }; + MediaStream.prototype.getAudioTracks = function() { + return this.audioTracks; + }; + } + if (PeerConnection.prototype.getLocalStreams) { + PeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + PeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + } } - return this.once("sdp", function() { - return _this.pc.createAnswer(done, err); - }); + out = { + PeerConnection: PeerConnection, + IceCandidate: IceCandidate, + SessionDescription: SessionDescription, + MediaStream: MediaStream, + getUserMedia: getUserMedia, + URL: URL, + attachStream: attachStream, + processSDP: processSDP, + PeerConnConfig: PeerConnConfig, + supported: supported + }; + return out; }; - return Call; + module.exports = shim(); -})(EventEmitter); - -module.exports = Call; +}).call(this); }); require.alias("wearefractal-protosock/dist/main.js", "holla/deps/protosock/dist/main.js"); @@ -3724,7 +3847,7 @@ require.alias("holla/dist/holla.js", "holla/index.js"); if (typeof exports == "object") { module.exports = require("holla"); } else if (typeof define == "function" && define.amd) { - define(function(){ return require("holla"); }); + define(require("holla")); } else { window["holla"] = require("holla"); }})(); \ No newline at end of file diff --git a/holla.js b/holla.js index 1830699..280eafc 100644 --- a/holla.js +++ b/holla.js @@ -456,9 +456,7 @@ debug.skips = []; */ debug.enable = function(name) { - try { - localStorage.debug = name; - } catch(e){} + localStorage.debug = name; var split = (name || '').split(/[\s,]+/) , len = split.length; @@ -528,7 +526,6 @@ debug.enabled = function(name) { // persist if (window.localStorage) debug.enable(localStorage.debug); - }); require.register("LearnBoost-engine.io-client/lib/index.js", function(exports, require, module){ @@ -605,6 +602,7 @@ function Socket(uri, opts){ location.port : (this.secure ? 443 : 80)); this.query = opts.query || {}; + this.query.uid = rnd(); this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; @@ -1058,6 +1056,17 @@ Socket.prototype.filterUpgrades = function (upgrades) { } return filteredUpgrades; }; + +/** + * Generates a random uid. + * + * @api private + */ + +function rnd () { + return String(Math.random()).substr(5) + String(Math.random()).substr(5); +} + }); require.register("LearnBoost-engine.io-client/lib/transport.js", function(exports, require, module){ @@ -2224,7 +2233,6 @@ JSONPPolling.prototype.doClose = function () { */ JSONPPolling.prototype.doPoll = function () { - var self = this; var script = document.createElement('script'); if (this.script) { @@ -2234,15 +2242,11 @@ JSONPPolling.prototype.doPoll = function () { script.async = true; script.src = this.uri(); - script.onerror = function(e){ - self.onError('jsonp poll error',e); - } var insertAt = document.getElementsByTagName('script')[0]; insertAt.parentNode.insertBefore(script, insertAt); this.script = script; - if (util.ua.gecko) { setTimeout(function () { var iframe = document.createElement('iframe'); @@ -2951,7 +2955,7 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ __extends(Client, _super); function Client(plugin, options) { - var eiopts, k, v, _base, _base1, _base2, _ref, _ref1, _ref2; + var eiopts, k, v, _base, _base1, _ref, _ref1; if (options == null) { options = {}; } @@ -2979,9 +2983,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if ((_ref1 = (_base1 = this.options).reconnectLimit) == null) { _base1.reconnectLimit = Infinity; } - if ((_ref2 = (_base2 = this.options).reconnectTimeout) == null) { - _base2.reconnectTimeout = Infinity; - } this.isServer = false; this.isClient = true; this.isBrowser = typeof window !== "undefined" && window !== null; @@ -3014,13 +3015,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ return this; }; - Client.prototype.destroy = function() { - this.options.reconnect = false; - this.ssocket.disconnect(); - this.emit("destroyed"); - return this; - }; - Client.prototype.handleConnection = function() { this.emit('connected'); return this.connect(this.ssocket); @@ -3069,7 +3063,7 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ }; Client.prototype.reconnect = function(cb) { - var attempts, connect, done, err, maxAttempts, start, timeout, + var attempts, connect, done, err, maxAttempts, _this = this; if (this.ssocket.reconnecting) { return cb("Already reconnecting"); @@ -3078,13 +3072,10 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if (this.ssocket.readyState === 'open') { this.ssocket.disconnect(); } - start = Date.now(); maxAttempts = this.options.reconnectLimit; - timeout = this.options.reconnectTimeout; attempts = 0; done = function() { _this.ssocket.reconnecting = false; - _this.emit("reconnected"); return cb(); }; err = function(e) { @@ -3099,9 +3090,6 @@ require.register("wearefractal-protosock/dist/Client.js", function(exports, requ if (attempts >= maxAttempts) { return err("Exceeded max attempts"); } - if ((Date.now() - start) > timeout) { - return err("Timeout on reconnect"); - } attempts++; _this.ssocket.open(); return setTimeout(connect, getDelay(attempts)); @@ -3271,416 +3259,551 @@ Emitter.prototype.hasListeners = function(event){ }); require.register("holla/dist/holla.js", function(exports, require, module){ // Generated by CoffeeScript 1.4.0 -var Call, ProtoSock, RTC, client, holla; - -Call = require('./Call'); - -ProtoSock = require('protosock'); - -RTC = require('./RTC'); - -client = { - options: { - namespace: 'holla', - resource: 'default', - debug: false - }, - register: function(name, cb) { - var _this = this; - this.ssocket.write({ - type: "register", - args: { - name: name - } - }); - return this.once("register", function(worked) { - if (worked) { - _this.user = name; - _this.emit("authorized"); - } - _this.authorized = worked; - return typeof cb === "function" ? cb(worked) : void 0; - }); - }, - call: function(user) { - return new Call(this, user, true); - }, - chat: function(user, msg) { - this.ssocket.write({ - type: "chat", - to: user, - args: { - message: msg - } - }); - return this; - }, - ready: function(fn) { - if (this.authorized) { - fn(); - } else { - this.once('authorized', fn); - } - return this; - }, - validate: function(socket, msg, done) { - if (this.options.debug) { - console.log(msg); - } - if (typeof msg !== 'object') { - return done(false); - } - if (typeof msg.type !== 'string') { - return done(false); - } - if (msg.type === "register") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.args.result !== 'boolean') { - return done(false); - } - } else if (msg.type === "offer") { - if (typeof msg.from !== 'string') { - return done(false); - } - } else if (msg.type === "answer") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (typeof msg.args.accepted !== 'boolean') { - return done(false); - } - } else if (msg.type === "sdp") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (!msg.args.sdp) { - return done(false); - } - if (!msg.args.type) { - return done(false); - } - } else if (msg.type === "candidate") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); - } - if (typeof msg.args.candidate !== 'object') { - return done(false); - } - } else if (msg.type === "chat") { - if (typeof msg.args !== 'object') { - return done(false); - } - if (typeof msg.from !== 'string') { - return done(false); +(function() { + var Call, ProtoSock, RTC, client, holla; + + Call = require('./Call'); + + RTC = require('./RTC'); + + ProtoSock = require('protosock'); + + client = { + options: { + namespace: 'holla', + resource: 'default', + debug: false + }, + register: function(name, cb) { + var _this = this; + this.ssocket.write({ + type: "register", + args: { + name: name + } + }); + return this.once("register", function(worked) { + if (worked) { + _this.user = name; + _this.emit("authorized"); + } + _this.authorized = worked; + return typeof cb === "function" ? cb(worked) : void 0; + }); + }, + call: function(user) { + return new Call(this, user, true); + }, + chat: function(user, msg) { + this.ssocket.write({ + type: "chat", + to: user, + args: { + message: msg + } + }); + return this; + }, + ready: function(fn) { + if (this.authorized) { + fn(); + } else { + this.once('authorized', fn); } - if (typeof msg.args.message !== 'string') { - return done(false); + return this; + }, + validate: function(socket, msg, done) { + if (this.options.debug) { + console.log(msg); } - } else if (msg.type === "hangup") { - if (typeof msg.from !== 'string') { + if (typeof msg !== 'object') { return done(false); } - } else if (msg.type === "presence") { - if (typeof msg.args !== 'object') { + if (typeof msg.type !== 'string') { return done(false); } - if (typeof msg.args.name !== 'string') { + if (msg.type === "register") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.result !== 'boolean') { + return done(false); + } + } else if (msg.type === "offer") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "answer") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.accepted !== 'boolean') { + return done(false); + } + } else if (msg.type === "sdp") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (!msg.args.sdp) { + return done(false); + } + if (!msg.args.type) { + return done(false); + } + } else if (msg.type === "candidate") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.candidate !== 'object') { + return done(false); + } + } else if (msg.type === "chat") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.from !== 'string') { + return done(false); + } + if (typeof msg.args.message !== 'string') { + return done(false); + } + } else if (msg.type === "hangup") { + if (typeof msg.from !== 'string') { + return done(false); + } + } else if (msg.type === "presence") { + if (typeof msg.args !== 'object') { + return done(false); + } + if (typeof msg.args.name !== 'string') { + return done(false); + } + if (typeof msg.args.online !== 'boolean') { + return done(false); + } + } else { return done(false); } - if (typeof msg.args.online !== 'boolean') { - return done(false); + return done(true); + }, + error: function(socket, err) { + return this.emit('error', err, socket); + }, + message: function(socket, msg) { + var c; + switch (msg.type) { + case "register": + return this.emit("register", msg.args.result); + case "offer": + c = new Call(this, msg.from, false); + return this.emit("call", c); + case "presence": + this.emit("presence", msg.args); + return this.emit("presence." + msg.args.name, msg.args.online); + case "chat": + this.emit("chat", { + from: msg.from, + message: msg.args.message + }); + return this.emit("chat." + msg.from, msg.args.message); + case "hangup": + this.emit("hangup", { + from: msg.from + }); + return this.emit("hangup." + msg.from); + case "answer": + this.emit("answer", { + from: msg.from, + accepted: msg.args.accepted + }); + return this.emit("answer." + msg.from, msg.args.accepted); + case "candidate": + this.emit("candidate", { + from: msg.from, + candidate: msg.args.candidate + }); + return this.emit("candidate." + msg.from, msg.args.candidate); + case "sdp": + this.emit("sdp", { + from: msg.from, + sdp: msg.args.sdp, + type: msg.args.type + }); + return this.emit("sdp." + msg.from, msg.args); } - } else { - return done(false); - } - return done(true); - }, - error: function(socket, err) { - return this.emit('error', err, socket); - }, - message: function(socket, msg) { - var c; - switch (msg.type) { - case "register": - return this.emit("register", msg.args.result); - case "offer": - c = new Call(this, msg.from, false); - return this.emit("call", c); - case "presence": - this.emit("presence", msg.args); - return this.emit("presence." + msg.args.name, msg.args.online); - case "chat": - this.emit("chat", { - from: msg.from, - message: msg.args.message - }); - return this.emit("chat." + msg.from, msg.args.message); - case "hangup": - this.emit("hangup", { - from: msg.from - }); - return this.emit("hangup." + msg.from); - case "answer": - this.emit("answer", { - from: msg.from, - accepted: msg.args.accepted - }); - return this.emit("answer." + msg.from, msg.args.accepted); - case "candidate": - this.emit("candidate", { - from: msg.from, - candidate: msg.args.candidate - }); - return this.emit("candidate." + msg.from, msg.args.candidate); - case "sdp": - this.emit("sdp", { - from: msg.from, - sdp: msg.args.sdp, - type: msg.args.type - }); - return this.emit("sdp." + msg.from, msg.args); } - } -}; + }; -holla = { - createClient: ProtoSock.createClientWrapper(client), - Call: Call, - supported: supported, - streamToBlob: function(s) { - return RTC.URL.createObjectURL(s); - }, - pipe: function(stream, el) { - var uri; - uri = holla.streamToBlob(stream); - return RTC.attachStream(uri, el); - }, - createStream: function(opt, cb) { - var err, succ; - if (RTC.getUserMedia == null) { - return cb("Missing getUserMedia"); + holla = { + createClient: ProtoSock.createClientWrapper(client), + Call: Call, + supported: RTC.supported, + config: RTC.PeerConnConfig, + streamToBlob: function(s) { + return RTC.URL.createObjectURL(s); + }, + pipe: function(stream, el) { + var uri; + uri = holla.streamToBlob(stream); + return RTC.attachStream(uri, el); + }, + createStream: function(opt, cb) { + var err, succ; + if (RTC.getUserMedia == null) { + return cb("Missing getUserMedia"); + } + err = cb; + succ = function(s) { + return cb(null, s); + }; + RTC.getUserMedia(opt, succ, err); + return holla; + }, + createFullStream: function(cb) { + return holla.createStream({ + video: true, + audio: true + }, cb); + }, + createVideoStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); + }, + createAudioStream: function(cb) { + return holla.createStream({ + video: true, + audio: false + }, cb); } - err = cb; - succ = function(s) { - return cb(null, s); - }; - RTC.getUserMedia(opt, succ, err); - return holla; - }, - createFullStream: function(cb) { - return holla.createStream({ - video: true, - audio: true - }, cb); - }, - createVideoStream: function(cb) { - return holla.createStream({ - video: true, - audio: false - }, cb); - }, - createAudioStream: function(cb) { - return holla.createStream({ - video: true, - audio: false - }, cb); - } -}; - -module.exports = holla; + }; + + module.exports = holla; + +}).call(this); }); require.register("holla/dist/Call.js", function(exports, require, module){ // Generated by CoffeeScript 1.4.0 -var Call, EventEmitter, RTC, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +(function() { + var Call, EventEmitter, RTC, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; -RTC = require('./RTC'); + RTC = require('./RTC'); -EventEmitter = require('emitter'); + EventEmitter = require('emitter'); -Call = (function(_super) { + Call = (function(_super) { - __extends(Call, _super); + __extends(Call, _super); - function Call(parent, user, isCaller) { - var _this = this; - this.parent = parent; - this.user = user; - this.isCaller = isCaller; - this.startTime = new Date; - this.socket = this.parent.ssocket; - this.pc = this.createConnection(); - if (this.isCaller) { - this.socket.write({ - type: "offer", - to: this.user + function Call(parent, user, isCaller) { + var _this = this; + this.parent = parent; + this.user = user; + this.isCaller = isCaller; + this.startTime = new Date; + this.socket = this.parent.ssocket; + this.pc = this.createConnection(); + if (this.isCaller) { + this.socket.write({ + type: "offer", + to: this.user + }); + } + this.emit("calling"); + this.parent.on("answer." + this.user, function(accepted) { + if (!accepted) { + return _this.emit("rejected"); + } + _this.emit("answered"); + return _this.initSDP(); + }); + this.parent.on("candidate." + this.user, function(candidate) { + return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); + }); + this.parent.on("sdp." + this.user, function(stuff) { + _this.pc.setRemoteDescription(new RTC.SessionDescription(stuff)); + return _this.emit("sdp"); + }); + this.parent.on("hangup." + this.user, function() { + return _this.emit("hangup"); + }); + this.parent.on("chat." + this.user, function(msg) { + return _this.emit("chat", msg); }); } - this.emit("calling"); - this.parent.on("answer." + this.user, function(accepted) { - if (!accepted) { - return _this.emit("rejected"); + + Call.prototype.createConnection = function() { + var pc, + _this = this; + pc = new RTC.PeerConnection(RTC.PeerConnConfig); + pc.onconnecting = function() { + _this.emit('connecting'); + }; + pc.onopen = function() { + _this.emit('connected'); + }; + pc.onicecandidate = function(evt) { + if (evt.candidate) { + _this.socket.write({ + type: "candidate", + to: _this.user, + args: { + candidate: evt.candidate + } + }); + } + }; + pc.onaddstream = function(evt) { + _this.remoteStream = evt.stream; + _this._ready = true; + _this.emit("ready", _this.remoteStream); + }; + pc.onremovestream = function(evt) { + console.log("removestream", evt); + }; + return pc; + }; + + Call.prototype.addStream = function(s) { + this.pc.addStream(s); + return this; + }; + + Call.prototype.ready = function(fn) { + if (this._ready) { + fn(this.remoteStream); + } else { + this.once('ready', fn); } - _this.emit("answered"); - return _this.initSDP(); - }); - this.parent.on("candidate." + this.user, function(candidate) { - return _this.pc.addIceCandidate(new RTC.IceCandidate(candidate)); - }); - this.parent.on("sdp." + this.user, function(stuff) { - _this.pc.setRemoteDescription(new RTC.SessionDescription(stuff)); - return _this.emit("sdp"); - }); - this.parent.on("hangup." + this.user, function() { - return _this.emit("hangup"); - }); - this.parent.on("chat." + this.user, function(msg) { - return _this.emit("chat", msg); - }); - } + return this; + }; + + Call.prototype.duration = function() { + var e, s; + if (this.endTime != null) { + s = this.endTime.getTime(); + } + if (s == null) { + s = Date.now(); + } + e = this.startTime.getTime(); + return (s - e) / 1000; + }; + + Call.prototype.chat = function(msg) { + this.parent.chat(this.user, msg); + return this; + }; + + Call.prototype.answer = function() { + this.startTime = new Date; + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: true + } + }); + this.initSDP(); + return this; + }; - Call.prototype.createConnection = function() { - var pc, - _this = this; - pc = new RTC.PeerConnection(PeerConnConfig); - pc.onconnecting = function() { - _this.emit('connecting'); + Call.prototype.decline = function() { + this.socket.write({ + type: "answer", + to: this.user, + args: { + accepted: false + } + }); + return this; }; - pc.onopen = function() { - _this.emit('connected'); + + Call.prototype.end = function() { + this.endTime = new Date; + this.pc.close(); + this.socket.write({ + type: "hangup", + to: this.user + }); + this.emit("hangup"); + return this; }; - pc.onicecandidate = function(evt) { - if (evt.candidate) { - _this.socket.write({ - type: "candidate", + + Call.prototype.initSDP = function() { + var done, err, + _this = this; + done = function(desc) { + desc.sdp = RTC.processSDP(desc.sdp); + _this.pc.setLocalDescription(desc); + return _this.socket.write({ + type: "sdp", to: _this.user, - args: { - candidate: evt.candidate - } + args: desc }); + }; + err = function(e) { + return console.log(e); + }; + if (this.isCaller) { + return this.pc.createOffer(done, err); } + if (this.pc.remoteDescription) { + return this.pc.createAnswer(done, err); + } + return this.once("sdp", function() { + return _this.pc.createAnswer(done, err); + }); }; - pc.onaddstream = function(evt) { - _this.remoteStream = evt.stream; - _this._ready = true; - _this.emit("ready", _this.remoteStream); - }; - pc.onremovestream = function(evt) { - console.log("removestream", evt); - }; - return pc; - }; - Call.prototype.addStream = function(s) { - this.pc.addStream(s); - return this; - }; + return Call; - Call.prototype.ready = function(fn) { - if (this._ready) { - fn(this.remoteStream); - } else { - this.once('ready', fn); - } - return this; - }; + })(EventEmitter); - Call.prototype.duration = function() { - var e, s; - if (this.endTime != null) { - s = this.endTime.getTime(); - } - if (s == null) { - s = Date.now(); - } - e = this.startTime.getTime(); - return (s - e) / 1000; - }; + module.exports = Call; - Call.prototype.chat = function(msg) { - this.parent.chat(this.user, msg); - return this; - }; +}).call(this); - Call.prototype.answer = function() { - this.startTime = new Date; - this.socket.write({ - type: "answer", - to: this.user, - args: { - accepted: true - } - }); - this.initSDP(); - return this; - }; +}); +require.register("holla/dist/RTC.js", function(exports, require, module){ +// Generated by CoffeeScript 1.4.0 +(function() { + var IceCandidate, MediaStream, PeerConnection, SessionDescription, URL, attachStream, browser, getUserMedia, processSDP, shim, supported, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + PeerConnection = window.PeerConnection || window.webkitPeerConnection00 || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; + + IceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + + SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; + + MediaStream = window.webkitMediaStream || window.MediaStream; + + getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia).bind(navigator); + + URL = window.URL || window.webkitURL || window.msURL || window.oURL; + + browser = (navigator.mozGetUserMedia ? 'firefox' : 'chrome'); - Call.prototype.decline = function() { - this.socket.write({ - type: "answer", - to: this.user, - args: { - accepted: false + supported = (PeerConnection != null) && (getUserMedia != null); + + processSDP = function(sdp) { + var addCrypto, line, out, _i, _len, _ref; + if (browser !== 'mozilla') { + return sdp; + } + addCrypto = "a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + out = []; + _ref = sdp.split('\r\n'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + line = _ref[_i]; + out.push(line); + if ((__indexOf.call(search, 'm=') >= 0) !== -1) { + out.push(addCrypto); } - }); - return this; + } + return out.join('\r\n'); }; - Call.prototype.end = function() { - this.endTime = new Date; - this.pc.close(); - this.socket.write({ - type: "hangup", - to: this.user - }); - this.emit("hangup"); - return this; + attachStream = function(uri, el) { + var e, srcAttr, _i, _len; + srcAttr = (browser === 'mozilla' ? 'mozSrcObject' : 'src'); + if (typeof el === "string") { + return attachStream(uri, document.getElementById(el)); + } else if (el.jquery) { + el.attr(srcAttr, uri); + for (_i = 0, _len = el.length; _i < _len; _i++) { + e = el[_i]; + e.play(); + } + } else { + el[srcAttr] = uri; + el.play(); + } + return el; }; - Call.prototype.initSDP = function() { - var done, err, - _this = this; - done = function(desc) { - desc.sdp = RTC.processSDP(desc.sdp); - _this.pc.setLocalDescription(desc); - return _this.socket.write({ - type: "sdp", - to: _this.user, - args: desc - }); - }; - err = function(e) { - return console.log(e); - }; - if (this.isCaller) { - return this.pc.createOffer(done, err); + shim = function() { + var PeerConnConfig, out; + if (!supported) { + return; } - if (this.pc.remoteDescription) { - return this.pc.createAnswer(done, err); + if (browser === 'mozilla') { + PeerConnConfig = { + iceServers: [ + { + url: "stun:23.21.150.121" + } + ], + optional: [] + }; + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } else { + PeerConnConfig = { + iceServers: [ + { + url: "stun:stun.l.google.com:19302" + } + ], + optional: [ + { + DtlsSrtpKeyAgreement: true + } + ] + }; + if (MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return this.videoTracks; + }; + MediaStream.prototype.getAudioTracks = function() { + return this.audioTracks; + }; + } + if (PeerConnection.prototype.getLocalStreams) { + PeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + PeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + } } - return this.once("sdp", function() { - return _this.pc.createAnswer(done, err); - }); + out = { + PeerConnection: PeerConnection, + IceCandidate: IceCandidate, + SessionDescription: SessionDescription, + MediaStream: MediaStream, + getUserMedia: getUserMedia, + URL: URL, + attachStream: attachStream, + processSDP: processSDP, + PeerConnConfig: PeerConnConfig, + supported: supported + }; + return out; }; - return Call; + module.exports = shim(); -})(EventEmitter); - -module.exports = Call; +}).call(this); }); require.alias("wearefractal-protosock/dist/main.js", "holla/deps/protosock/dist/main.js"); @@ -3724,7 +3847,7 @@ require.alias("holla/dist/holla.js", "holla/index.js"); if (typeof exports == "object") { module.exports = require("holla"); } else if (typeof define == "function" && define.amd) { - define(function(){ return require("holla"); }); + define(require("holla")); } else { window["holla"] = require("holla"); }})(); \ No newline at end of file diff --git a/holla.min.js b/holla.min.js index 9d19f63..450a06e 100644 --- a/holla.min.js +++ b/holla.min.js @@ -1,2 +1,2 @@ -(function(){function t(e,o,n){var r=t.resolve(e);if(null==r){n=n||e,o=o||"root";var i=Error('Failed to require "'+n+'" from "'+o+'"');throw i.path=n,i.parent=o,i.require=!0,i}var s=t.modules[r];return s.exports||(s.exports={},s.client=s.component=!0,s.call(this,s.exports,t.relative(r),s)),s.exports}var e=Object.prototype.hasOwnProperty;t.modules={},t.aliases={},t.resolve=function(o){"/"===o.charAt(0)&&(o=o.slice(1));for(var n=o+"/index.js",r=[o,o+".js",o+".json",o+"/index.js",o+"/index.json"],i=0;r.length>i;i++){var o=r[i];if(e.call(t.modules,o))return o}return e.call(t.aliases,n)?t.aliases[n]:void 0},t.normalize=function(t,e){var o=[];if("."!=e.charAt(0))return e;t=t.split("/"),e=e.split("/");for(var n=0;e.length>n;++n)".."==e[n]?t.pop():"."!=e[n]&&""!=e[n]&&o.push(e[n]);return t.concat(o).join("/")},t.register=function(e,o){t.modules[e]=o},t.alias=function(o,n){if(!e.call(t.modules,o))throw Error('Failed to alias "'+o+'", it does not exist');t.aliases[n]=o},t.relative=function(o){function n(t,e){for(var o=t.length;o--;)if(t[o]===e)return o;return-1}function r(e){var n=r.resolve(e);return t(n,o,e)}var i=t.normalize(o,"..");return r.resolve=function(e){var r=e.charAt(0);if("/"==r)return e.slice(1);if("."==r)return t.normalize(i,e);var s=o.split("/"),a=n(s,"deps")+1;return a||(a=0),e=s.slice(0,a+1).join("/")+"/deps/"+e},r.exists=function(o){return e.call(t.modules,r.resolve(o))},r},t.register("LearnBoost-engine.io-protocol/lib/index.js",function(t,e){var o=e("./keys"),n=t.packets={open:0,close:1,ping:2,pong:3,message:4,upgrade:5,noop:6},r=o(n),i={type:"error",data:"parser error"};t.encodePacket=function(t){var e=n[t.type];return void 0!==t.data&&(e+=t.data+""),""+e},t.decodePacket=function(t){var e=t.charAt(0);return Number(e)==e&&r[e]?t.length>1?{type:r[e],data:t.substring(1)}:{type:r[e]}:i},t.encodePayload=function(e){if(!e.length)return"0:";for(var o,n="",r=0,i=e.length;i>r;r++)o=t.encodePacket(e[r]),n+=o.length+":"+o;return n},t.decodePayload=function(e){if(""==e)return[i];for(var o,n,r,s=[],a="",c=0,p=e.length;p>c;c++){var l=e.charAt(c);if(":"!=l)a+=l;else{if(""==a||a!=(o=Number(a)))return[i];if(n=e.substr(c+1,o),a!=n.length)return[i];if(n.length){if(r=t.decodePacket(n),i.type==r.type&&i.data==r.data)return[i];s.push(r)}c+=o,a=""}}return""!=a?[i]:s}}),t.register("LearnBoost-engine.io-protocol/lib/keys.js",function(t,e,o){o.exports=Object.keys||function(t){var e=[],o=Object.prototype.hasOwnProperty;for(var n in t)o.call(t,n)&&e.push(n);return e}}),t.register("visionmedia-debug/index.js",function(t,e,o){o.exports="undefined"==typeof window?e("./lib/debug"):e("./debug")}),t.register("visionmedia-debug/debug.js",function(t,e,o){function n(t){return n.enabled(t)?function(e){var o=new Date,r=o-(n[t]||o);n[t]=o,e=t+" "+e+" +"+n.humanize(r),window.console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}:function(){}}o.exports=n,n.names=[],n.skips=[],n.enable=function(t){try{localStorage.debug=t}catch(e){}for(var o=(t||"").split(/[\s,]+/),r=o.length,i=0;r>i;i++)t=o[i].replace("*",".*?"),"-"===t[0]?n.skips.push(RegExp("^"+t.substr(1)+"$")):n.names.push(RegExp("^"+t+"$"))},n.disable=function(){n.enable("")},n.humanize=function(t){var e=1e3,o=6e4,n=60*o;return t>=n?(t/n).toFixed(1)+"h":t>=o?(t/o).toFixed(1)+"m":t>=e?(0|t/e)+"s":t+"ms"},n.enabled=function(t){for(var e=0,o=n.skips.length;o>e;e++)if(n.skips[e].test(t))return!1;for(var e=0,o=n.names.length;o>e;e++)if(n.names[e].test(t))return!0;return!1},window.localStorage&&n.enable(localStorage.debug)}),t.register("LearnBoost-engine.io-client/lib/index.js",function(t,e,o){o.exports=e("./socket"),o.exports.parser=e("engine.io-parser")}),t.register("LearnBoost-engine.io-client/lib/socket.js",function(t,e,o){function n(t,e){if(!(this instanceof n))return new n(t,e);if(e=e||{},"object"==typeof t&&(e=t,t=null),t&&(t=i.parseUri(t),e.host=t.host,e.secure="https"==t.protocol||"wss"==t.protocol,e.port=t.port),this.secure=null!=e.secure?e.secure:p.location&&"https:"==location.protocol,e.host){var o=e.host.split(":");e.hostname=o.shift(),o.length&&(e.port=o.pop())}this.hostname=e.hostname||(p.location?location.hostname:"localhost"),this.port=e.port||(p.location&&location.port?location.port:this.secure?443:80),this.query=e.query||{},this.upgrade=!1!==e.upgrade,this.path=(e.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!e.forceJSONP,this.timestampParam=e.timestampParam||"t",this.timestampRequests=!!e.timestampRequests,this.flashPath=e.flashPath||"",this.transports=e.transports||["polling","websocket","flashsocket"],this.readyState="",this.writeBuffer=[],this.policyPort=e.policyPort||843,this.open(),n.sockets.push(this),n.sockets.evs.emit("add",this)}function r(t){var e={};for(var o in t)t.hasOwnProperty(o)&&(e[o]=t[o]);return e}var i=e("./util"),s=e("./transports"),a=e("./emitter"),c=e("debug")("engine-client:socket");o.exports=n;var p=i.global();a(n.prototype),n.protocol=1,n.sockets=[],n.sockets.evs=new a,n.Socket=n,n.Transport=e("./transport"),n.Emitter=e("./emitter"),n.transports=e("./transports"),n.util=e("./util"),n.parser=e("engine.io-parser"),n.prototype.createTransport=function(t){c('creating transport "%s"',t);var e=r(this.query);e.transport=t,this.id&&(e.sid=this.id);var o=new s[t]({hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:e,forceJSONP:this.forceJSONP,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,flashPath:this.flashPath,policyPort:this.policyPort});return o},n.prototype.open=function(){this.readyState="opening";var t=this.createTransport(this.transports[0]);t.open(),this.setTransport(t)},n.prototype.setTransport=function(t){var e=this;this.transport&&(c("clearing existing transport"),this.transport.removeAllListeners()),this.transport=t,t.on("drain",function(){e.flush()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},n.prototype.probe=function(t){function e(e){if(!n){n=!0;var i=Error("probe error: "+e);i.transport=o.name,o.close(),o=null,c('probe transport "%s" failed because of error: %s',t,e),r.emit("error",i)}}c('probing transport "%s"',t);var o=this.createTransport(t,{probe:1}),n=!1,r=this;o.once("open",function(){n||(c('probe transport "%s" opened',t),o.send([{type:"ping",data:"probe"}]),o.once("packet",function(i){if(!n)if("pong"==i.type&&"probe"==i.data)c('probe transport "%s" pong',t),r.upgrading=!0,r.emit("upgrading",o),c('pausing current transport "%s"',r.transport.name),r.transport.pause(function(){n||"closed"!=r.readyState&&"closing"!=r.readyState&&(c("changing transport and sending upgrade packet"),o.removeListener("error",e),r.emit("upgrade",o),r.setTransport(o),o.send([{type:"upgrade"}]),o=null,r.upgrading=!1,r.flush())});else{c('probe transport "%s" failed',t);var s=Error("probe error");s.transport=o.name,r.emit("error",s)}}))}),o.once("error",e),o.open(),this.once("close",function(){o&&(c("socket closed prematurely - aborting probe"),n=!0,o.close(),o=null)}),this.once("upgrading",function(t){o&&t.name!=o.name&&(c('"%s" works - aborting "%s"',t.name,o.name),o.close(),o=null)})},n.prototype.onOpen=function(){if(c("socket open"),this.readyState="open",this.emit("open"),this.onopen&&this.onopen.call(this),this.flush(),"open"==this.readyState&&this.upgrade&&this.transport.pause){c("starting upgrade probes");for(var t=0,e=this.upgrades.length;e>t;t++)this.probe(this.upgrades[t])}},n.prototype.onPacket=function(t){if("opening"==this.readyState||"open"==this.readyState)switch(c('socket receive: type "%s", data "%s"',t.type,t.data),this.emit("packet",t),this.emit("heartbeat"),t.type){case"open":this.onHandshake(i.parseJSON(t.data));break;case"pong":this.ping();break;case"error":var e=Error("server error");e.code=t.data,this.emit("error",e);break;case"message":this.emit("data",t.data),this.emit("message",t.data);var o={data:t.data};o.toString=function(){return t.data},this.onmessage&&this.onmessage.call(this,o)}else c('packet received with socket readyState "%s"',this.readyState)},n.prototype.onHandshake=function(t){this.emit("handshake",t),this.id=t.sid,this.transport.query.sid=t.sid,this.upgrades=this.filterUpgrades(t.upgrades),this.pingInterval=t.pingInterval,this.pingTimeout=t.pingTimeout,this.onOpen(),this.ping(),this.removeListener("heartbeat",this.onHeartbeat),this.on("heartbeat",this.onHeartbeat)},n.prototype.onHeartbeat=function(t){clearTimeout(this.pingTimeoutTimer);var e=this;e.pingTimeoutTimer=setTimeout(function(){"closed"!=e.readyState&&e.onClose("ping timeout")},t||e.pingInterval+e.pingTimeout)},n.prototype.ping=function(){var t=this;clearTimeout(t.pingIntervalTimer),t.pingIntervalTimer=setTimeout(function(){c("writing ping packet - expecting pong within %sms",t.pingTimeout),t.sendPacket("ping"),t.onHeartbeat(t.pingTimeout)},t.pingInterval)},n.prototype.flush=function(){"closed"!=this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length&&(c("flushing %d packets in socket",this.writeBuffer.length),this.transport.send(this.writeBuffer),this.writeBuffer=[])},n.prototype.write=n.prototype.send=function(t){return this.sendPacket("message",t),this},n.prototype.sendPacket=function(t,e){var o={type:t,data:e};this.emit("packetCreate",o),this.writeBuffer.push(o),this.flush()},n.prototype.close=function(){return("opening"==this.readyState||"open"==this.readyState)&&(this.onClose("forced close"),c("socket closing - telling transport to close"),this.transport.close(),this.transport.removeAllListeners()),this},n.prototype.onError=function(t){c("socket error %j",t),this.emit("error",t),this.onClose("transport error",t)},n.prototype.onClose=function(t,e){("opening"==this.readyState||"open"==this.readyState)&&(c('socket close with reason: "%s"',t),clearTimeout(this.pingIntervalTimer),clearTimeout(this.pingTimeoutTimer),this.readyState="closed",this.emit("close",t,e),this.onclose&&this.onclose.call(this),this.id=null)},n.prototype.filterUpgrades=function(t){for(var e=[],o=0,n=t.length;n>o;o++)~this.transports.indexOf(t[o])&&e.push(t[o]);return e}}),t.register("LearnBoost-engine.io-client/lib/transport.js",function(t,e,o){function n(t){this.path=t.path,this.hostname=t.hostname,this.port=t.port,this.secure=t.secure,this.query=t.query,this.timestampParam=t.timestampParam,this.timestampRequests=t.timestampRequests,this.readyState=""}var r=(e("./util"),e("engine.io-parser")),i=e("./emitter");o.exports=n,i(n.prototype),n.prototype.onError=function(t,e){var o=Error(t);return o.type="TransportError",o.description=e,this.emit("error",o),this},n.prototype.open=function(){return("closed"==this.readyState||""==this.readyState)&&(this.readyState="opening",this.doOpen()),this},n.prototype.close=function(){return("opening"==this.readyState||"open"==this.readyState)&&(this.doClose(),this.onClose()),this},n.prototype.send=function(t){if("open"!=this.readyState)throw Error("Transport not open");this.write(t)},n.prototype.onOpen=function(){this.readyState="open",this.writable=!0,this.emit("open")},n.prototype.onData=function(t){this.onPacket(r.decodePacket(t))},n.prototype.onPacket=function(t){this.emit("packet",t)},n.prototype.onClose=function(){this.readyState="closed",this.emit("close")}}),t.register("LearnBoost-engine.io-client/lib/emitter.js",function(t,e,o){var n;try{n=e("emitter")}catch(r){n=e("emitter-component")}o.exports=n,n.prototype.addEventListener=n.prototype.on,n.prototype.removeEventListener=n.prototype.off,n.prototype.removeListener=n.prototype.off,n.prototype.removeAllListeners=function(){this._callbacks={}}}),t.register("LearnBoost-engine.io-client/lib/util.js",function(t,e){var o=!1;t.global=function(){return"undefined"!=typeof window?window:global},t.inherits=function(t,e){function o(){}o.prototype=e.prototype,t.prototype=new o},t.keys=Object.keys||function(t){var e=[],o=Object.prototype.hasOwnProperty;for(var n in t)o.call(t,n)&&e.push(n);return e},t.on=function(t,e,o,n){t.attachEvent?t.attachEvent("on"+e,o):t.addEventListener&&t.addEventListener(e,o,n)},t.load=function(e){var n=t.global();return n.document&&"complete"===document.readyState||o?e():(t.on(n,"load",e,!1),void 0)},"undefined"!=typeof window&&t.load(function(){o=!0}),t.defer=function(e){return t.ua.webkit&&"undefined"==typeof importScripts?(t.load(function(){setTimeout(e,100)}),void 0):e()};var n=/^[\],:{}\s]*$/,r=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,i=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,s=/(?:^|:|,)(?:\s*\[)+/g,a=/^\s+/,c=/\s+$/;t.parseJSON=function(e){var o=t.global();return"string"==typeof e&&e?(e=e.replace(a,"").replace(c,""),o.JSON&&JSON.parse?JSON.parse(e):n.test(e.replace(r,"@").replace(i,"]").replace(s,""))?Function("return "+e)():void 0):null},t.ua={},t.ua.hasCORS="undefined"!=typeof XMLHttpRequest&&function(){try{var t=new XMLHttpRequest}catch(e){return!1}return void 0!=t.withCredentials}(),t.ua.webkit="undefined"!=typeof navigator&&/webkit/i.test(navigator.userAgent),t.ua.gecko="undefined"!=typeof navigator&&/gecko/i.test(navigator.userAgent),t.ua.android="undefined"!=typeof navigator&&/android/i.test(navigator.userAgent),t.ua.ios="undefined"!=typeof navigator&&/^(iPad|iPhone|iPod)$/.test(navigator.platform),t.ua.ios6=t.ua.ios&&/OS 6_/.test(navigator.userAgent),t.request=function(o){if("undefined"==typeof window){var n=e("xmlhttprequest").XMLHttpRequest;return new n}if(o&&"undefined"!=typeof XDomainRequest&&!t.ua.hasCORS)return new XDomainRequest;try{if("undefined"!=typeof XMLHttpRequest&&(!o||t.ua.hasCORS))return new XMLHttpRequest}catch(r){}if(!o)try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(r){}};var p=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,l=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.parseUri=function(t){for(var e=p.exec(t||""),o={},n=14;n--;)o[l[n]]=e[n]||"";return o},t.qs=function(t){var e="";for(var o in t)t.hasOwnProperty(o)&&(e.length&&(e+="&"),e+=o+"="+encodeURIComponent(t[o]));return e}}),t.register("LearnBoost-engine.io-client/lib/transports/index.js",function(t,e){function o(t){var e,o=!1,i=!1;if(c.location){var s="https:"==location.protocol,p=location.port;Number(p)!=p&&(p=s?443:80),o=t.host!=location.hostname||p!=t.port,i=t.secure!=s}return e=a.request(o),i&&c.XDomainRequest&&e instanceof c.XDomainRequest?new r(t):e&&!t.forceJSONP?new n(t):new r(t)}var n=e("./polling-xhr"),r=e("./polling-jsonp"),i=e("./websocket"),s=e("./flashsocket"),a=e("../util");t.polling=o,t.websocket=i,t.flashsocket=s;var c=a.global()}),t.register("LearnBoost-engine.io-client/lib/transports/polling.js",function(t,e,o){function n(t){r.call(this,t)}var r=e("../transport"),i=e("../util"),s=e("engine.io-parser"),a=e("debug")("engine.io-client:polling");o.exports=n;var c=i.global();i.inherits(n,r),n.prototype.name="polling",n.prototype.doOpen=function(){this.poll()},n.prototype.pause=function(t){function e(){a("paused"),o.readyState="paused",t()}var o=this;if(this.readyState="pausing",this.polling||!this.writable){var n=0;this.polling&&(a("we are currently polling - waiting to pause"),n++,this.once("pollComplete",function(){a("pre-pause polling complete"),--n||e()})),this.writable||(a("we are currently writing - waiting to pause"),n++,this.once("drain",function(){a("pre-pause writing complete"),--n||e()}))}else e()},n.prototype.poll=function(){a("polling"),this.polling=!0,this.doPoll(),this.emit("poll")},n.prototype.onData=function(t){a("polling got data %s",t);for(var e=s.decodePayload(t),o=0,n=e.length;n>o;o++){if("opening"==this.readyState&&this.onOpen(),"close"==e[o].type)return this.onClose(),void 0;this.onPacket(e[o])}this.polling=!1,this.emit("pollComplete"),"open"==this.readyState?this.poll():a('ignoring poll - transport state "%s"',this.readyState)},n.prototype.doClose=function(){a("sending close packet"),this.send([{type:"close"}])},n.prototype.write=function(t){var e=this;this.writable=!1,this.doWrite(s.encodePayload(t),function(){e.writable=!0,e.emit("drain")})},n.prototype.uri=function(){var t=this.query||{},e=this.secure?"https":"http",o="";return(c.ActiveXObject||i.ua.android||i.ua.ios6||this.timestampRequests)&&(t[this.timestampParam]=+new Date),t=i.qs(t),this.port&&("https"==e&&443!=this.port||"http"==e&&80!=this.port)&&(o=":"+this.port),t.length&&(t="?"+t),e+"://"+this.hostname+o+this.path+t}}),t.register("LearnBoost-engine.io-client/lib/transports/polling-xhr.js",function(t,e,o){function n(){}function r(t){s.call(this,t),l.location&&(this.xd=t.host!=l.location.hostname||l.location.port!=t.port)}function i(t){this.method=t.method||"GET",this.uri=t.uri,this.xd=!!t.xd,this.async=!1!==t.async,this.data=void 0!=t.data?t.data:null,this.create()}var s=e("./polling"),a=e("../util"),c=e("../emitter"),p=e("debug")("engine.io-client:polling-xhr");o.exports=r,o.exports.Request=i;var l=a.global(),u=l[["Active"].concat("Object").join("X")];a.inherits(r,s),r.prototype.doOpen=function(){var t=this;a.defer(function(){s.prototype.doOpen.call(t)})},r.prototype.request=function(t){return t=t||{},t.uri=this.uri(),t.xd=this.xd,new i(t)},r.prototype.doWrite=function(t,e){var o=this.request({method:"POST",data:t}),n=this;o.on("success",e),o.on("error",function(t){n.onError("xhr post error",t)}),this.sendXhr=o},r.prototype.doPoll=function(){p("xhr poll");var t=this.request(),e=this;t.on("data",function(t){e.onData(t)}),t.on("error",function(t){e.onError("xhr poll error",t)}),this.pollXhr=t},c(i.prototype),i.prototype.create=function(){var t=this.xhr=a.request(this.xd),e=this;if(t.open(this.method,this.uri,this.async),"POST"==this.method)try{t.setRequestHeader?t.setRequestHeader("Content-type","text/plain;charset=UTF-8"):t.contentType="text/plain"}catch(o){}this.xd&&l.XDomainRequest&&t instanceof XDomainRequest?(t.onerror=function(t){e.onError(t)},t.onload=function(){e.onData(t.responseText)},t.onprogress=n):("withCredentials"in t&&(t.withCredentials=!0),t.onreadystatechange=function(){var o;try{if(4!=t.readyState)return;200==t.status||1223==t.status?o=t.responseText:e.onError(t.status)}catch(n){e.onError(n)}void 0!==o&&e.onData(o)}),p("sending xhr with url %s | data %s",this.uri,this.data),t.send(this.data),u&&(this.index=i.requestsCount++,i.requests[this.index]=this)},i.prototype.onSuccess=function(){this.emit("success"),this.cleanup()},i.prototype.onData=function(t){this.emit("data",t),this.onSuccess()},i.prototype.onError=function(t){this.emit("error",t),this.cleanup()},i.prototype.cleanup=function(){this.xhr.onreadystatechange=n,this.xhr.onload=this.xhr.onerror=n;try{this.xhr.abort()}catch(t){}u&&delete i.requests[this.index],this.xhr=null},i.prototype.abort=function(){this.cleanup()},u&&(i.requestsCount=0,i.requests={},l.attachEvent("onunload",function(){for(var t in i.requests)i.requests.hasOwnProperty(t)&&i.requests[t].abort()}))}),t.register("LearnBoost-engine.io-client/lib/transports/polling-jsonp.js",function(t,e,o){function n(t){r.call(this,t),s||(a.___eio||(a.___eio=[]),s=a.___eio),this.index=s.length;var e=this;s.push(function(t){e.onData(t)}),this.query.j=this.index}var r=e("./polling"),i=e("../util");o.exports=n;var s,a=i.global(),c=/\n/g;i.inherits(n,r),n.prototype.doOpen=function(){var t=this;i.defer(function(){r.prototype.doOpen.call(t)})},n.prototype.doClose=function(){this.script&&(this.script.parentNode.removeChild(this.script),this.script=null),this.form&&(this.form.parentNode.removeChild(this.form),this.form=null),r.prototype.doClose.call(this)},n.prototype.doPoll=function(){var t=this,e=document.createElement("script");this.script&&(this.script.parentNode.removeChild(this.script),this.script=null),e.async=!0,e.src=this.uri(),e.onerror=function(e){t.onError("jsonp poll error",e)};var o=document.getElementsByTagName("script")[0];o.parentNode.insertBefore(e,o),this.script=e,i.ua.gecko&&setTimeout(function(){var t=document.createElement("iframe");document.body.appendChild(t),document.body.removeChild(t)},100)},n.prototype.doWrite=function(t,e){function o(){n(),e()}function n(){r.iframe&&r.form.removeChild(r.iframe);try{var t='