kurento_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +41 -0
  8. data/Rakefile +25 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +7 -0
  11. data/kurento_rails.gemspec +30 -0
  12. data/lib/generators/kurento_rails/install_generator.rb +45 -0
  13. data/lib/generators/kurento_rails/templates/config/events.rb +24 -0
  14. data/lib/generators/kurento_rails/templates/controllers/kurento_controller.rb +27 -0
  15. data/lib/generators/kurento_rails/templates/controllers/kurento_websockets_controller.rb +103 -0
  16. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/adapterjs/publish/adapter.min.js +3 -0
  17. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-client/js/kurento-client.map +1 -0
  18. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-client/js/kurento-client.min.js +557 -0
  19. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-rails.js +635 -0
  20. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-utils/js/kurento-utils.map +1 -0
  21. data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-utils/js/kurento-utils.min.js +36 -0
  22. data/lib/generators/kurento_rails/templates/migrations/create_video_streams.rb +13 -0
  23. data/lib/generators/kurento_rails/templates/models/kurento_rails_video_stream.rb +2 -0
  24. data/lib/generators/kurento_rails/templates/views/kurento/broadcast.html.slim +2 -0
  25. data/lib/generators/kurento_rails/templates/views/kurento/index.html.slim +2 -0
  26. data/lib/generators/kurento_rails/templates/views/kurento/old_streams.html.slim +2 -0
  27. data/lib/kurento_rails.rb +5 -0
  28. data/lib/kurento_rails/version.rb +3 -0
  29. data/src/events.rb +24 -0
  30. data/src/kurento-rails.coffee +387 -0
  31. data/src/kurento_controller.rb +27 -0
  32. data/src/kurento_rails_video_stream.rb +2 -0
  33. data/src/kurento_websockets_controller.rb +103 -0
  34. metadata +161 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../node_modules/browserify/node_modules/browser-pack/_prelude.js","../node_modules/freeice/stun.json","../node_modules/freeice/turn.json","../lib/browser.js","../lib/index.js","../lib/WebRtcPeer.js","../node_modules/browserify/node_modules/events/events.js","../node_modules/freeice/index.js","../node_modules/inherits/inherits_browser.js","../node_modules/merge/merge.js","../node_modules/uuid/uuid.js","../node_modules/ua-parser-js/src/ua-parser.js","../node_modules/uuid/rng-browser.js","../node_modules/freeice/node_modules/normalice/index.js"],"names":["window","addEventListener","module","exports","require","WebRtcPeer","noop","error","console","trackStop","track","stop","streamStop","stream","getTracks","forEach","bufferizeCandidates","pc","onerror","candidatesQueue","this","signalingState","length","entry","shift","addIceCandidate","candidate","callback","Error","remoteDescription","push","removeFIDFromOffer","sdp","n","indexOf","slice","getSimulcastInfo","videoStream","videoTracks","getVideoTracks","lines","id","join","mode","options","setRemoteVideo","remoteVideo","getRemoteStreams","url","URL","createObjectURL","pause","src","load","log","streamEndedListener","self","emit","start","localVideo","showLocalVideo","addStream","audioStream","browser","parser","getBrowser","name","major","getMedia","constraints","undefined","MEDIA_CONSTRAINTS","getUserMedia","super_","call","Function","bind","mediaConstraints","connectionConstraints","peerConnection","sendSource","guid","uuid","v4","configuration","recursive","iceServers","freeice","onstreamended","on","onicecandidate","oncandidategatheringdone","simulcast","RTCPeerConnection","Object","defineProperties","get","currentFrame","readyState","HAVE_CURRENT_DATA","canvas","document","createElement","width","videoWidth","height","videoHeight","getContext","drawImage","candidatesQueueOut","candidategatheringdone","event","EventEmitter","listenerCount","listener","iceCandidate","RTCIceCandidate","generateOffer","offerAudio","offerVideo","audio","video","browserDependantConstraints","version","offerToReceiveAudio","offerToReceiveVideo","mandatory","OfferToReceiveAudio","OfferToReceiveVideo","optional","DtlsSrtpKeyAgreement","JSON","stringify","createOffer","offer","RTCSessionDescription","type","warn","setLocalDescription","processAnswer","getLocalSessionDescriptor","localDescription","getRemoteSessionDescriptor","muted","sdpAnswer","answer","setRemoteDescription","processOffer","sdpOffer","createAnswer","setTimeout","getScreenConstraints","constraints_","unshift","apply","removeAllListeners","cancelChooseDesktopMedia","createEnableDescriptor","method","enumerable","streams","getLocalStreams","i","tracks","j","enabled","set","value","trackSetEnable","WebRtcPeerRecvonly","WebRtcPeerSendonly","WebRtcPeerSendrecv","inherits","UAParser","framerate","ua","navigator","userAgent","prototype","audioEnabled","videoEnabled","getLocalStream","index","getRemoteStream","dispose","close","err","_events","_maxListeners","isFunction","arg","isNumber","isObject","isUndefined","defaultMaxListeners","setMaxListeners","isNaN","TypeError","er","handler","len","args","listeners","arguments","Array","addListener","m","newListener","warned","trace","once","g","removeListener","fired","list","position","splice","key","ret","emitter","normalice","opts","getServers","count","idx","out","input","concat","servers","Math","random","map","String","selected","stun","turn","stunCount","turnCount","create","ctor","superCtor","constructor","writable","configurable","TempCtor","isNode","merge_recursive","base","extend","typeOf","merge","clone","argv","result","size","item","sitem","Public","toString","toLowerCase","publicName","output","parse","s","buf","offset","ii","replace","oct","_hexToByte","unparse","bth","_byteToHex","v1","b","clockseq","_clockseq","msecs","Date","getTime","nsecs","_lastNSecs","dt","_lastMSecs","tl","tmh","node","_nodeId","rnds","rng","_rng","substr","_seedBytes","LIBVERSION","EMPTY","UNKNOWN","FUNC_TYPE","UNDEF_TYPE","OBJ_TYPE","STR_TYPE","MAJOR","MODEL","NAME","TYPE","VENDOR","VERSION","ARCHITECTURE","CONSOLE","MOBILE","TABLET","SMARTTV","WEARABLE","EMBEDDED","util","regexes","extensions","has","str1","str2","lowerize","str","split","mapper","rgx","k","p","q","matches","match","regex","props","exec","getUA","test","maps","oldsafari","1.0","1.2","1.3","2.0","2.0.2","2.0.3","2.0.4","?","device","amazon","model","Fire Phone","sprint","Evo Shift 4G","vendor","HTC","Sprint","os","windows","ME","NT 3.11","NT 4.0","2000","XP","Vista","7","8","8.1","10","RT","cpu","engine","uastring","getResult","rgxmap","getCPU","getDevice","getEngine","getOS","setUA","BROWSER","CPU","DEVICE","ENGINE","OS","define","amd","$","jQuery","Zepto","prop","global","crypto","getRandomValues","_rnds8","Uint8Array","_rnds","r","protocols","protocol","parts","trim","username","credential","urls"],"mappings":"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","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})","module.exports=[\"stun.l.google.com:19302\",\"stun1.l.google.com:19302\",\"stun2.l.google.com:19302\",\"stun3.l.google.com:19302\",\"stun4.l.google.com:19302\",\"stun.ekiga.net\",\"stun.ideasip.com\",\"stun.schlund.de\",\"stun.stunprotocol.org:3478\",\"stun.voiparound.com\",\"stun.voipbuster.com\",\"stun.voipstunt.com\",\"stun.voxgratia.org\",\"stun.services.mozilla.com\"]","module.exports=[]","if (window.addEventListener)\n module.exports = require('./index');","var WebRtcPeer = require('./WebRtcPeer');\nexports.WebRtcPeer = WebRtcPeer;","var freeice = require('freeice');\nvar inherits = require('inherits');\nvar UAParser = require('ua-parser-js');\nvar uuid = require('uuid');\nvar EventEmitter = require('events').EventEmitter;\nvar recursive = require('merge').recursive.bind(undefined, true);\ntry {\n require('kurento-browser-extensions');\n} catch (error) {\n if (typeof getScreenConstraints === 'undefined') {\n console.warn('screen sharing is not available');\n getScreenConstraints = function getScreenConstraints(sendSource, callback) {\n callback(new Error('This library is not enabled for screen sharing'));\n };\n }\n}\nvar MEDIA_CONSTRAINTS = {\n audio: true,\n video: {\n width: 640,\n framerate: 15\n }\n };\nvar ua = window && window.navigator ? window.navigator.userAgent : '';\nvar parser = new UAParser(ua);\nfunction noop(error) {\n if (error)\n console.error(error);\n}\nfunction trackStop(track) {\n track.stop && track.stop();\n}\nfunction streamStop(stream) {\n stream.getTracks().forEach(trackStop);\n}\nfunction bufferizeCandidates(pc, onerror) {\n var candidatesQueue = [];\n pc.addEventListener('signalingstatechange', function () {\n if (this.signalingState === 'stable') {\n while (candidatesQueue.length) {\n var entry = candidatesQueue.shift();\n this.addIceCandidate(entry.candidate, entry.callback, entry.callback);\n }\n }\n });\n return function (candidate, callback) {\n callback = callback || onerror;\n switch (pc.signalingState) {\n case 'closed':\n callback(new Error('PeerConnection object is closed'));\n break;\n case 'stable':\n if (pc.remoteDescription) {\n pc.addIceCandidate(candidate, callback, callback);\n break;\n }\n default:\n candidatesQueue.push({\n candidate: candidate,\n callback: callback\n });\n }\n };\n}\nfunction removeFIDFromOffer(sdp) {\n var n = sdp.indexOf('a=ssrc-group:FID');\n if (n > 0) {\n return sdp.slice(0, n);\n } else {\n return sdp;\n }\n}\nfunction getSimulcastInfo(videoStream) {\n var videoTracks = videoStream.getVideoTracks();\n var lines = [\n 'a=x-google-flag:conference',\n 'a=ssrc-group:SIM 1 2 3',\n 'a=ssrc:1 cname:localVideo',\n 'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:1 mslabel:' + videoStream.id,\n 'a=ssrc:1 label:' + videoTracks[0].id,\n 'a=ssrc:2 cname:localVideo',\n 'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:2 mslabel:' + videoStream.id,\n 'a=ssrc:2 label:' + videoTracks[0].id,\n 'a=ssrc:3 cname:localVideo',\n 'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:3 mslabel:' + videoStream.id,\n 'a=ssrc:3 label:' + videoTracks[0].id\n ];\n lines.push('');\n return lines.join('\\n');\n}\nfunction WebRtcPeer(mode, options, callback) {\n if (!(this instanceof WebRtcPeer)) {\n return new WebRtcPeer(mode, options, callback);\n }\n WebRtcPeer.super_.call(this);\n if (options instanceof Function) {\n callback = options;\n options = undefined;\n }\n options = options || {};\n callback = (callback || noop).bind(this);\n var localVideo = options.localVideo;\n var remoteVideo = options.remoteVideo;\n var videoStream = options.videoStream;\n var audioStream = options.audioStream;\n var mediaConstraints = options.mediaConstraints;\n var connectionConstraints = options.connectionConstraints;\n var pc = options.peerConnection;\n var sendSource = options.sendSource || 'webcam';\n var guid = uuid.v4();\n var configuration = recursive({ iceServers: freeice() }, options.configuration);\n var onstreamended = options.onstreamended;\n if (onstreamended)\n this.on('streamended', onstreamended);\n var onicecandidate = options.onicecandidate;\n if (onicecandidate)\n this.on('icecandidate', onicecandidate);\n var oncandidategatheringdone = options.oncandidategatheringdone;\n if (oncandidategatheringdone) {\n this.on('candidategatheringdone', oncandidategatheringdone);\n }\n var simulcast = options.simulcast;\n if (!pc)\n pc = new RTCPeerConnection(configuration);\n Object.defineProperties(this, {\n 'peerConnection': {\n get: function () {\n return pc;\n }\n },\n 'remoteVideo': {\n get: function () {\n return remoteVideo;\n }\n },\n 'localVideo': {\n get: function () {\n return localVideo;\n }\n },\n 'currentFrame': {\n get: function () {\n if (!remoteVideo)\n return;\n if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)\n throw new Error('No video stream data available');\n var canvas = document.createElement('canvas');\n canvas.width = remoteVideo.videoWidth;\n canvas.height = remoteVideo.videoHeight;\n canvas.getContext('2d').drawImage(remoteVideo, 0, 0);\n return canvas;\n }\n }\n });\n var self = this;\n var candidatesQueueOut = [];\n var candidategatheringdone = false;\n pc.addEventListener('icecandidate', function (event) {\n var candidate = event.candidate;\n if (EventEmitter.listenerCount(self, 'icecandidate') || EventEmitter.listenerCount(self, 'candidategatheringdone')) {\n if (candidate) {\n self.emit('icecandidate', candidate);\n candidategatheringdone = false;\n } else if (!candidategatheringdone) {\n self.emit('candidategatheringdone');\n candidategatheringdone = true;\n }\n } else if (!candidategatheringdone) {\n candidatesQueueOut.push(candidate);\n if (!candidate)\n candidategatheringdone = true;\n }\n });\n this.on('newListener', function (event, listener) {\n if (event === 'icecandidate' || event === 'candidategatheringdone') {\n while (candidatesQueueOut.length) {\n var candidate = candidatesQueueOut.shift();\n if (!candidate === (event === 'candidategatheringdone')) {\n listener(candidate);\n }\n }\n }\n });\n var addIceCandidate = bufferizeCandidates(pc);\n this.addIceCandidate = function (iceCandidate, callback) {\n var candidate = new RTCIceCandidate(iceCandidate);\n console.log('ICE candidate received');\n callback = (callback || noop).bind(this);\n addIceCandidate(candidate, callback);\n };\n this.generateOffer = function (callback) {\n callback = callback.bind(this);\n var browser = parser.getBrowser();\n var offerAudio = true;\n var offerVideo = true;\n if (mediaConstraints) {\n offerAudio = typeof mediaConstraints.audio === 'boolean' ? mediaConstraints.audio : true;\n offerVideo = typeof mediaConstraints.video === 'boolean' ? mediaConstraints.video : true;\n }\n var browserDependantConstraints = browser.name === 'Firefox' && browser.version > 34 ? {\n offerToReceiveAudio: mode !== 'sendonly' && offerAudio,\n offerToReceiveVideo: mode !== 'sendonly' && offerVideo\n } : {\n mandatory: {\n OfferToReceiveAudio: mode !== 'sendonly' && offerAudio,\n OfferToReceiveVideo: mode !== 'sendonly' && offerVideo\n },\n optional: [{ DtlsSrtpKeyAgreement: true }]\n };\n var constraints = recursive(browserDependantConstraints, connectionConstraints);\n console.log('constraints: ' + JSON.stringify(constraints));\n pc.createOffer(function (offer) {\n console.log('Created SDP offer');\n if (simulcast) {\n if (browser.name === 'Chrome' || browser.name === 'Chromium') {\n console.log('Adding multicast info');\n offer = new RTCSessionDescription({\n 'type': offer.type,\n 'sdp': removeFIDFromOffer(offer.sdp) + getSimulcastInfo(videoStream)\n });\n } else {\n console.warn('Simulcast is only available in Chrome browser.');\n }\n }\n pc.setLocalDescription(offer, function () {\n console.log('Local description set', offer.sdp);\n callback(null, offer.sdp, self.processAnswer.bind(self));\n }, callback);\n }, callback, constraints);\n };\n this.getLocalSessionDescriptor = function () {\n return pc.localDescription;\n };\n this.getRemoteSessionDescriptor = function () {\n return pc.remoteDescription;\n };\n function setRemoteVideo() {\n if (remoteVideo) {\n var stream = pc.getRemoteStreams()[0];\n var url = stream ? URL.createObjectURL(stream) : '';\n remoteVideo.pause();\n remoteVideo.src = url;\n remoteVideo.load();\n console.log('Remote URL:', url);\n }\n }\n this.showLocalVideo = function () {\n localVideo.src = URL.createObjectURL(videoStream);\n localVideo.muted = true;\n };\n this.processAnswer = function (sdpAnswer, callback) {\n callback = (callback || noop).bind(this);\n var answer = new RTCSessionDescription({\n type: 'answer',\n sdp: sdpAnswer\n });\n console.log('SDP answer received, setting remote description');\n if (pc.signalingState === 'closed') {\n return callback('PeerConnection is closed');\n }\n pc.setRemoteDescription(answer, function () {\n setRemoteVideo();\n callback();\n }, callback);\n };\n this.processOffer = function (sdpOffer, callback) {\n callback = callback.bind(this);\n var offer = new RTCSessionDescription({\n type: 'offer',\n sdp: sdpOffer\n });\n console.log('SDP offer received, setting remote description');\n if (pc.signalingState === 'closed') {\n return callback('PeerConnection is closed');\n }\n pc.setRemoteDescription(offer, function () {\n setRemoteVideo();\n pc.createAnswer(function (answer) {\n console.log('Created SDP answer');\n pc.setLocalDescription(answer, function () {\n console.log('Local description set', answer.sdp);\n callback(null, answer.sdp);\n }, callback);\n }, callback);\n }, callback);\n };\n function streamEndedListener() {\n self.emit('streamended', this);\n }\n function start() {\n if (pc.signalingState === 'closed') {\n callback('The peer connection object is in \"closed\" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');\n }\n if (videoStream && localVideo) {\n self.showLocalVideo();\n }\n if (videoStream) {\n videoStream.addEventListener('ended', streamEndedListener);\n pc.addStream(videoStream);\n }\n if (audioStream) {\n audioStream.addEventListener('ended', streamEndedListener);\n pc.addStream(audioStream);\n }\n var browser = parser.getBrowser();\n if (mode === 'sendonly' && (browser.name === 'Chrome' || browser.name === 'Chromium') && browser.major === 39) {\n mode = 'sendrecv';\n }\n callback();\n }\n if (mode !== 'recvonly' && !videoStream && !audioStream) {\n function getMedia(constraints) {\n if (constraints === undefined) {\n constraints = MEDIA_CONSTRAINTS;\n }\n getUserMedia(constraints, function (stream) {\n videoStream = stream;\n start();\n }, callback);\n }\n if (sendSource === 'webcam') {\n getMedia(mediaConstraints);\n } else {\n getScreenConstraints(sendSource, function (error, constraints_) {\n if (error)\n return callback(error);\n constraints = [mediaConstraints];\n constraints.unshift(constraints_);\n getMedia(recursive.apply(undefined, constraints));\n }, guid);\n }\n } else {\n setTimeout(start, 0);\n }\n this.on('_dispose', function () {\n if (localVideo) {\n localVideo.pause();\n localVideo.src = '';\n localVideo.load();\n }\n if (remoteVideo) {\n remoteVideo.pause();\n remoteVideo.src = '';\n remoteVideo.load();\n }\n self.removeAllListeners();\n if (window.cancelChooseDesktopMedia !== undefined) {\n window.cancelChooseDesktopMedia(guid);\n }\n });\n}\ninherits(WebRtcPeer, EventEmitter);\nfunction createEnableDescriptor(type) {\n var method = 'get' + type + 'Tracks';\n return {\n enumerable: true,\n get: function () {\n if (!this.peerConnection)\n return;\n var streams = this.peerConnection.getLocalStreams();\n if (!streams.length)\n return;\n for (var i = 0, stream; stream = streams[i]; i++) {\n var tracks = stream[method]();\n for (var j = 0, track; track = tracks[j]; j++)\n if (!track.enabled)\n return false;\n }\n return true;\n },\n set: function (value) {\n function trackSetEnable(track) {\n track.enabled = value;\n }\n this.peerConnection.getLocalStreams().forEach(function (stream) {\n stream[method]().forEach(trackSetEnable);\n });\n }\n };\n}\nObject.defineProperties(WebRtcPeer.prototype, {\n 'enabled': {\n enumerable: true,\n get: function () {\n return this.audioEnabled && this.videoEnabled;\n },\n set: function (value) {\n this.audioEnabled = this.videoEnabled = value;\n }\n },\n 'audioEnabled': createEnableDescriptor('Audio'),\n 'videoEnabled': createEnableDescriptor('Video')\n});\nWebRtcPeer.prototype.getLocalStream = function (index) {\n if (this.peerConnection) {\n return this.peerConnection.getLocalStreams()[index || 0];\n }\n};\nWebRtcPeer.prototype.getRemoteStream = function (index) {\n if (this.peerConnection) {\n return this.peerConnection.getRemoteStreams()[index || 0];\n }\n};\nWebRtcPeer.prototype.dispose = function () {\n console.log('Disposing WebRtcPeer');\n var pc = this.peerConnection;\n try {\n if (pc) {\n if (pc.signalingState === 'closed')\n return;\n pc.getLocalStreams().forEach(streamStop);\n pc.close();\n }\n } catch (err) {\n console.warn('Exception disposing webrtc peer ' + err);\n }\n this.emit('_dispose');\n};\nfunction WebRtcPeerRecvonly(options, callback) {\n if (!(this instanceof WebRtcPeerRecvonly)) {\n return new WebRtcPeerRecvonly(options, callback);\n }\n WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback);\n}\ninherits(WebRtcPeerRecvonly, WebRtcPeer);\nfunction WebRtcPeerSendonly(options, callback) {\n if (!(this instanceof WebRtcPeerSendonly)) {\n return new WebRtcPeerSendonly(options, callback);\n }\n WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback);\n}\ninherits(WebRtcPeerSendonly, WebRtcPeer);\nfunction WebRtcPeerSendrecv(options, callback) {\n if (!(this instanceof WebRtcPeerSendrecv)) {\n return new WebRtcPeerSendrecv(options, callback);\n }\n WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback);\n}\ninherits(WebRtcPeerSendrecv, WebRtcPeer);\nexports.bufferizeCandidates = bufferizeCandidates;\nexports.WebRtcPeerRecvonly = WebRtcPeerRecvonly;\nexports.WebRtcPeerSendonly = WebRtcPeerSendonly;\nexports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfunction EventEmitter() {\n this._events = this._events || {};\n this._maxListeners = this._maxListeners || undefined;\n}\nmodule.exports = EventEmitter;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nEventEmitter.defaultMaxListeners = 10;\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function(n) {\n if (!isNumber(n) || n < 0 || isNaN(n))\n throw TypeError('n must be a positive number');\n this._maxListeners = n;\n return this;\n};\n\nEventEmitter.prototype.emit = function(type) {\n var er, handler, len, args, i, listeners;\n\n if (!this._events)\n this._events = {};\n\n // If there is no 'error' event listener then throw.\n if (type === 'error') {\n if (!this._events.error ||\n (isObject(this._events.error) && !this._events.error.length)) {\n er = arguments[1];\n if (er instanceof Error) {\n throw er; // Unhandled 'error' event\n }\n throw TypeError('Uncaught, unspecified \"error\" event.');\n }\n }\n\n handler = this._events[type];\n\n if (isUndefined(handler))\n return false;\n\n if (isFunction(handler)) {\n switch (arguments.length) {\n // fast cases\n case 1:\n handler.call(this);\n break;\n case 2:\n handler.call(this, arguments[1]);\n break;\n case 3:\n handler.call(this, arguments[1], arguments[2]);\n break;\n // slower\n default:\n len = arguments.length;\n args = new Array(len - 1);\n for (i = 1; i < len; i++)\n args[i - 1] = arguments[i];\n handler.apply(this, args);\n }\n } else if (isObject(handler)) {\n len = arguments.length;\n args = new Array(len - 1);\n for (i = 1; i < len; i++)\n args[i - 1] = arguments[i];\n\n listeners = handler.slice();\n len = listeners.length;\n for (i = 0; i < len; i++)\n listeners[i].apply(this, args);\n }\n\n return true;\n};\n\nEventEmitter.prototype.addListener = function(type, listener) {\n var m;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events)\n this._events = {};\n\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (this._events.newListener)\n this.emit('newListener', type,\n isFunction(listener.listener) ?\n listener.listener : listener);\n\n if (!this._events[type])\n // Optimize the case of one listener. Don't need the extra array object.\n this._events[type] = listener;\n else if (isObject(this._events[type]))\n // If we've already got an array, just append.\n this._events[type].push(listener);\n else\n // Adding the second element, need to change to array.\n this._events[type] = [this._events[type], listener];\n\n // Check for listener leak\n if (isObject(this._events[type]) && !this._events[type].warned) {\n var m;\n if (!isUndefined(this._maxListeners)) {\n m = this._maxListeners;\n } else {\n m = EventEmitter.defaultMaxListeners;\n }\n\n if (m && m > 0 && this._events[type].length > m) {\n this._events[type].warned = true;\n console.error('(node) warning: possible EventEmitter memory ' +\n 'leak detected. %d listeners added. ' +\n 'Use emitter.setMaxListeners() to increase limit.',\n this._events[type].length);\n if (typeof console.trace === 'function') {\n // not supported in IE 10\n console.trace();\n }\n }\n }\n\n return this;\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.once = function(type, listener) {\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n var fired = false;\n\n function g() {\n this.removeListener(type, g);\n\n if (!fired) {\n fired = true;\n listener.apply(this, arguments);\n }\n }\n\n g.listener = listener;\n this.on(type, g);\n\n return this;\n};\n\n// emits a 'removeListener' event iff the listener was removed\nEventEmitter.prototype.removeListener = function(type, listener) {\n var list, position, length, i;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events || !this._events[type])\n return this;\n\n list = this._events[type];\n length = list.length;\n position = -1;\n\n if (list === listener ||\n (isFunction(list.listener) && list.listener === listener)) {\n delete this._events[type];\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n\n } else if (isObject(list)) {\n for (i = length; i-- > 0;) {\n if (list[i] === listener ||\n (list[i].listener && list[i].listener === listener)) {\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (list.length === 1) {\n list.length = 0;\n delete this._events[type];\n } else {\n list.splice(position, 1);\n }\n\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n }\n\n return this;\n};\n\nEventEmitter.prototype.removeAllListeners = function(type) {\n var key, listeners;\n\n if (!this._events)\n return this;\n\n // not listening for removeListener, no need to emit\n if (!this._events.removeListener) {\n if (arguments.length === 0)\n this._events = {};\n else if (this._events[type])\n delete this._events[type];\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n for (key in this._events) {\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = {};\n return this;\n }\n\n listeners = this._events[type];\n\n if (isFunction(listeners)) {\n this.removeListener(type, listeners);\n } else {\n // LIFO order\n while (listeners.length)\n this.removeListener(type, listeners[listeners.length - 1]);\n }\n delete this._events[type];\n\n return this;\n};\n\nEventEmitter.prototype.listeners = function(type) {\n var ret;\n if (!this._events || !this._events[type])\n ret = [];\n else if (isFunction(this._events[type]))\n ret = [this._events[type]];\n else\n ret = this._events[type].slice();\n return ret;\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n var ret;\n if (!emitter._events || !emitter._events[type])\n ret = 0;\n else if (isFunction(emitter._events[type]))\n ret = 1;\n else\n ret = emitter._events[type].length;\n return ret;\n};\n\nfunction isFunction(arg) {\n return typeof arg === 'function';\n}\n\nfunction isNumber(arg) {\n return typeof arg === 'number';\n}\n\nfunction isObject(arg) {\n return typeof arg === 'object' && arg !== null;\n}\n\nfunction isUndefined(arg) {\n return arg === void 0;\n}\n","/* jshint node: true */\n'use strict';\n\nvar normalice = require('normalice');\n\n/**\n # freeice\n\n The `freeice` module is a simple way of getting random STUN or TURN server\n for your WebRTC application. The list of servers (just STUN at this stage)\n were sourced from this [gist](https://gist.github.com/zziuni/3741933).\n\n ## Example Use\n\n The following demonstrates how you can use `freeice` with\n [rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):\n\n <<< examples/quickconnect.js\n\n As the `freeice` module generates ice servers in a list compliant with the\n WebRTC spec you will be able to use it with raw `RTCPeerConnection`\n constructors and other WebRTC libraries.\n\n ## Hey, don't use my STUN/TURN server!\n\n If for some reason your free STUN or TURN server ends up in the\n list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or\n [turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))\n that is used in this module, you can feel\n free to open an issue on this repository and those servers will be removed\n within 24 hours (or sooner). This is the quickest and probably the most\n polite way to have something removed (and provides us some visibility\n if someone opens a pull request requesting that a server is added).\n\n ## Please add my server!\n\n If you have a server that you wish to add to the list, that's awesome! I'm\n sure I speak on behalf of a whole pile of WebRTC developers who say thanks.\n To get it into the list, feel free to either open a pull request or if you\n find that process a bit daunting then just create an issue requesting\n the addition of the server (make sure you provide all the details, and if\n you have a Terms of Service then including that in the PR/issue would be\n awesome).\n\n ## I know of a free server, can I add it?\n\n Sure, if you do your homework and make sure it is ok to use (I'm currently\n in the process of reviewing the terms of those STUN servers included from\n the original list). If it's ok to go, then please see the previous entry\n for how to add it.\n\n ## Current List of Servers\n\n * current as at the time of last `README.md` file generation\n\n ### STUN\n\n <<< stun.json\n\n ### TURN\n\n <<< turn.json\n\n**/\n\nvar freeice = module.exports = function(opts) {\n // if a list of servers has been provided, then use it instead of defaults\n var servers = {\n stun: (opts || {}).stun || require('./stun.json'),\n turn: (opts || {}).turn || require('./turn.json')\n };\n\n var stunCount = (opts || {}).stunCount || 2;\n var turnCount = (opts || {}).turnCount || 0;\n var selected;\n\n function getServers(type, count) {\n var out = [];\n var input = [].concat(servers[type]);\n var idx;\n\n while (input.length && out.length < count) {\n idx = (Math.random() * input.length) | 0;\n out = out.concat(input.splice(idx, 1));\n }\n\n return out.map(function(url) {\n //If it's a not a string, don't try to \"normalice\" it otherwise using type:url will screw it up\n if ((typeof url !== 'string') && (! (url instanceof String))) {\n return url;\n } else {\n return normalice(type + ':' + url);\n }\n });\n }\n\n // add stun servers\n selected = [].concat(getServers('stun', stunCount));\n\n if (turnCount) {\n selected = selected.concat(getServers('turn', turnCount));\n }\n\n return selected;\n};\n","if (typeof Object.create === 'function') {\n // implementation from standard node.js 'util' module\n module.exports = function inherits(ctor, superCtor) {\n ctor.super_ = superCtor\n ctor.prototype = Object.create(superCtor.prototype, {\n constructor: {\n value: ctor,\n enumerable: false,\n writable: true,\n configurable: true\n }\n });\n };\n} else {\n // old school shim for old browsers\n module.exports = function inherits(ctor, superCtor) {\n ctor.super_ = superCtor\n var TempCtor = function () {}\n TempCtor.prototype = superCtor.prototype\n ctor.prototype = new TempCtor()\n ctor.prototype.constructor = ctor\n }\n}\n","/*!\r\n * @name JavaScript/NodeJS Merge v1.2.0\r\n * @author yeikos\r\n * @repository https://github.com/yeikos/js.merge\r\n\r\n * Copyright 2014 yeikos - MIT license\r\n * https://raw.github.com/yeikos/js.merge/master/LICENSE\r\n */\r\n\r\n;(function(isNode) {\r\n\r\n\t/**\r\n\t * Merge one or more objects \r\n\t * @param bool? clone\r\n\t * @param mixed,... arguments\r\n\t * @return object\r\n\t */\r\n\r\n\tvar Public = function(clone) {\r\n\r\n\t\treturn merge(clone === true, false, arguments);\r\n\r\n\t}, publicName = 'merge';\r\n\r\n\t/**\r\n\t * Merge two or more objects recursively \r\n\t * @param bool? clone\r\n\t * @param mixed,... arguments\r\n\t * @return object\r\n\t */\r\n\r\n\tPublic.recursive = function(clone) {\r\n\r\n\t\treturn merge(clone === true, true, arguments);\r\n\r\n\t};\r\n\r\n\t/**\r\n\t * Clone the input removing any reference\r\n\t * @param mixed input\r\n\t * @return mixed\r\n\t */\r\n\r\n\tPublic.clone = function(input) {\r\n\r\n\t\tvar output = input,\r\n\t\t\ttype = typeOf(input),\r\n\t\t\tindex, size;\r\n\r\n\t\tif (type === 'array') {\r\n\r\n\t\t\toutput = [];\r\n\t\t\tsize = input.length;\r\n\r\n\t\t\tfor (index=0;index<size;++index)\r\n\r\n\t\t\t\toutput[index] = Public.clone(input[index]);\r\n\r\n\t\t} else if (type === 'object') {\r\n\r\n\t\t\toutput = {};\r\n\r\n\t\t\tfor (index in input)\r\n\r\n\t\t\t\toutput[index] = Public.clone(input[index]);\r\n\r\n\t\t}\r\n\r\n\t\treturn output;\r\n\r\n\t};\r\n\r\n\t/**\r\n\t * Merge two objects recursively\r\n\t * @param mixed input\r\n\t * @param mixed extend\r\n\t * @return mixed\r\n\t */\r\n\r\n\tfunction merge_recursive(base, extend) {\r\n\r\n\t\tif (typeOf(base) !== 'object')\r\n\r\n\t\t\treturn extend;\r\n\r\n\t\tfor (var key in extend) {\r\n\r\n\t\t\tif (typeOf(base[key]) === 'object' && typeOf(extend[key]) === 'object') {\r\n\r\n\t\t\t\tbase[key] = merge_recursive(base[key], extend[key]);\r\n\r\n\t\t\t} else {\r\n\r\n\t\t\t\tbase[key] = extend[key];\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\treturn base;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Merge two or more objects\r\n\t * @param bool clone\r\n\t * @param bool recursive\r\n\t * @param array argv\r\n\t * @return object\r\n\t */\r\n\r\n\tfunction merge(clone, recursive, argv) {\r\n\r\n\t\tvar result = argv[0],\r\n\t\t\tsize = argv.length;\r\n\r\n\t\tif (clone || typeOf(result) !== 'object')\r\n\r\n\t\t\tresult = {};\r\n\r\n\t\tfor (var index=0;index<size;++index) {\r\n\r\n\t\t\tvar item = argv[index],\r\n\r\n\t\t\t\ttype = typeOf(item);\r\n\r\n\t\t\tif (type !== 'object') continue;\r\n\r\n\t\t\tfor (var key in item) {\r\n\r\n\t\t\t\tvar sitem = clone ? Public.clone(item[key]) : item[key];\r\n\r\n\t\t\t\tif (recursive) {\r\n\r\n\t\t\t\t\tresult[key] = merge_recursive(result[key], sitem);\r\n\r\n\t\t\t\t} else {\r\n\r\n\t\t\t\t\tresult[key] = sitem;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Get type of variable\r\n\t * @param mixed input\r\n\t * @return string\r\n\t *\r\n\t * @see http://jsperf.com/typeofvar\r\n\t */\r\n\r\n\tfunction typeOf(input) {\r\n\r\n\t\treturn ({}).toString.call(input).slice(8, -1).toLowerCase();\r\n\r\n\t}\r\n\r\n\tif (isNode) {\r\n\r\n\t\tmodule.exports = Public;\r\n\r\n\t} else {\r\n\r\n\t\twindow[publicName] = Public;\r\n\r\n\t}\r\n\r\n})(typeof module === 'object' && module && typeof module.exports === 'object' && module.exports);","// uuid.js\n//\n// Copyright (c) 2010-2012 Robert Kieffer\n// MIT License - http://opensource.org/licenses/mit-license.php\n\n// Unique ID creation requires a high quality random # generator. We feature\n// detect to determine the best RNG source, normalizing to a function that\n// returns 128-bits of randomness, since that's what's usually required\nvar _rng = require('./rng');\n\n// Maps for number <-> hex string conversion\nvar _byteToHex = [];\nvar _hexToByte = {};\nfor (var i = 0; i < 256; i++) {\n _byteToHex[i] = (i + 0x100).toString(16).substr(1);\n _hexToByte[_byteToHex[i]] = i;\n}\n\n// **`parse()` - Parse a UUID into it's component bytes**\nfunction parse(s, buf, offset) {\n var i = (buf && offset) || 0, ii = 0;\n\n buf = buf || [];\n s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {\n if (ii < 16) { // Don't overflow!\n buf[i + ii++] = _hexToByte[oct];\n }\n });\n\n // Zero out remaining bytes if string was short\n while (ii < 16) {\n buf[i + ii++] = 0;\n }\n\n return buf;\n}\n\n// **`unparse()` - Convert UUID byte array (ala parse()) into a string**\nfunction unparse(buf, offset) {\n var i = offset || 0, bth = _byteToHex;\n return bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]];\n}\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\n\n// random #'s we need to init node and clockseq\nvar _seedBytes = _rng();\n\n// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\nvar _nodeId = [\n _seedBytes[0] | 0x01,\n _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]\n];\n\n// Per 4.2.2, randomize (14 bit) clockseq\nvar _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;\n\n// Previous uuid creation time\nvar _lastMSecs = 0, _lastNSecs = 0;\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v1(options, buf, offset) {\n var i = buf && offset || 0;\n var b = buf || [];\n\n options = options || {};\n\n var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;\n\n // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so\n // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();\n\n // Per 4.2.1.2, use count of uuid's generated during the current clock\n // cycle to simulate higher resolution clock\n var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;\n\n // Time since last uuid creation (in msecs)\n var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;\n\n // Per 4.2.1.2, Bump clockseq on clock regression\n if (dt < 0 && options.clockseq === undefined) {\n clockseq = clockseq + 1 & 0x3fff;\n }\n\n // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n // time interval\n if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n nsecs = 0;\n }\n\n // Per 4.2.1.2 Throw error if too many uuids are requested\n if (nsecs >= 10000) {\n throw new Error('uuid.v1(): Can\\'t create more than 10M uuids/sec');\n }\n\n _lastMSecs = msecs;\n _lastNSecs = nsecs;\n _clockseq = clockseq;\n\n // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n msecs += 12219292800000;\n\n // `time_low`\n var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n b[i++] = tl >>> 24 & 0xff;\n b[i++] = tl >>> 16 & 0xff;\n b[i++] = tl >>> 8 & 0xff;\n b[i++] = tl & 0xff;\n\n // `time_mid`\n var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;\n b[i++] = tmh >>> 8 & 0xff;\n b[i++] = tmh & 0xff;\n\n // `time_high_and_version`\n b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n b[i++] = tmh >>> 16 & 0xff;\n\n // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n b[i++] = clockseq >>> 8 | 0x80;\n\n // `clock_seq_low`\n b[i++] = clockseq & 0xff;\n\n // `node`\n var node = options.node || _nodeId;\n for (var n = 0; n < 6; n++) {\n b[i + n] = node[n];\n }\n\n return buf ? buf : unparse(b);\n}\n\n// **`v4()` - Generate random UUID**\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v4(options, buf, offset) {\n // Deprecated - 'format' argument, as supported in v1.2\n var i = buf && offset || 0;\n\n if (typeof(options) == 'string') {\n buf = options == 'binary' ? new Array(16) : null;\n options = null;\n }\n options = options || {};\n\n var rnds = options.random || (options.rng || _rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n for (var ii = 0; ii < 16; ii++) {\n buf[i + ii] = rnds[ii];\n }\n }\n\n return buf || unparse(rnds);\n}\n\n// Export public API\nvar uuid = v4;\nuuid.v1 = v1;\nuuid.v4 = v4;\nuuid.parse = parse;\nuuid.unparse = unparse;\n\nmodule.exports = uuid;\n","/**\n * UAParser.js v0.7.9\n * Lightweight JavaScript-based User-Agent string parser\n * https://github.com/faisalman/ua-parser-js\n *\n * Copyright © 2012-2015 Faisal Salman <fyzlman@gmail.com>\n * Dual licensed under GPLv2 & MIT\n */\n\n(function (window, undefined) {\n\n 'use strict';\n\n //////////////\n // Constants\n /////////////\n\n\n var LIBVERSION = '0.7.9',\n EMPTY = '',\n UNKNOWN = '?',\n FUNC_TYPE = 'function',\n UNDEF_TYPE = 'undefined',\n OBJ_TYPE = 'object',\n STR_TYPE = 'string',\n MAJOR = 'major', // deprecated\n MODEL = 'model',\n NAME = 'name',\n TYPE = 'type',\n VENDOR = 'vendor',\n VERSION = 'version',\n ARCHITECTURE= 'architecture',\n CONSOLE = 'console',\n MOBILE = 'mobile',\n TABLET = 'tablet',\n SMARTTV = 'smarttv',\n WEARABLE = 'wearable',\n EMBEDDED = 'embedded';\n\n\n ///////////\n // Helper\n //////////\n\n\n var util = {\n extend : function (regexes, extensions) {\n for (var i in extensions) {\n if (\"browser cpu device engine os\".indexOf(i) !== -1 && extensions[i].length % 2 === 0) {\n regexes[i] = extensions[i].concat(regexes[i]);\n }\n }\n return regexes;\n },\n has : function (str1, str2) {\n if (typeof str1 === \"string\") {\n return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;\n } else {\n return false;\n }\n },\n lowerize : function (str) {\n return str.toLowerCase();\n },\n major : function (version) {\n return typeof(version) === STR_TYPE ? version.split(\".\")[0] : undefined;\n }\n };\n\n\n ///////////////\n // Map helper\n //////////////\n\n\n var mapper = {\n\n rgx : function () {\n\n var result, i = 0, j, k, p, q, matches, match, args = arguments;\n\n // loop through all regexes maps\n while (i < args.length && !matches) {\n\n var regex = args[i], // even sequence (0,2,4,..)\n props = args[i + 1]; // odd sequence (1,3,5,..)\n\n // construct object barebones\n if (typeof result === UNDEF_TYPE) {\n result = {};\n for (p in props) {\n q = props[p];\n if (typeof q === OBJ_TYPE) {\n result[q[0]] = undefined;\n } else {\n result[q] = undefined;\n }\n }\n }\n\n // try matching uastring with regexes\n j = k = 0;\n while (j < regex.length && !matches) {\n matches = regex[j++].exec(this.getUA());\n if (!!matches) {\n for (p = 0; p < props.length; p++) {\n match = matches[++k];\n q = props[p];\n // check if given property is actually array\n if (typeof q === OBJ_TYPE && q.length > 0) {\n if (q.length == 2) {\n if (typeof q[1] == FUNC_TYPE) {\n // assign modified match\n result[q[0]] = q[1].call(this, match);\n } else {\n // assign given value, ignore regex match\n result[q[0]] = q[1];\n }\n } else if (q.length == 3) {\n // check whether function or regex\n if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {\n // call function (usually string mapper)\n result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;\n } else {\n // sanitize match using given regex\n result[q[0]] = match ? match.replace(q[1], q[2]) : undefined;\n }\n } else if (q.length == 4) {\n result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;\n }\n } else {\n result[q] = match ? match : undefined;\n }\n }\n }\n }\n i += 2;\n }\n return result;\n },\n\n str : function (str, map) {\n\n for (var i in map) {\n // check if array\n if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {\n for (var j = 0; j < map[i].length; j++) {\n if (util.has(map[i][j], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n } else if (util.has(map[i], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n return str;\n }\n };\n\n\n ///////////////\n // String map\n //////////////\n\n\n var maps = {\n\n browser : {\n oldsafari : {\n version : {\n '1.0' : '/8',\n '1.2' : '/1',\n '1.3' : '/3',\n '2.0' : '/412',\n '2.0.2' : '/416',\n '2.0.3' : '/417',\n '2.0.4' : '/419',\n '?' : '/'\n }\n }\n },\n\n device : {\n amazon : {\n model : {\n 'Fire Phone' : ['SD', 'KF']\n }\n },\n sprint : {\n model : {\n 'Evo Shift 4G' : '7373KT'\n },\n vendor : {\n 'HTC' : 'APA',\n 'Sprint' : 'Sprint'\n }\n }\n },\n\n os : {\n windows : {\n version : {\n 'ME' : '4.90',\n 'NT 3.11' : 'NT3.51',\n 'NT 4.0' : 'NT4.0',\n '2000' : 'NT 5.0',\n 'XP' : ['NT 5.1', 'NT 5.2'],\n 'Vista' : 'NT 6.0',\n '7' : 'NT 6.1',\n '8' : 'NT 6.2',\n '8.1' : 'NT 6.3',\n '10' : ['NT 6.4', 'NT 10.0'],\n 'RT' : 'ARM'\n }\n }\n }\n };\n\n\n //////////////\n // Regex map\n /////////////\n\n\n var regexes = {\n\n browser : [[\n\n // Presto based\n /(opera\\smini)\\/([\\w\\.-]+)/i, // Opera Mini\n /(opera\\s[mobiletab]+).+version\\/([\\w\\.-]+)/i, // Opera Mobi/Tablet\n /(opera).+version\\/([\\w\\.]+)/i, // Opera > 9.80\n /(opera)[\\/\\s]+([\\w\\.]+)/i // Opera < 9.80\n\n ], [NAME, VERSION], [\n\n /\\s(opr)\\/([\\w\\.]+)/i // Opera Webkit\n ], [[NAME, 'Opera'], VERSION], [\n\n // Mixed\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /(lunascape|maxthon|netfront|jasmine|blazer)[\\/\\s]?([\\w\\.]+)*/i,\n // Lunascape/Maxthon/Netfront/Jasmine/Blazer\n\n // Trident based\n /(avant\\s|iemobile|slim|baidu)(?:browser)?[\\/\\s]?([\\w\\.]*)/i,\n // Avant/IEMobile/SlimBrowser/Baidu\n /(?:ms|\\()(ie)\\s([\\w\\.]+)/i, // Internet Explorer\n\n // Webkit/KHTML based\n /(rekonq)\\/([\\w\\.]+)*/i, // Rekonq\n /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium)\\/([\\w\\.-]+)/i\n // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium\n ], [NAME, VERSION], [\n\n /(trident).+rv[:\\s]([\\w\\.]+).+like\\sgecko/i // IE11\n ], [[NAME, 'IE'], VERSION], [\n\n /(edge)\\/((\\d+)?[\\w\\.]+)/i // Microsoft Edge\n ], [NAME, VERSION], [\n\n /(yabrowser)\\/([\\w\\.]+)/i // Yandex\n ], [[NAME, 'Yandex'], VERSION], [\n\n /(comodo_dragon)\\/([\\w\\.]+)/i // Comodo Dragon\n ], [[NAME, /_/g, ' '], VERSION], [\n\n /(chrome|omniweb|arora|[tizenoka]{5}\\s?browser)\\/v?([\\w\\.]+)/i,\n // Chrome/OmniWeb/Arora/Tizen/Nokia\n /(uc\\s?browser|qqbrowser)[\\/\\s]?([\\w\\.]+)/i\n // UCBrowser/QQBrowser\n ], [NAME, VERSION], [\n\n /(dolfin)\\/([\\w\\.]+)/i // Dolphin\n ], [[NAME, 'Dolphin'], VERSION], [\n\n /((?:android.+)crmo|crios)\\/([\\w\\.]+)/i // Chrome for Android/iOS\n ], [[NAME, 'Chrome'], VERSION], [\n\n /XiaoMi\\/MiuiBrowser\\/([\\w\\.]+)/i // MIUI Browser\n ], [VERSION, [NAME, 'MIUI Browser']], [\n\n /android.+version\\/([\\w\\.]+)\\s+(?:mobile\\s?safari|safari)/i // Android Browser\n ], [VERSION, [NAME, 'Android Browser']], [\n\n /FBAV\\/([\\w\\.]+);/i // Facebook App for iOS\n ], [VERSION, [NAME, 'Facebook']], [\n\n /version\\/([\\w\\.]+).+?mobile\\/\\w+\\s(safari)/i // Mobile Safari\n ], [VERSION, [NAME, 'Mobile Safari']], [\n\n /version\\/([\\w\\.]+).+?(mobile\\s?safari|safari)/i // Safari & Safari Mobile\n ], [VERSION, NAME], [\n\n /webkit.+?(mobile\\s?safari|safari)(\\/[\\w\\.]+)/i // Safari < 3.0\n ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [\n\n /(konqueror)\\/([\\w\\.]+)/i, // Konqueror\n /(webkit|khtml)\\/([\\w\\.]+)/i\n ], [NAME, VERSION], [\n\n // Gecko based\n /(navigator|netscape)\\/([\\w\\.-]+)/i // Netscape\n ], [[NAME, 'Netscape'], VERSION], [\n /fxios\\/([\\w\\.-]+)/i // Firefox for iOS\n ], [VERSION, [NAME, 'Firefox']], [\n /(swiftfox)/i, // Swiftfox\n /(icedragon|iceweasel|camino|chimera|fennec|maemo\\sbrowser|minimo|conkeror)[\\/\\s]?([\\w\\.\\+]+)/i,\n // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror\n /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\\/([\\w\\.-]+)/i,\n // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix\n /(mozilla)\\/([\\w\\.]+).+rv\\:.+gecko\\/\\d+/i, // Mozilla\n\n // Other\n /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\\/\\s]?([\\w\\.]+)/i,\n // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf\n /(links)\\s\\(([\\w\\.]+)/i, // Links\n /(gobrowser)\\/?([\\w\\.]+)*/i, // GoBrowser\n /(ice\\s?browser)\\/v?([\\w\\._]+)/i, // ICE Browser\n /(mosaic)[\\/\\s]([\\w\\.]+)/i // Mosaic\n ], [NAME, VERSION]\n\n /* /////////////////////\n // Media players BEGIN\n ////////////////////////\n\n , [\n\n /(apple(?:coremedia|))\\/((\\d+)[\\w\\._]+)/i, // Generic Apple CoreMedia\n /(coremedia) v((\\d+)[\\w\\._]+)/i\n ], [NAME, VERSION], [\n\n /(aqualung|lyssna|bsplayer)\\/((\\d+)?[\\w\\.-]+)/i // Aqualung/Lyssna/BSPlayer\n ], [NAME, VERSION], [\n\n /(ares|ossproxy)\\s((\\d+)[\\w\\.-]+)/i // Ares/OSSProxy\n ], [NAME, VERSION], [\n\n /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\\/((\\d+)[\\w\\.-]+)/i,\n // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC\n // NSPlayer/PSP-InternetRadioPlayer/Videos\n /(clementine|music player daemon)\\s((\\d+)[\\w\\.-]+)/i, // Clementine/MPD\n /(lg player|nexplayer)\\s((\\d+)[\\d\\.]+)/i,\n /player\\/(nexplayer|lg player)\\s((\\d+)[\\w\\.-]+)/i // NexPlayer/LG Player\n ], [NAME, VERSION], [\n /(nexplayer)\\s((\\d+)[\\w\\.-]+)/i // Nexplayer\n ], [NAME, VERSION], [\n\n /(flrp)\\/((\\d+)[\\w\\.-]+)/i // Flip Player\n ], [[NAME, 'Flip Player'], VERSION], [\n\n /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i\n // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit\n ], [NAME], [\n\n /(gstreamer) souphttpsrc (?:\\([^\\)]+\\)){0,1} libsoup\\/((\\d+)[\\w\\.-]+)/i\n // Gstreamer\n ], [NAME, VERSION], [\n\n /(htc streaming player)\\s[\\w_]+\\s\\/\\s((\\d+)[\\d\\.]+)/i, // HTC Streaming Player\n /(java|python-urllib|python-requests|wget|libcurl)\\/((\\d+)[\\w\\.-_]+)/i,\n // Java/urllib/requests/wget/cURL\n /(lavf)((\\d+)[\\d\\.]+)/i // Lavf (FFMPEG)\n ], [NAME, VERSION], [\n\n /(htc_one_s)\\/((\\d+)[\\d\\.]+)/i // HTC One S\n ], [[NAME, /_/g, ' '], VERSION], [\n\n /(mplayer)(?:\\s|\\/)(?:(?:sherpya-){0,1}svn)(?:-|\\s)(r\\d+(?:-\\d+[\\w\\.-]+){0,1})/i\n // MPlayer SVN\n ], [NAME, VERSION], [\n\n /(mplayer)(?:\\s|\\/|[unkow-]+)((\\d+)[\\w\\.-]+)/i // MPlayer\n ], [NAME, VERSION], [\n\n /(mplayer)/i, // MPlayer (no other info)\n /(yourmuze)/i, // YourMuze\n /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime\n ], [NAME], [\n\n /(nero (?:home|scout))\\/((\\d+)[\\w\\.-]+)/i // Nero Home/Nero Scout\n ], [NAME, VERSION], [\n\n /(nokia\\d+)\\/((\\d+)[\\w\\.-]+)/i // Nokia\n ], [NAME, VERSION], [\n\n /\\s(songbird)\\/((\\d+)[\\w\\.-]+)/i // Songbird/Philips-Songbird\n ], [NAME, VERSION], [\n\n /(winamp)3 version ((\\d+)[\\w\\.-]+)/i, // Winamp\n /(winamp)\\s((\\d+)[\\w\\.-]+)/i,\n /(winamp)mpeg\\/((\\d+)[\\w\\.-]+)/i\n ], [NAME, VERSION], [\n\n /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)\n // inlight radio\n ], [NAME], [\n\n /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\\/((\\d+)[\\w\\.-]+)/i\n // QuickTime/RealMedia/RadioApp/RadioClientApplication/\n // SoundTap/Totem/Stagefright/Streamium\n ], [NAME, VERSION], [\n\n /(smp)((\\d+)[\\d\\.]+)/i // SMP\n ], [NAME, VERSION], [\n\n /(vlc) media player - version ((\\d+)[\\w\\.]+)/i, // VLC Videolan\n /(vlc)\\/((\\d+)[\\w\\.-]+)/i,\n /(xbmc|gvfs|xine|xmms|irapp)\\/((\\d+)[\\w\\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp\n /(foobar2000)\\/((\\d+)[\\d\\.]+)/i, // Foobar2000\n /(itunes)\\/((\\d+)[\\d\\.]+)/i // iTunes\n ], [NAME, VERSION], [\n\n /(wmplayer)\\/((\\d+)[\\w\\.-]+)/i, // Windows Media Player\n /(windows-media-player)\\/((\\d+)[\\w\\.-]+)/i\n ], [[NAME, /-/g, ' '], VERSION], [\n\n /windows\\/((\\d+)[\\w\\.-]+) upnp\\/[\\d\\.]+ dlnadoc\\/[\\d\\.]+ (home media server)/i\n // Windows Media Server\n ], [VERSION, [NAME, 'Windows']], [\n\n /(com\\.riseupradioalarm)\\/((\\d+)[\\d\\.]*)/i // RiseUP Radio Alarm\n ], [NAME, VERSION], [\n\n /(rad.io)\\s((\\d+)[\\d\\.]+)/i, // Rad.io\n /(radio.(?:de|at|fr))\\s((\\d+)[\\d\\.]+)/i\n ], [[NAME, 'rad.io'], VERSION]\n\n //////////////////////\n // Media players END\n ////////////////////*/\n\n ],\n\n cpu : [[\n\n /(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\\)]/i // AMD64\n ], [[ARCHITECTURE, 'amd64']], [\n\n /(ia32(?=;))/i // IA32 (quicktime)\n ], [[ARCHITECTURE, util.lowerize]], [\n\n /((?:i[346]|x)86)[;\\)]/i // IA32\n ], [[ARCHITECTURE, 'ia32']], [\n\n // PocketPC mistakenly identified as PowerPC\n /windows\\s(ce|mobile);\\sppc;/i\n ], [[ARCHITECTURE, 'arm']], [\n\n /((?:ppc|powerpc)(?:64)?)(?:\\smac|;|\\))/i // PowerPC\n ], [[ARCHITECTURE, /ower/, '', util.lowerize]], [\n\n /(sun4\\w)[;\\)]/i // SPARC\n ], [[ARCHITECTURE, 'sparc']], [\n\n /((?:avr32|ia64(?=;))|68k(?=\\))|arm(?:64|(?=v\\d+;))|(?=atmel\\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i\n // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC\n ], [[ARCHITECTURE, util.lowerize]]\n ],\n\n device : [[\n\n /\\((ipad|playbook);[\\w\\s\\);-]+(rim|apple)/i // iPad/PlayBook\n ], [MODEL, VENDOR, [TYPE, TABLET]], [\n\n /applecoremedia\\/[\\w\\.]+ \\((ipad)/ // iPad\n ], [MODEL, [VENDOR, 'Apple'], [TYPE, TABLET]], [\n\n /(apple\\s{0,1}tv)/i // Apple TV\n ], [[MODEL, 'Apple TV'], [VENDOR, 'Apple']], [\n\n /(archos)\\s(gamepad2?)/i, // Archos\n /(hp).+(touchpad)/i, // HP TouchPad\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /\\s(nook)[\\w\\s]+build\\/(\\w+)/i, // Nook\n /(dell)\\s(strea[kpr\\s\\d]*[\\dko])/i // Dell Streak\n ], [VENDOR, MODEL, [TYPE, TABLET]], [\n\n /(kf[A-z]+)\\sbuild\\/[\\w\\.]+.*silk\\//i // Kindle Fire HD\n ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [\n /(sd|kf)[0349hijorstuw]+\\sbuild\\/[\\w\\.]+.*silk\\//i // Fire Phone\n ], [[MODEL, mapper.str, maps.device.amazon.model], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [\n\n /\\((ip[honed|\\s\\w*]+);.+(apple)/i // iPod/iPhone\n ], [MODEL, VENDOR, [TYPE, MOBILE]], [\n /\\((ip[honed|\\s\\w*]+);/i // iPod/iPhone\n ], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [\n\n /(blackberry)[\\s-]?(\\w+)/i, // BlackBerry\n /(blackberry|benq|palm(?=\\-)|sonyericsson|acer|asus|dell|huawei|meizu|motorola|polytron)[\\s_-]?([\\w-]+)*/i,\n // BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Huawei/Meizu/Motorola/Polytron\n /(hp)\\s([\\w\\s]+\\w)/i, // HP iPAQ\n /(asus)-?(\\w+)/i // Asus\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /\\(bb10;\\s(\\w+)/i // BlackBerry 10\n ], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [\n // Asus Tablets\n /android.+(transfo[prime\\s]{4,10}\\s\\w+|eeepc|slider\\s\\w+|nexus 7)/i\n ], [MODEL, [VENDOR, 'Asus'], [TYPE, TABLET]], [\n\n /(sony)\\s(tablet\\s[ps])\\sbuild\\//i, // Sony\n /(sony)?(?:sgp.+)\\sbuild\\//i\n ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Tablet'], [TYPE, TABLET]], [\n /(?:sony)?(?:(?:(?:c|d)\\d{4})|(?:so[-l].+))\\sbuild\\//i\n ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Phone'], [TYPE, MOBILE]], [\n\n /\\s(ouya)\\s/i, // Ouya\n /(nintendo)\\s([wids3u]+)/i // Nintendo\n ], [VENDOR, MODEL, [TYPE, CONSOLE]], [\n\n /android.+;\\s(shield)\\sbuild/i // Nvidia\n ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [\n\n /(playstation\\s[3portablevi]+)/i // Playstation\n ], [MODEL, [VENDOR, 'Sony'], [TYPE, CONSOLE]], [\n\n /(sprint\\s(\\w+))/i // Sprint Phones\n ], [[VENDOR, mapper.str, maps.device.sprint.vendor], [MODEL, mapper.str, maps.device.sprint.model], [TYPE, MOBILE]], [\n\n /(lenovo)\\s?(S(?:5000|6000)+(?:[-][\\w+]))/i // Lenovo tablets\n ], [VENDOR, MODEL, [TYPE, TABLET]], [\n\n /(htc)[;_\\s-]+([\\w\\s]+(?=\\))|\\w+)*/i, // HTC\n /(zte)-(\\w+)*/i, // ZTE\n /(alcatel|geeksphone|huawei|lenovo|nexian|panasonic|(?=;\\s)sony)[_\\s-]?([\\w-]+)*/i\n // Alcatel/GeeksPhone/Huawei/Lenovo/Nexian/Panasonic/Sony\n ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [\n \n /(nexus\\s9)/i // HTC Nexus 9\n ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [\n\n /[\\s\\(;](xbox(?:\\sone)?)[\\s\\);]/i // Microsoft Xbox\n ], [MODEL, [VENDOR, 'Microsoft'], [TYPE, CONSOLE]], [\n /(kin\\.[onetw]{3})/i // Microsoft Kin\n ], [[MODEL, /\\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [\n\n // Motorola\n /\\s(milestone|droid(?:[2-4x]|\\s(?:bionic|x2|pro|razr))?(:?\\s4g)?)[\\w\\s]+build\\//i,\n /mot[\\s-]?(\\w+)*/i,\n /(XT\\d{3,4}) build\\//i\n ], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [\n /android.+\\s(mz60\\d|xoom[\\s2]{0,2})\\sbuild\\//i\n ], [MODEL, [VENDOR, 'Motorola'], [TYPE, TABLET]], [\n\n /android.+((sch-i[89]0\\d|shw-m380s|gt-p\\d{4}|gt-n8000|sgh-t8[56]9|nexus 10))/i,\n /((SM-T\\w+))/i\n ], [[VENDOR, 'Samsung'], MODEL, [TYPE, TABLET]], [ // Samsung\n /((s[cgp]h-\\w+|gt-\\w+|galaxy\\snexus|sm-n900))/i,\n /(sam[sung]*)[\\s-]*(\\w+-?[\\w-]*)*/i,\n /sec-((sgh\\w+))/i\n ], [[VENDOR, 'Samsung'], MODEL, [TYPE, MOBILE]], [\n /(samsung);smarttv/i\n ], [VENDOR, MODEL, [TYPE, SMARTTV]], [\n\n /\\(dtv[\\);].+(aquos)/i // Sharp\n ], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [\n /sie-(\\w+)*/i // Siemens\n ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [\n\n /(maemo|nokia).*(n900|lumia\\s\\d+)/i, // Nokia\n /(nokia)[\\s_-]?([\\w-]+)*/i\n ], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [\n\n /android\\s3\\.[\\s\\w;-]{10}(a\\d{3})/i // Acer\n ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [\n\n /android\\s3\\.[\\s\\w;-]{10}(lg?)-([06cv9]{3,4})/i // LG Tablet\n ], [[VENDOR, 'LG'], MODEL, [TYPE, TABLET]], [\n /(lg) netcast\\.tv/i // LG SmartTV\n ], [VENDOR, MODEL, [TYPE, SMARTTV]], [\n /(nexus\\s[45])/i, // LG\n /lg[e;\\s\\/-]+(\\w+)*/i\n ], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [\n\n /android.+(ideatab[a-z0-9\\-\\s]+)/i // Lenovo\n ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [\n\n /linux;.+((jolla));/i // Jolla\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n\n /((pebble))app\\/[\\d\\.]+\\s/i // Pebble\n ], [VENDOR, MODEL, [TYPE, WEARABLE]], [\n\n /android.+;\\s(glass)\\s\\d/i // Google Glass\n ], [MODEL, [VENDOR, 'Google'], [TYPE, WEARABLE]], [\n\n /android.+(\\w+)\\s+build\\/hm\\1/i, // Xiaomi Hongmi 'numeric' models\n /android.+(hm[\\s\\-_]*note?[\\s_]*(?:\\d\\w)?)\\s+build/i, // Xiaomi Hongmi\n /android.+(mi[\\s\\-_]*(?:one|one[\\s_]plus)?[\\s_]*(?:\\d\\w)?)\\s+build/i // Xiaomi Mi\n ], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [\n\n /(mobile|tablet);.+rv\\:.+gecko\\//i // Unidentifiable\n ], [[TYPE, util.lowerize], VENDOR, MODEL]\n\n /*//////////////////////////\n // TODO: move to string map\n ////////////////////////////\n\n /(C6603)/i // Sony Xperia Z C6603\n ], [[MODEL, 'Xperia Z C6603'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [\n /(C6903)/i // Sony Xperia Z 1\n ], [[MODEL, 'Xperia Z 1'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [\n\n /(SM-G900[F|H])/i // Samsung Galaxy S5\n ], [[MODEL, 'Galaxy S5'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G7102)/i // Samsung Galaxy Grand 2\n ], [[MODEL, 'Galaxy Grand 2'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G530H)/i // Samsung Galaxy Grand Prime\n ], [[MODEL, 'Galaxy Grand Prime'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G313HZ)/i // Samsung Galaxy V\n ], [[MODEL, 'Galaxy V'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-T805)/i // Samsung Galaxy Tab S 10.5\n ], [[MODEL, 'Galaxy Tab S 10.5'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [\n /(SM-G800F)/i // Samsung Galaxy S5 Mini\n ], [[MODEL, 'Galaxy S5 Mini'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-T311)/i // Samsung Galaxy Tab 3 8.0\n ], [[MODEL, 'Galaxy Tab 3 8.0'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [\n\n /(R1001)/i // Oppo R1001\n ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [\n /(X9006)/i // Oppo Find 7a\n ], [[MODEL, 'Find 7a'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(R2001)/i // Oppo YOYO R2001\n ], [[MODEL, 'Yoyo R2001'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(R815)/i // Oppo Clover R815\n ], [[MODEL, 'Clover R815'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(U707)/i // Oppo Find Way S\n ], [[MODEL, 'Find Way S'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n\n /(T3C)/i // Advan Vandroid T3C\n ], [MODEL, [VENDOR, 'Advan'], [TYPE, TABLET]], [\n /(ADVAN T1J\\+)/i // Advan Vandroid T1J+\n ], [[MODEL, 'Vandroid T1J+'], [VENDOR, 'Advan'], [TYPE, TABLET]], [\n /(ADVAN S4A)/i // Advan Vandroid S4A\n ], [[MODEL, 'Vandroid S4A'], [VENDOR, 'Advan'], [TYPE, MOBILE]], [\n\n /(V972M)/i // ZTE V972M\n ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [\n\n /(i-mobile)\\s(IQ\\s[\\d\\.]+)/i // i-mobile IQ\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /(IQ6.3)/i // i-mobile IQ IQ 6.3\n ], [[MODEL, 'IQ 6.3'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [\n /(i-mobile)\\s(i-style\\s[\\d\\.]+)/i // i-mobile i-STYLE\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /(i-STYLE2.1)/i // i-mobile i-STYLE 2.1\n ], [[MODEL, 'i-STYLE 2.1'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [\n \n /(mobiistar touch LAI 512)/i // mobiistar touch LAI 512\n ], [[MODEL, 'Touch LAI 512'], [VENDOR, 'mobiistar'], [TYPE, MOBILE]], [\n\n /////////////\n // END TODO\n ///////////*/\n\n ],\n\n engine : [[\n\n /windows.+\\sedge\\/([\\w\\.]+)/i // EdgeHTML\n ], [VERSION, [NAME, 'EdgeHTML']], [\n\n /(presto)\\/([\\w\\.]+)/i, // Presto\n /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\\/([\\w\\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m\n /(khtml|tasman|links)[\\/\\s]\\(?([\\w\\.]+)/i, // KHTML/Tasman/Links\n /(icab)[\\/\\s]([23]\\.[\\d\\.]+)/i // iCab\n ], [NAME, VERSION], [\n\n /rv\\:([\\w\\.]+).*(gecko)/i // Gecko\n ], [VERSION, NAME]\n ],\n\n os : [[\n\n // Windows based\n /microsoft\\s(windows)\\s(vista|xp)/i // Windows (iTunes)\n ], [NAME, VERSION], [\n /(windows)\\snt\\s6\\.2;\\s(arm)/i, // Windows RT\n /(windows\\sphone(?:\\sos)*|windows\\smobile|windows)[\\s\\/]?([ntce\\d\\.\\s]+\\w)/i\n ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [\n /(win(?=3|9|n)|win\\s9x\\s)([nt\\d\\.]+)/i\n ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [\n\n // Mobile/Embedded OS\n /\\((bb)(10);/i // BlackBerry 10\n ], [[NAME, 'BlackBerry'], VERSION], [\n /(blackberry)\\w*\\/?([\\w\\.]+)*/i, // Blackberry\n /(tizen)[\\/\\s]([\\w\\.]+)/i, // Tizen\n /(android|webos|palm\\sos|qnx|bada|rim\\stablet\\sos|meego|contiki)[\\/\\s-]?([\\w\\.]+)*/i,\n // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki\n /linux;.+(sailfish);/i // Sailfish OS\n ], [NAME, VERSION], [\n /(symbian\\s?os|symbos|s60(?=;))[\\/\\s-]?([\\w\\.]+)*/i // Symbian\n ], [[NAME, 'Symbian'], VERSION], [\n /\\((series40);/i // Series 40\n ], [NAME], [\n /mozilla.+\\(mobile;.+gecko.+firefox/i // Firefox OS\n ], [[NAME, 'Firefox OS'], VERSION], [\n\n // Console\n /(nintendo|playstation)\\s([wids3portablevu]+)/i, // Nintendo/Playstation\n\n // GNU/Linux based\n /(mint)[\\/\\s\\(]?(\\w+)*/i, // Mint\n /(mageia|vectorlinux)[;\\s]/i, // Mageia/VectorLinux\n /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\\/\\s-]?([\\w\\.-]+)*/i,\n // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware\n // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus\n /(hurd|linux)\\s?([\\w\\.]+)*/i, // Hurd/Linux\n /(gnu)\\s?([\\w\\.]+)*/i // GNU\n ], [NAME, VERSION], [\n\n /(cros)\\s[\\w]+\\s([\\w\\.]+\\w)/i // Chromium OS\n ], [[NAME, 'Chromium OS'], VERSION],[\n\n // Solaris\n /(sunos)\\s?([\\w\\.]+\\d)*/i // Solaris\n ], [[NAME, 'Solaris'], VERSION], [\n\n // BSD based\n /\\s([frentopc-]{0,4}bsd|dragonfly)\\s?([\\w\\.]+)*/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly\n ], [NAME, VERSION],[\n\n /(ip[honead]+)(?:.*os\\s*([\\w]+)*\\slike\\smac|;\\sopera)/i // iOS\n ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [\n\n /(mac\\sos\\sx)\\s?([\\w\\s\\.]+\\w)*/i,\n /(macintosh|mac(?=_powerpc)\\s)/i // Mac OS\n ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [\n\n // Other\n /((?:open)?solaris)[\\/\\s-]?([\\w\\.]+)*/i, // Solaris\n /(haiku)\\s(\\w+)/i, // Haiku\n /(aix)\\s((\\d)(?=\\.|\\)|\\s)[\\w\\.]*)*/i, // AIX\n /(plan\\s9|minix|beos|os\\/2|amigaos|morphos|risc\\sos|openvms)/i,\n // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS\n /(unix)\\s?([\\w\\.]+)*/i // UNIX\n ], [NAME, VERSION]\n ]\n };\n\n\n /////////////////\n // Constructor\n ////////////////\n\n\n var UAParser = function (uastring, extensions) {\n\n if (!(this instanceof UAParser)) {\n return new UAParser(uastring, extensions).getResult();\n }\n\n var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);\n var rgxmap = extensions ? util.extend(regexes, extensions) : regexes;\n\n this.getBrowser = function () {\n var browser = mapper.rgx.apply(this, rgxmap.browser);\n browser.major = util.major(browser.version);\n return browser;\n };\n this.getCPU = function () {\n return mapper.rgx.apply(this, rgxmap.cpu);\n };\n this.getDevice = function () {\n return mapper.rgx.apply(this, rgxmap.device);\n };\n this.getEngine = function () {\n return mapper.rgx.apply(this, rgxmap.engine);\n };\n this.getOS = function () {\n return mapper.rgx.apply(this, rgxmap.os);\n };\n this.getResult = function() {\n return {\n ua : this.getUA(),\n browser : this.getBrowser(),\n engine : this.getEngine(),\n os : this.getOS(),\n device : this.getDevice(),\n cpu : this.getCPU()\n };\n };\n this.getUA = function () {\n return ua;\n };\n this.setUA = function (uastring) {\n ua = uastring;\n return this;\n };\n this.setUA(ua);\n return this;\n };\n\n UAParser.VERSION = LIBVERSION;\n UAParser.BROWSER = {\n NAME : NAME,\n MAJOR : MAJOR, // deprecated\n VERSION : VERSION\n };\n UAParser.CPU = {\n ARCHITECTURE : ARCHITECTURE\n };\n UAParser.DEVICE = {\n MODEL : MODEL,\n VENDOR : VENDOR,\n TYPE : TYPE,\n CONSOLE : CONSOLE,\n MOBILE : MOBILE,\n SMARTTV : SMARTTV,\n TABLET : TABLET,\n WEARABLE: WEARABLE,\n EMBEDDED: EMBEDDED\n };\n UAParser.ENGINE = {\n NAME : NAME,\n VERSION : VERSION\n };\n UAParser.OS = {\n NAME : NAME,\n VERSION : VERSION\n };\n\n\n ///////////\n // Export\n //////////\n\n\n // check js environment\n if (typeof(exports) !== UNDEF_TYPE) {\n // nodejs env\n if (typeof module !== UNDEF_TYPE && module.exports) {\n exports = module.exports = UAParser;\n }\n exports.UAParser = UAParser;\n } else {\n // requirejs env (optional)\n if (typeof(define) === FUNC_TYPE && define.amd) {\n define(function () {\n return UAParser;\n });\n } else {\n // browser env\n window.UAParser = UAParser;\n }\n }\n\n // jQuery/Zepto specific (optional)\n // Note: \n // In AMD env the global scope should be kept clean, but jQuery is an exception.\n // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,\n // and we should catch that.\n var $ = window.jQuery || window.Zepto;\n if (typeof $ !== UNDEF_TYPE) {\n var parser = new UAParser();\n $.ua = parser.getResult();\n $.ua.get = function() {\n return parser.getUA();\n };\n $.ua.set = function (uastring) {\n parser.setUA(uastring);\n var result = parser.getResult();\n for (var prop in result) {\n $.ua[prop] = result[prop];\n }\n };\n }\n\n})(typeof window === 'object' ? window : this);\n","\nvar rng;\n\nif (global.crypto && crypto.getRandomValues) {\n // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto\n // Moderately fast, high quality\n var _rnds8 = new Uint8Array(16);\n rng = function whatwgRNG() {\n crypto.getRandomValues(_rnds8);\n return _rnds8;\n };\n}\n\nif (!rng) {\n // Math.random()-based (RNG)\n //\n // If all else fails, use Math.random(). It's fast, but is of unspecified\n // quality.\n var _rnds = new Array(16);\n rng = function() {\n for (var i = 0, r; i < 16; i++) {\n if ((i & 0x03) === 0) r = Math.random() * 0x100000000;\n _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;\n }\n\n return _rnds;\n };\n}\n\nmodule.exports = rng;\n\n","/**\n # normalice\n\n Normalize an ice server configuration object (or plain old string) into a format\n that is usable in all browsers supporting WebRTC. Primarily this module is designed\n to help with the transition of the `url` attribute of the configuration object to\n the `urls` attribute.\n\n ## Example Usage\n\n <<< examples/simple.js\n\n**/\n\nvar protocols = [\n 'stun:',\n 'turn:'\n];\n\nmodule.exports = function(input) {\n var url = (input || {}).url || input;\n var protocol;\n var parts;\n var output = {};\n\n // if we don't have a string url, then allow the input to passthrough\n if (typeof url != 'string' && (! (url instanceof String))) {\n return input;\n }\n\n // trim the url string, and convert to an array\n url = url.trim();\n\n // if the protocol is not known, then passthrough\n protocol = protocols[protocols.indexOf(url.slice(0, 5))];\n if (! protocol) {\n return input;\n }\n\n // now let's attack the remaining url parts\n url = url.slice(5);\n parts = url.split('@');\n\n output.username = input.username;\n output.credential = input.credential;\n // if we have an authentication part, then set the credentials\n if (parts.length > 1) {\n url = parts[1];\n parts = parts[0].split(':');\n\n // add the output credential and username\n output.username = parts[0];\n output.credential = (input || {}).credential || parts[1] || '';\n }\n\n output.url = protocol + url;\n output.urls = [ output.url ];\n\n return output;\n};\n"]}
@@ -0,0 +1,36 @@
1
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.kurentoUtils = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ function noop(e){e&&console.error(e)}function trackStop(e){e.stop&&e.stop()}function streamStop(e){e.getTracks().forEach(trackStop)}function bufferizeCandidates(e,n){var t=[];return e.addEventListener("signalingstatechange",function(){if("stable"===this.signalingState)for(;t.length;){var e=t.shift();this.addIceCandidate(e.candidate,e.callback,e.callback)}}),function(i,o){switch(o=o||n,e.signalingState){case"closed":o(new Error("PeerConnection object is closed"));break;case"stable":if(e.remoteDescription){e.addIceCandidate(i,o,o);break}default:t.push({candidate:i,callback:o})}}}function removeFIDFromOffer(e){var n=e.indexOf("a=ssrc-group:FID");return n>0?e.slice(0,n):e}function getSimulcastInfo(e){var n=e.getVideoTracks(),t=["a=x-google-flag:conference","a=ssrc-group:SIM 1 2 3","a=ssrc:1 cname:localVideo","a=ssrc:1 msid:"+e.id+" "+n[0].id,"a=ssrc:1 mslabel:"+e.id,"a=ssrc:1 label:"+n[0].id,"a=ssrc:2 cname:localVideo","a=ssrc:2 msid:"+e.id+" "+n[0].id,"a=ssrc:2 mslabel:"+e.id,"a=ssrc:2 label:"+n[0].id,"a=ssrc:3 cname:localVideo","a=ssrc:3 msid:"+e.id+" "+n[0].id,"a=ssrc:3 mslabel:"+e.id,"a=ssrc:3 label:"+n[0].id];return t.push(""),t.join("\n")}function WebRtcPeer(e,n,t){function i(){if(a){var e=p.getRemoteStreams()[0],n=e?URL.createObjectURL(e):"";a.pause(),a.src=n,a.load(),console.log("Remote URL:",n)}}function o(){C.emit("streamended",this)}function r(){"closed"===p.signalingState&&t('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue'),d&&c&&C.showLocalVideo(),d&&(d.addEventListener("ended",o),p.addStream(d)),l&&(l.addEventListener("ended",o),p.addStream(l));var n=parser.getBrowser();"sendonly"!==e||"Chrome"!==n.name&&"Chromium"!==n.name||39!==n.major||(e="sendrecv"),t()}function s(e){void 0===e&&(e=MEDIA_CONSTRAINTS),getUserMedia(e,function(e){d=e,r()},t)}if(!(this instanceof WebRtcPeer))return new WebRtcPeer(e,n,t);WebRtcPeer.super_.call(this),n instanceof Function&&(t=n,n=void 0),n=n||{},t=(t||noop).bind(this);var c=n.localVideo,a=n.remoteVideo,d=n.videoStream,l=n.audioStream,u=n.mediaConstraints,f=n.connectionConstraints,p=n.peerConnection,v=n.sendSource||"webcam",h=uuid.v4(),g=recursive({iceServers:freeice()},n.configuration),b=n.onstreamended;b&&this.on("streamended",b);var m=n.onicecandidate;m&&this.on("icecandidate",m);var R=n.oncandidategatheringdone;R&&this.on("candidategatheringdone",R);var S=n.simulcast;p||(p=new RTCPeerConnection(g)),Object.defineProperties(this,{peerConnection:{get:function(){return p}},remoteVideo:{get:function(){return a}},localVideo:{get:function(){return c}},currentFrame:{get:function(){if(a){if(a.readyState<a.HAVE_CURRENT_DATA)throw new Error("No video stream data available");var e=document.createElement("canvas");return e.width=a.videoWidth,e.height=a.videoHeight,e.getContext("2d").drawImage(a,0,0),e}}}});var C=this,w=[],P=!1;p.addEventListener("icecandidate",function(e){var n=e.candidate;EventEmitter.listenerCount(C,"icecandidate")||EventEmitter.listenerCount(C,"candidategatheringdone")?n?(C.emit("icecandidate",n),P=!1):P||(C.emit("candidategatheringdone"),P=!0):P||(w.push(n),n||(P=!0))}),this.on("newListener",function(e,n){if("icecandidate"===e||"candidategatheringdone"===e)for(;w.length;){var t=w.shift();!t==("candidategatheringdone"===e)&&n(t)}});var y=bufferizeCandidates(p);this.addIceCandidate=function(e,n){var t=new RTCIceCandidate(e);console.log("ICE candidate received"),n=(n||noop).bind(this),y(t,n)},this.generateOffer=function(n){n=n.bind(this);var t=parser.getBrowser(),i=!0,o=!0;u&&(i="boolean"==typeof u.audio?u.audio:!0,o="boolean"==typeof u.video?u.video:!0);var r="Firefox"===t.name&&t.version>34?{offerToReceiveAudio:"sendonly"!==e&&i,offerToReceiveVideo:"sendonly"!==e&&o}:{mandatory:{OfferToReceiveAudio:"sendonly"!==e&&i,OfferToReceiveVideo:"sendonly"!==e&&o},optional:[{DtlsSrtpKeyAgreement:!0}]},s=recursive(r,f);console.log("constraints: "+JSON.stringify(s)),p.createOffer(function(e){console.log("Created SDP offer"),S&&("Chrome"===t.name||"Chromium"===t.name?(console.log("Adding multicast info"),e=new RTCSessionDescription({type:e.type,sdp:removeFIDFromOffer(e.sdp)+getSimulcastInfo(d)})):console.warn("Simulcast is only available in Chrome browser.")),p.setLocalDescription(e,function(){console.log("Local description set",e.sdp),n(null,e.sdp,C.processAnswer.bind(C))},n)},n,s)},this.getLocalSessionDescriptor=function(){return p.localDescription},this.getRemoteSessionDescriptor=function(){return p.remoteDescription},this.showLocalVideo=function(){c.src=URL.createObjectURL(d),c.muted=!0},this.processAnswer=function(e,n){n=(n||noop).bind(this);var t=new RTCSessionDescription({type:"answer",sdp:e});return console.log("SDP answer received, setting remote description"),"closed"===p.signalingState?n("PeerConnection is closed"):void p.setRemoteDescription(t,function(){i(),n()},n)},this.processOffer=function(e,n){n=n.bind(this);var t=new RTCSessionDescription({type:"offer",sdp:e});return console.log("SDP offer received, setting remote description"),"closed"===p.signalingState?n("PeerConnection is closed"):void p.setRemoteDescription(t,function(){i(),p.createAnswer(function(e){console.log("Created SDP answer"),p.setLocalDescription(e,function(){console.log("Local description set",e.sdp),n(null,e.sdp)},n)},n)},n)},"recvonly"===e||d||l?setTimeout(r,0):"webcam"===v?s(u):getScreenConstraints(v,function(e,n){return e?t(e):(constraints=[u],constraints.unshift(n),void s(recursive.apply(void 0,constraints)))},h),this.on("_dispose",function(){c&&(c.pause(),c.src="",c.load()),a&&(a.pause(),a.src="",a.load()),C.removeAllListeners(),void 0!==window.cancelChooseDesktopMedia&&window.cancelChooseDesktopMedia(h)})}function createEnableDescriptor(e){var n="get"+e+"Tracks";return{enumerable:!0,get:function(){if(this.peerConnection){var e=this.peerConnection.getLocalStreams();if(e.length){for(var t,i=0;t=e[i];i++)for(var o,r=t[n](),s=0;o=r[s];s++)if(!o.enabled)return!1;return!0}}},set:function(e){function t(n){n.enabled=e}this.peerConnection.getLocalStreams().forEach(function(e){e[n]().forEach(t)})}}}function WebRtcPeerRecvonly(e,n){return this instanceof WebRtcPeerRecvonly?void WebRtcPeerRecvonly.super_.call(this,"recvonly",e,n):new WebRtcPeerRecvonly(e,n)}function WebRtcPeerSendonly(e,n){return this instanceof WebRtcPeerSendonly?void WebRtcPeerSendonly.super_.call(this,"sendonly",e,n):new WebRtcPeerSendonly(e,n)}function WebRtcPeerSendrecv(e,n){return this instanceof WebRtcPeerSendrecv?void WebRtcPeerSendrecv.super_.call(this,"sendrecv",e,n):new WebRtcPeerSendrecv(e,n)}var freeice=require("freeice"),inherits=require("inherits"),UAParser=require("ua-parser-js"),uuid=require("uuid"),EventEmitter=require("events").EventEmitter,recursive=require("merge").recursive.bind(void 0,!0);try{require("kurento-browser-extensions")}catch(error){"undefined"==typeof getScreenConstraints&&(console.warn("screen sharing is not available"),getScreenConstraints=function(e,n){n(new Error("This library is not enabled for screen sharing"))})}var MEDIA_CONSTRAINTS={audio:!0,video:{width:640,framerate:15}},ua=window&&window.navigator?window.navigator.userAgent:"",parser=new UAParser(ua);inherits(WebRtcPeer,EventEmitter),Object.defineProperties(WebRtcPeer.prototype,{enabled:{enumerable:!0,get:function(){return this.audioEnabled&&this.videoEnabled},set:function(e){this.audioEnabled=this.videoEnabled=e}},audioEnabled:createEnableDescriptor("Audio"),videoEnabled:createEnableDescriptor("Video")}),WebRtcPeer.prototype.getLocalStream=function(e){return this.peerConnection?this.peerConnection.getLocalStreams()[e||0]:void 0},WebRtcPeer.prototype.getRemoteStream=function(e){return this.peerConnection?this.peerConnection.getRemoteStreams()[e||0]:void 0},WebRtcPeer.prototype.dispose=function(){console.log("Disposing WebRtcPeer");var e=this.peerConnection;try{if(e){if("closed"===e.signalingState)return;e.getLocalStreams().forEach(streamStop),e.close()}}catch(n){console.warn("Exception disposing webrtc peer "+n)}this.emit("_dispose")},inherits(WebRtcPeerRecvonly,WebRtcPeer),inherits(WebRtcPeerSendonly,WebRtcPeer),inherits(WebRtcPeerSendrecv,WebRtcPeer),exports.bufferizeCandidates=bufferizeCandidates,exports.WebRtcPeerRecvonly=WebRtcPeerRecvonly,exports.WebRtcPeerSendonly=WebRtcPeerSendonly,exports.WebRtcPeerSendrecv=WebRtcPeerSendrecv;
3
+ },{"events":4,"freeice":5,"inherits":9,"kurento-browser-extensions":10,"merge":11,"ua-parser-js":12,"uuid":14}],2:[function(require,module,exports){
4
+ window.addEventListener&&(module.exports=require("./index"));
5
+ },{"./index":3}],3:[function(require,module,exports){
6
+ var WebRtcPeer=require("./WebRtcPeer");exports.WebRtcPeer=WebRtcPeer;
7
+ },{"./WebRtcPeer":1}],4:[function(require,module,exports){
8
+ function EventEmitter(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function isFunction(e){return"function"==typeof e}function isNumber(e){return"number"==typeof e}function isObject(e){return"object"==typeof e&&null!==e}function isUndefined(e){return void 0===e}module.exports=EventEmitter,EventEmitter.EventEmitter=EventEmitter,EventEmitter.prototype._events=void 0,EventEmitter.prototype._maxListeners=void 0,EventEmitter.defaultMaxListeners=10,EventEmitter.prototype.setMaxListeners=function(e){if(!isNumber(e)||0>e||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},EventEmitter.prototype.emit=function(e){var t,n,s,i,r,o;if(this._events||(this._events={}),"error"===e&&(!this._events.error||isObject(this._events.error)&&!this._events.error.length)){if(t=arguments[1],t instanceof Error)throw t;throw TypeError('Uncaught, unspecified "error" event.')}if(n=this._events[e],isUndefined(n))return!1;if(isFunction(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:for(s=arguments.length,i=new Array(s-1),r=1;s>r;r++)i[r-1]=arguments[r];n.apply(this,i)}else if(isObject(n)){for(s=arguments.length,i=new Array(s-1),r=1;s>r;r++)i[r-1]=arguments[r];for(o=n.slice(),s=o.length,r=0;s>r;r++)o[r].apply(this,i)}return!0},EventEmitter.prototype.addListener=function(e,t){var n;if(!isFunction(t))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,isFunction(t.listener)?t.listener:t),this._events[e]?isObject(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,isObject(this._events[e])&&!this._events[e].warned){var n;n=isUndefined(this._maxListeners)?EventEmitter.defaultMaxListeners:this._maxListeners,n&&n>0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())}return this},EventEmitter.prototype.on=EventEmitter.prototype.addListener,EventEmitter.prototype.once=function(e,t){function n(){this.removeListener(e,n),s||(s=!0,t.apply(this,arguments))}if(!isFunction(t))throw TypeError("listener must be a function");var s=!1;return n.listener=t,this.on(e,n),this},EventEmitter.prototype.removeListener=function(e,t){var n,s,i,r;if(!isFunction(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],i=n.length,s=-1,n===t||isFunction(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(isObject(n)){for(r=i;r-- >0;)if(n[r]===t||n[r].listener&&n[r].listener===t){s=r;break}if(0>s)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(s,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},EventEmitter.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],isFunction(n))this.removeListener(e,n);else for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},EventEmitter.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?isFunction(this._events[e])?[this._events[e]]:this._events[e].slice():[]},EventEmitter.listenerCount=function(e,t){var n;return n=e._events&&e._events[t]?isFunction(e._events[t])?1:e._events[t].length:0};
9
+ },{}],5:[function(require,module,exports){
10
+ "use strict";var normalice=require("normalice"),freeice=module.exports=function(n){function t(n,t){for(var r,u=[],o=[].concat(e[n]);o.length&&u.length<t;)r=Math.random()*o.length|0,u=u.concat(o.splice(r,1));return u.map(function(t){return"string"==typeof t||t instanceof String?normalice(n+":"+t):t})}var r,e={stun:(n||{}).stun||require("./stun.json"),turn:(n||{}).turn||require("./turn.json")},u=(n||{}).stunCount||2,o=(n||{}).turnCount||0;return r=[].concat(t("stun",u)),o&&(r=r.concat(t("turn",o))),r};
11
+ },{"./stun.json":7,"./turn.json":8,"normalice":6}],6:[function(require,module,exports){
12
+ var protocols=["stun:","turn:"];module.exports=function(e){var r,t,n=(e||{}).url||e,l={};return"string"==typeof n||n instanceof String?(n=n.trim(),(r=protocols[protocols.indexOf(n.slice(0,5))])?(n=n.slice(5),t=n.split("@"),l.username=e.username,l.credential=e.credential,t.length>1&&(n=t[1],t=t[0].split(":"),l.username=t[0],l.credential=(e||{}).credential||t[1]||""),l.url=r+n,l.urls=[l.url],l):e):e};
13
+ },{}],7:[function(require,module,exports){
14
+ module.exports=["stun.l.google.com:19302","stun1.l.google.com:19302","stun2.l.google.com:19302","stun3.l.google.com:19302","stun4.l.google.com:19302","stun.ekiga.net","stun.ideasip.com","stun.schlund.de","stun.stunprotocol.org:3478","stun.voiparound.com","stun.voipbuster.com","stun.voipstunt.com","stun.voxgratia.org","stun.services.mozilla.com"]
15
+ },{}],8:[function(require,module,exports){
16
+ module.exports=[]
17
+ },{}],9:[function(require,module,exports){
18
+ "function"==typeof Object.create?module.exports=function(t,e){t.super_=e,t.prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}})}:module.exports=function(t,e){t.super_=e;var o=function(){};o.prototype=e.prototype,t.prototype=new o,t.prototype.constructor=t};
19
+ },{}],10:[function(require,module,exports){
20
+
21
+ },{}],11:[function(require,module,exports){
22
+ !function(e){function r(e,o){if("object"!==n(e))return o;for(var t in o)"object"===n(e[t])&&"object"===n(o[t])?e[t]=r(e[t],o[t]):e[t]=o[t];return e}function o(e,o,c){var u=c[0],f=c.length;(e||"object"!==n(u))&&(u={});for(var i=0;f>i;++i){var l=c[i],a=n(l);if("object"===a)for(var s in l){var v=e?t.clone(l[s]):l[s];o?u[s]=r(u[s],v):u[s]=v}}return u}function n(e){return{}.toString.call(e).slice(8,-1).toLowerCase()}var t=function(e){return o(e===!0,!1,arguments)},c="merge";t.recursive=function(e){return o(e===!0,!0,arguments)},t.clone=function(e){var r,o,c=e,u=n(e);if("array"===u)for(c=[],o=e.length,r=0;o>r;++r)c[r]=t.clone(e[r]);else if("object"===u){c={};for(r in e)c[r]=t.clone(e[r])}return c},e?module.exports=t:window[c]=t}("object"==typeof module&&module&&"object"==typeof module.exports&&module.exports);
23
+ },{}],12:[function(require,module,exports){
24
+ !function(i,e){"use strict";var s="0.7.9",o="",r="?",n="function",a="undefined",t="object",w="string",l="major",d="model",p="name",c="type",u="vendor",m="version",f="architecture",g="console",b="mobile",h="tablet",v="smarttv",x="wearable",k="embedded",y={extend:function(i,e){for(var s in e)-1!=="browser cpu device engine os".indexOf(s)&&e[s].length%2===0&&(i[s]=e[s].concat(i[s]));return i},has:function(i,e){return"string"==typeof i?-1!==e.toLowerCase().indexOf(i.toLowerCase()):!1},lowerize:function(i){return i.toLowerCase()},major:function(i){return typeof i===w?i.split(".")[0]:e}},A={rgx:function(){for(var i,s,o,r,w,l,d,p=0,c=arguments;p<c.length&&!l;){var u=c[p],m=c[p+1];if(typeof i===a){i={};for(r in m)w=m[r],typeof w===t?i[w[0]]=e:i[w]=e}for(s=o=0;s<u.length&&!l;)if(l=u[s++].exec(this.getUA()))for(r=0;r<m.length;r++)d=l[++o],w=m[r],typeof w===t&&w.length>0?2==w.length?typeof w[1]==n?i[w[0]]=w[1].call(this,d):i[w[0]]=w[1]:3==w.length?typeof w[1]!==n||w[1].exec&&w[1].test?i[w[0]]=d?d.replace(w[1],w[2]):e:i[w[0]]=d?w[1].call(this,d,w[2]):e:4==w.length&&(i[w[0]]=d?w[3].call(this,d.replace(w[1],w[2])):e):i[w]=d?d:e;p+=2}return i},str:function(i,s){for(var o in s)if(typeof s[o]===t&&s[o].length>0){for(var n=0;n<s[o].length;n++)if(y.has(s[o][n],i))return o===r?e:o}else if(y.has(s[o],i))return o===r?e:o;return i}},E={browser:{oldsafari:{version:{"1.0":"/8",1.2:"/1",1.3:"/3","2.0":"/412","2.0.2":"/416","2.0.3":"/417","2.0.4":"/419","?":"/"}}},device:{amazon:{model:{"Fire Phone":["SD","KF"]}},sprint:{model:{"Evo Shift 4G":"7373KT"},vendor:{HTC:"APA",Sprint:"Sprint"}}},os:{windows:{version:{ME:"4.90","NT 3.11":"NT3.51","NT 4.0":"NT4.0",2000:"NT 5.0",XP:["NT 5.1","NT 5.2"],Vista:"NT 6.0",7:"NT 6.1",8:"NT 6.2",8.1:"NT 6.3",10:["NT 6.4","NT 10.0"],RT:"ARM"}}}},S={browser:[[/(opera\smini)\/([\w\.-]+)/i,/(opera\s[mobiletab]+).+version\/([\w\.-]+)/i,/(opera).+version\/([\w\.]+)/i,/(opera)[\/\s]+([\w\.]+)/i],[p,m],[/\s(opr)\/([\w\.]+)/i],[[p,"Opera"],m],[/(kindle)\/([\w\.]+)/i,/(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]+)*/i,/(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?([\w\.]*)/i,/(?:ms|\()(ie)\s([\w\.]+)/i,/(rekonq)\/([\w\.]+)*/i,/(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium)\/([\w\.-]+)/i],[p,m],[/(trident).+rv[:\s]([\w\.]+).+like\sgecko/i],[[p,"IE"],m],[/(edge)\/((\d+)?[\w\.]+)/i],[p,m],[/(yabrowser)\/([\w\.]+)/i],[[p,"Yandex"],m],[/(comodo_dragon)\/([\w\.]+)/i],[[p,/_/g," "],m],[/(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i,/(uc\s?browser|qqbrowser)[\/\s]?([\w\.]+)/i],[p,m],[/(dolfin)\/([\w\.]+)/i],[[p,"Dolphin"],m],[/((?:android.+)crmo|crios)\/([\w\.]+)/i],[[p,"Chrome"],m],[/XiaoMi\/MiuiBrowser\/([\w\.]+)/i],[m,[p,"MIUI Browser"]],[/android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)/i],[m,[p,"Android Browser"]],[/FBAV\/([\w\.]+);/i],[m,[p,"Facebook"]],[/version\/([\w\.]+).+?mobile\/\w+\s(safari)/i],[m,[p,"Mobile Safari"]],[/version\/([\w\.]+).+?(mobile\s?safari|safari)/i],[m,p],[/webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i],[p,[m,A.str,E.browser.oldsafari.version]],[/(konqueror)\/([\w\.]+)/i,/(webkit|khtml)\/([\w\.]+)/i],[p,m],[/(navigator|netscape)\/([\w\.-]+)/i],[[p,"Netscape"],m],[/fxios\/([\w\.-]+)/i],[m,[p,"Firefox"]],[/(swiftfox)/i,/(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i,/(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/([\w\.-]+)/i,/(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i,/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\/\s]?([\w\.]+)/i,/(links)\s\(([\w\.]+)/i,/(gobrowser)\/?([\w\.]+)*/i,/(ice\s?browser)\/v?([\w\._]+)/i,/(mosaic)[\/\s]([\w\.]+)/i],[p,m]],cpu:[[/(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\)]/i],[[f,"amd64"]],[/(ia32(?=;))/i],[[f,y.lowerize]],[/((?:i[346]|x)86)[;\)]/i],[[f,"ia32"]],[/windows\s(ce|mobile);\sppc;/i],[[f,"arm"]],[/((?:ppc|powerpc)(?:64)?)(?:\smac|;|\))/i],[[f,/ower/,"",y.lowerize]],[/(sun4\w)[;\)]/i],[[f,"sparc"]],[/((?:avr32|ia64(?=;))|68k(?=\))|arm(?:64|(?=v\d+;))|(?=atmel\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i],[[f,y.lowerize]]],device:[[/\((ipad|playbook);[\w\s\);-]+(rim|apple)/i],[d,u,[c,h]],[/applecoremedia\/[\w\.]+ \((ipad)/],[d,[u,"Apple"],[c,h]],[/(apple\s{0,1}tv)/i],[[d,"Apple TV"],[u,"Apple"]],[/(archos)\s(gamepad2?)/i,/(hp).+(touchpad)/i,/(kindle)\/([\w\.]+)/i,/\s(nook)[\w\s]+build\/(\w+)/i,/(dell)\s(strea[kpr\s\d]*[\dko])/i],[u,d,[c,h]],[/(kf[A-z]+)\sbuild\/[\w\.]+.*silk\//i],[d,[u,"Amazon"],[c,h]],[/(sd|kf)[0349hijorstuw]+\sbuild\/[\w\.]+.*silk\//i],[[d,A.str,E.device.amazon.model],[u,"Amazon"],[c,b]],[/\((ip[honed|\s\w*]+);.+(apple)/i],[d,u,[c,b]],[/\((ip[honed|\s\w*]+);/i],[d,[u,"Apple"],[c,b]],[/(blackberry)[\s-]?(\w+)/i,/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|huawei|meizu|motorola|polytron)[\s_-]?([\w-]+)*/i,/(hp)\s([\w\s]+\w)/i,/(asus)-?(\w+)/i],[u,d,[c,b]],[/\(bb10;\s(\w+)/i],[d,[u,"BlackBerry"],[c,b]],[/android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7)/i],[d,[u,"Asus"],[c,h]],[/(sony)\s(tablet\s[ps])\sbuild\//i,/(sony)?(?:sgp.+)\sbuild\//i],[[u,"Sony"],[d,"Xperia Tablet"],[c,h]],[/(?:sony)?(?:(?:(?:c|d)\d{4})|(?:so[-l].+))\sbuild\//i],[[u,"Sony"],[d,"Xperia Phone"],[c,b]],[/\s(ouya)\s/i,/(nintendo)\s([wids3u]+)/i],[u,d,[c,g]],[/android.+;\s(shield)\sbuild/i],[d,[u,"Nvidia"],[c,g]],[/(playstation\s[3portablevi]+)/i],[d,[u,"Sony"],[c,g]],[/(sprint\s(\w+))/i],[[u,A.str,E.device.sprint.vendor],[d,A.str,E.device.sprint.model],[c,b]],[/(lenovo)\s?(S(?:5000|6000)+(?:[-][\w+]))/i],[u,d,[c,h]],[/(htc)[;_\s-]+([\w\s]+(?=\))|\w+)*/i,/(zte)-(\w+)*/i,/(alcatel|geeksphone|huawei|lenovo|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]+)*/i],[u,[d,/_/g," "],[c,b]],[/(nexus\s9)/i],[d,[u,"HTC"],[c,h]],[/[\s\(;](xbox(?:\sone)?)[\s\);]/i],[d,[u,"Microsoft"],[c,g]],[/(kin\.[onetw]{3})/i],[[d,/\./g," "],[u,"Microsoft"],[c,b]],[/\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?(:?\s4g)?)[\w\s]+build\//i,/mot[\s-]?(\w+)*/i,/(XT\d{3,4}) build\//i],[d,[u,"Motorola"],[c,b]],[/android.+\s(mz60\d|xoom[\s2]{0,2})\sbuild\//i],[d,[u,"Motorola"],[c,h]],[/android.+((sch-i[89]0\d|shw-m380s|gt-p\d{4}|gt-n8000|sgh-t8[56]9|nexus 10))/i,/((SM-T\w+))/i],[[u,"Samsung"],d,[c,h]],[/((s[cgp]h-\w+|gt-\w+|galaxy\snexus|sm-n900))/i,/(sam[sung]*)[\s-]*(\w+-?[\w-]*)*/i,/sec-((sgh\w+))/i],[[u,"Samsung"],d,[c,b]],[/(samsung);smarttv/i],[u,d,[c,v]],[/\(dtv[\);].+(aquos)/i],[d,[u,"Sharp"],[c,v]],[/sie-(\w+)*/i],[d,[u,"Siemens"],[c,b]],[/(maemo|nokia).*(n900|lumia\s\d+)/i,/(nokia)[\s_-]?([\w-]+)*/i],[[u,"Nokia"],d,[c,b]],[/android\s3\.[\s\w;-]{10}(a\d{3})/i],[d,[u,"Acer"],[c,h]],[/android\s3\.[\s\w;-]{10}(lg?)-([06cv9]{3,4})/i],[[u,"LG"],d,[c,h]],[/(lg) netcast\.tv/i],[u,d,[c,v]],[/(nexus\s[45])/i,/lg[e;\s\/-]+(\w+)*/i],[d,[u,"LG"],[c,b]],[/android.+(ideatab[a-z0-9\-\s]+)/i],[d,[u,"Lenovo"],[c,h]],[/linux;.+((jolla));/i],[u,d,[c,b]],[/((pebble))app\/[\d\.]+\s/i],[u,d,[c,x]],[/android.+;\s(glass)\s\d/i],[d,[u,"Google"],[c,x]],[/android.+(\w+)\s+build\/hm\1/i,/android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i,/android.+(mi[\s\-_]*(?:one|one[\s_]plus)?[\s_]*(?:\d\w)?)\s+build/i],[[d,/_/g," "],[u,"Xiaomi"],[c,b]],[/(mobile|tablet);.+rv\:.+gecko\//i],[[c,y.lowerize],u,d]],engine:[[/windows.+\sedge\/([\w\.]+)/i],[m,[p,"EdgeHTML"]],[/(presto)\/([\w\.]+)/i,/(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i,/(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i,/(icab)[\/\s]([23]\.[\d\.]+)/i],[p,m],[/rv\:([\w\.]+).*(gecko)/i],[m,p]],os:[[/microsoft\s(windows)\s(vista|xp)/i],[p,m],[/(windows)\snt\s6\.2;\s(arm)/i,/(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i],[p,[m,A.str,E.os.windows.version]],[/(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i],[[p,"Windows"],[m,A.str,E.os.windows.version]],[/\((bb)(10);/i],[[p,"BlackBerry"],m],[/(blackberry)\w*\/?([\w\.]+)*/i,/(tizen)[\/\s]([\w\.]+)/i,/(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]+)*/i,/linux;.+(sailfish);/i],[p,m],[/(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i],[[p,"Symbian"],m],[/\((series40);/i],[p],[/mozilla.+\(mobile;.+gecko.+firefox/i],[[p,"Firefox OS"],m],[/(nintendo|playstation)\s([wids3portablevu]+)/i,/(mint)[\/\s\(]?(\w+)*/i,/(mageia|vectorlinux)[;\s]/i,/(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?([\w\.-]+)*/i,/(hurd|linux)\s?([\w\.]+)*/i,/(gnu)\s?([\w\.]+)*/i],[p,m],[/(cros)\s[\w]+\s([\w\.]+\w)/i],[[p,"Chromium OS"],m],[/(sunos)\s?([\w\.]+\d)*/i],[[p,"Solaris"],m],[/\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i],[p,m],[/(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i],[[p,"iOS"],[m,/_/g,"."]],[/(mac\sos\sx)\s?([\w\s\.]+\w)*/i,/(macintosh|mac(?=_powerpc)\s)/i],[[p,"Mac OS"],[m,/_/g,"."]],[/((?:open)?solaris)[\/\s-]?([\w\.]+)*/i,/(haiku)\s(\w+)/i,/(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i,/(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms)/i,/(unix)\s?([\w\.]+)*/i],[p,m]]},T=function(e,s){if(!(this instanceof T))return new T(e,s).getResult();var r=e||(i&&i.navigator&&i.navigator.userAgent?i.navigator.userAgent:o),n=s?y.extend(S,s):S;return this.getBrowser=function(){var i=A.rgx.apply(this,n.browser);return i.major=y.major(i.version),i},this.getCPU=function(){return A.rgx.apply(this,n.cpu)},this.getDevice=function(){return A.rgx.apply(this,n.device)},this.getEngine=function(){return A.rgx.apply(this,n.engine)},this.getOS=function(){return A.rgx.apply(this,n.os)},this.getResult=function(){return{ua:this.getUA(),browser:this.getBrowser(),engine:this.getEngine(),os:this.getOS(),device:this.getDevice(),cpu:this.getCPU()}},this.getUA=function(){return r},this.setUA=function(i){return r=i,this},this.setUA(r),this};T.VERSION=s,T.BROWSER={NAME:p,MAJOR:l,VERSION:m},T.CPU={ARCHITECTURE:f},T.DEVICE={MODEL:d,VENDOR:u,TYPE:c,CONSOLE:g,MOBILE:b,SMARTTV:v,TABLET:h,WEARABLE:x,EMBEDDED:k},T.ENGINE={NAME:p,VERSION:m},T.OS={NAME:p,VERSION:m},typeof exports!==a?(typeof module!==a&&module.exports&&(exports=module.exports=T),exports.UAParser=T):typeof define===n&&define.amd?define(function(){return T}):i.UAParser=T;var N=i.jQuery||i.Zepto;if(typeof N!==a){var z=new T;N.ua=z.getResult(),N.ua.get=function(){return z.getUA()},N.ua.set=function(i){z.setUA(i);var e=z.getResult();for(var s in e)N.ua[s]=e[s]}}}("object"==typeof window?window:this);
25
+ },{}],13:[function(require,module,exports){
26
+ (function (global){
27
+ var rng;if(global.crypto&&crypto.getRandomValues){var _rnds8=new Uint8Array(16);rng=function(){return crypto.getRandomValues(_rnds8),_rnds8}}if(!rng){var _rnds=new Array(16);rng=function(){for(var r,n=0;16>n;n++)0===(3&n)&&(r=4294967296*Math.random()),_rnds[n]=r>>>((3&n)<<3)&255;return _rnds}}module.exports=rng;
28
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
29
+
30
+ },{}],14:[function(require,module,exports){
31
+ function parse(e,s,r){var t=s&&r||0,n=0;for(s=s||[],e.toLowerCase().replace(/[0-9a-f]{2}/g,function(e){16>n&&(s[t+n++]=_hexToByte[e])});16>n;)s[t+n++]=0;return s}function unparse(e,s){var r=s||0,t=_byteToHex;return t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]}function v1(e,s,r){var t=s&&r||0,n=s||[];e=e||{};var o=void 0!==e.clockseq?e.clockseq:_clockseq,a=void 0!==e.msecs?e.msecs:(new Date).getTime(),u=void 0!==e.nsecs?e.nsecs:_lastNSecs+1,c=a-_lastMSecs+(u-_lastNSecs)/1e4;if(0>c&&void 0===e.clockseq&&(o=o+1&16383),(0>c||a>_lastMSecs)&&void 0===e.nsecs&&(u=0),u>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");_lastMSecs=a,_lastNSecs=u,_clockseq=o,a+=122192928e5;var i=(1e4*(268435455&a)+u)%4294967296;n[t++]=i>>>24&255,n[t++]=i>>>16&255,n[t++]=i>>>8&255,n[t++]=255&i;var _=a/4294967296*1e4&268435455;n[t++]=_>>>8&255,n[t++]=255&_,n[t++]=_>>>24&15|16,n[t++]=_>>>16&255,n[t++]=o>>>8|128,n[t++]=255&o;for(var d=e.node||_nodeId,v=0;6>v;v++)n[t+v]=d[v];return s?s:unparse(n)}function v4(e,s,r){var t=s&&r||0;"string"==typeof e&&(s="binary"==e?new Array(16):null,e=null),e=e||{};var n=e.random||(e.rng||_rng)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,s)for(var o=0;16>o;o++)s[t+o]=n[o];return s||unparse(n)}for(var _rng=require("./rng"),_byteToHex=[],_hexToByte={},i=0;256>i;i++)_byteToHex[i]=(i+256).toString(16).substr(1),_hexToByte[_byteToHex[i]]=i;var _seedBytes=_rng(),_nodeId=[1|_seedBytes[0],_seedBytes[1],_seedBytes[2],_seedBytes[3],_seedBytes[4],_seedBytes[5]],_clockseq=16383&(_seedBytes[6]<<8|_seedBytes[7]),_lastMSecs=0,_lastNSecs=0,uuid=v4;uuid.v1=v1,uuid.v4=v4,uuid.parse=parse,uuid.unparse=unparse,module.exports=uuid;
32
+ },{"./rng":13}]},{},[2])(2)
33
+ });
34
+
35
+
36
+ //# sourceMappingURL=kurento-utils.map
@@ -0,0 +1,13 @@
1
+ class CreateVideoStreams < ActiveRecord::Migration
2
+ def change
3
+ create_table :kurento_rails_video_streams do |t|
4
+ t.string :name
5
+ t.string :pipeline
6
+ t.string :file_url
7
+ t.string :sender_rtc
8
+ t.boolean :streaming
9
+
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ class KurentoRailsVideoStream < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ h1 Broadcast
2
+ p Find me at app/views/kurento/broadcast.html.slim
@@ -0,0 +1,2 @@
1
+ h1 Index
2
+ p Find me at app/views/kurento/index.html.slim
@@ -0,0 +1,2 @@
1
+ h1 Recorded Streams
2
+ p Find me at app/views/kurento/old_streams.html.slim
@@ -0,0 +1,5 @@
1
+ require "kurento_rails/version"
2
+
3
+ module KurentoRails
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,3 @@
1
+ module KurentoRails
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ WebsocketRails::EventMap.describe do
2
+ # You can use this file to map incoming events to controller actions.
3
+ # One event can be mapped to any number of controller actions. The
4
+ # actions will be executed in the order they were subscribed.
5
+ #
6
+ # Uncomment and edit the next line to handle the client connected event:
7
+ # subscribe :client_connected, :to => Controller, :with_method => :method_name
8
+ #
9
+ # Here is an example of mapping namespaced events:
10
+ # namespace :product do
11
+ # subscribe :new, :to => ProductController, :with_method => :new_product
12
+ # end
13
+ # The above will handle an event triggered on the client like `product.new`.
14
+ namespace :streams do
15
+ subscribe :active_streams, to: KurentoWebsocketsController, with_method: :active_streams
16
+ subscribe :recorded_streams, to: KurentoWebsocketsController, with_method: :recorded_streams
17
+ subscribe :broadcast, to: KurentoWebsocketsController, with_method: :broadcast
18
+ subscribe :stop_broadcasting, to: KurentoWebsocketsController, with_method: :stop_broadcasting
19
+ subscribe :view, to: KurentoWebsocketsController, with_method: :view
20
+ end
21
+
22
+ subscribe :client_disconnected, to: KurentoWebsocketsController, with_method: :user_disconnect
23
+
24
+ end
@@ -0,0 +1,387 @@
1
+ # # Kurento Rails Javascript Client
2
+ #
3
+ # Notes:
4
+ # - Make sure that the url you pass to store video files in has sufficient space for the videos. Digital Ocean servers aren't going to cut it under heavy load.
5
+ # - All of the functions in the StreamingHelper class take a callback with the signature function(error, object). Error will be null if the function call was successful.
6
+
7
+
8
+ kurentoRailsHelpers = {}
9
+
10
+ kurentoRailsHelpers.getRailsUrlFromBrowser = () => "#{location.hostname}/websocket"
11
+
12
+ kurentoRailsHelpers.generateKurentoVideoUrl = (baseFileLocation, videoName) =>
13
+ date = new Date()
14
+ dateString = "#{date.getMonth() + 1}-#{date.getDate()}-#{date.getFullYear()}_#{date.getHours()}:#{date.getMinutes()}:#{date.getSeconds()}"
15
+ return "#{baseFileLocation}/#{encodeURIComponent(videoName.toLowerCase().replace(/\s/g, '-'))}-#{dateString}.webm"
16
+
17
+ kurentoRailsHelpers.onIceCandidate = (webRtcEndpoint, webRtcPeer, onError) =>
18
+ webRtcPeer.on 'icecandidate', (candidate) =>
19
+ candidate = kurentoClient.register.complexTypes.IceCandidate(candidate)
20
+ webRtcEndpoint.addIceCandidate candidate, onError
21
+ webRtcEndpoint.on 'OnIceCandidate', (event) =>
22
+ webRtcPeer.addIceCandidate event.candidate, onError
23
+
24
+ class StreamingHelper
25
+
26
+ # ## Constructor - give the urls for the rails server this page was served from, as well as the kurento server we will be streaming to and from
27
+ constructor: (@kurentoUrl, @railsUrl = kurentoRailsHelpers.getRailsUrlFromBrowser()) ->
28
+ @kurento = kurentoClient @kurentoUrl
29
+ @rails_websocket_dispatcher = new WebSocketRails @railsUrl
30
+ @broadcast = {}
31
+ @view = {}
32
+
33
+ ### BROADCASTING FUNCTIONS AND MEMBERS ###
34
+ @broadcast.kurentoObjects = {}
35
+ @broadcast.kurentoObjects.pipeline = null
36
+ @broadcast.kurentoObjects.webRtcPeer = null
37
+ @broadcast.kurentoObjects.recorder = null
38
+ @broadcast.kurentoObjects.webRtcEndpoint = null
39
+ @broadcast.kurentoObjects.videoStreamId = null
40
+ @broadcast.kurentoObjects.currentCallback = null
41
+
42
+ onBroadcastingError = (error) =>
43
+ if error
44
+ console.log "Broadcasting error:", error
45
+ @broadcast.stopBroadcasting()
46
+ @broadcast.kurentoObjects.currentCallback(error) if @broadcast.kurentoObjects.currentCallback
47
+
48
+ onBroadcastingOffer = (error, offer) =>
49
+ return onBroadcastingError(error) if error
50
+ @kurento.create 'MediaPipeline', (error, pipeline) =>
51
+ return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.webRtcPeer
52
+ @broadcast.kurentoObjects.pipeline = pipeline
53
+ file_url = kurentoRailsHelpers.generateKurentoVideoUrl(@broadcast.kurentoObjects.baseFileLocation, @broadcast.kurentoObjects.videoName)
54
+ mediaElements = [
55
+ {type: 'WebRtcEndpoint', params: {}}
56
+ {type: 'RecorderEndpoint', params: {uri: file_url}}
57
+ ]
58
+ pipeline.create mediaElements, (error, elements) =>
59
+ return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
60
+ [@broadcast.kurentoObjects.webRtcEndpoint, @broadcast.kurentoObjects.recorder] = elements
61
+ kurentoRailsHelpers.onIceCandidate @broadcast.kurentoObjects.webRtcEndpoint, @broadcast.kurentoObjects.webRtcPeer, onBroadcastingError
62
+ @broadcast.kurentoObjects.webRtcEndpoint.processOffer offer, (error, answer) =>
63
+ return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
64
+ @broadcast.kurentoObjects.webRtcEndpoint.gatherCandidates onBroadcastingError
65
+ @broadcast.kurentoObjects.webRtcPeer.processAnswer answer, onBroadcastingError
66
+ @broadcast.kurentoObjects.webRtcEndpoint.connect @broadcast.kurentoObjects.recorder, (error) =>
67
+ return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
68
+ @broadcast.kurentoObjects.recorder.record (error) =>
69
+ return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
70
+ save_to_rails_success = (stream) =>
71
+ @broadcast.kurentoObjects.videoStreamId = stream.id
72
+ @broadcast.kurentoObjects.currentCallback(null, stream) if @broadcast.kurentoObjects.currentCallback
73
+ save_to_rails_failure = (stream) =>
74
+ @broadcast.kurentoObjects.currentCallback("Failed to save the data to rails", stream) if @broadcast.kurentoObjects.currentCallback
75
+ streamToBroadcast =
76
+ name: @broadcast.kurentoObjects.videoName
77
+ pipeline: @broadcast.kurentoObjects.pipeline.id
78
+ file_url: file_url
79
+ sender_rtc: @broadcast.kurentoObjects.webRtcEndpoint.id
80
+ streaming: true
81
+ @rails_websocket_dispatcher.trigger 'streams.broadcast', streamToBroadcast, save_to_rails_success, save_to_rails_failure
82
+
83
+ #### Broadcast (broadcast)
84
+ # This function can be used to send video to the kurento server. Users listening for new streams will also be notified of the new streams creation:
85
+ #
86
+ # Parameters:
87
+ # - videoName: string; The name of the video stream
88
+ # - videoElement: DOMElement; A DOM element representing an html 5 video tag
89
+ # - baseFileLocation: string; The file location on the kurento server (or an external file server) where files should be saved when streaming
90
+ # - callback: function(error, object); a function you wish to have called upon either successful completion of the streaming setup process, or on the occurence of any errors
91
+ #
92
+ # It should probably be noted that this function not only broadcasts, but also records the stream
93
+ @broadcast.broadcast = (videoName, videoElement, baseFileLocation = "file:///kurento", callback = null) =>
94
+ @broadcast.kurentoObjects.currentCallback = callback
95
+ @broadcast.kurentoObjects.baseFileLocation = baseFileLocation
96
+ @broadcast.kurentoObjects.videoName = videoName
97
+ return onBroadcastingError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
98
+ videoElement = $(videoElement)[0] # a little fix to make this work if they used jquery to get the element
99
+ options = { localVideo: videoElement }
100
+ @broadcast.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly options, (error) ->
101
+ return onBroadcastingError(error) if error
102
+ this.generateOffer(onBroadcastingOffer)
103
+
104
+ #### Stop Broadcasting (stopBroadcasting)
105
+ # This function can be used to stop transmitting video to the kurento server. Users listening for stream changes will be notified of its completion, as will users listening for changes on this stream.
106
+ #
107
+ # Parameters:
108
+ # - callback: function(error, object); a function you wish for us to call when we stop broadcasting successfully, or when an error occurs
109
+ @broadcast.stopBroadcasting = (callback = null) =>
110
+ successfully_notified_rails = (stream) =>
111
+ @broadcast.kurentoObjects.videoStreamId = null
112
+ callback() if callback
113
+ failed_to_notify_rails = (stream) =>
114
+ console.log "Unable to update video stream status in rails"
115
+ callback("Unable to update video stream status in rails") if callback
116
+ if @broadcast.kurentoObjects.recorder
117
+ @broadcast.kurentoObjects.recorder.stop()
118
+ @broadcast.kurentoObjects.recorder = null
119
+ if @broadcast.kurentoObjects.webRtcEndpoint
120
+ @broadcast.kurentoObjects.webRtcEndpoint.release()
121
+ @broadcast.kurentoObjects.webRtcEndpoint = null
122
+ if @broadcast.kurentoObjects.currentCallback
123
+ @broadcast.kurentoObjects.currentCallback = null
124
+ if @broadcast.kurentoObjects.webRtcPeer
125
+ @broadcast.kurentoObjects.webRtcPeer.dispose()
126
+ @broadcast.kurentoObjects.webRtcPeer = null
127
+ if @broadcast.kurentoObjects.pipeline
128
+ @broadcast.kurentoObjects.pipeline.release()
129
+ @broadcast.kurentoObjects.pipeline = null
130
+ if @broadcast.kurentoObjects.videoStreamId
131
+ @rails_websocket_dispatcher.trigger 'streams.stop_broadcasting', '', successfully_notified_rails, failed_to_notify_rails
132
+ else
133
+ callback() if callback
134
+
135
+ ### END BROADCASTING FUNCTIONS AND MEMBERS ###
136
+
137
+ ### VIEWING FUNCTIONS AND MEMBERS ###
138
+ @view.live = {}
139
+ @view.recorded = {}
140
+ @view.live.kurentoObjects = {}
141
+ @view.recorded.kurentoObjects = {}
142
+
143
+ #### Get Active Streams (view.live.activeStreams)
144
+ # This function gets an array of all of the currently active streams and passes them back in the callback you supply
145
+ # Parameters:
146
+ # - callback: function(error, streamObjectArray); this function will be called once either the streams are retrieved, or an error occurs
147
+ @view.live.activeStreams = (callback = null, searchParams = {}) =>
148
+ success = (streams) =>
149
+ callback(null, streams) if callback
150
+ error = (streams) =>
151
+ callback("There was an error getting the streams", streams) if callback
152
+ @rails_websocket_dispatcher.trigger 'streams.active_streams', searchParams, success, error
153
+
154
+ #### Subscribe to streams channel (view.live.subscribeToStreams)
155
+ # This function will subscribe you to the streams channel on the rails server. Every time a new stream becomes active, the new stream handler you pass in will be called. Every time a stream terminates, the stream deactivated handler you pass in will be called. Both handlers should take the form of function(streamObject)
156
+ @view.live.subscribeToStreams = (newStreamHandler, streamDeactivatedHandler) =>
157
+ @view.live.createChannel 'video_streams', {'new': newStreamHandler, 'remove': streamDeactivatedHandler}
158
+
159
+ #### Create custom channel (view.live.createChannel)
160
+ # This function will create a channel and subscribe you to it. The events parameter should be an object, where all of the keys are the names of the events (use quotes around these so that the keys work correctly with underscores and dashes and such), and the values are the handler function to be called when that event is triggered. Each handler should have have the appropriate number of arguments, for however you plan to trigger it.
161
+ @view.live.createChannel = (channelName, events) =>
162
+ channel = @rails_websocket_dispatcher.subscribe channelName
163
+ channel.bind eventName, eventHandler for own eventName, eventHandler of events
164
+
165
+ #### Trigger channel event (view.live.triggerChannelEvent)
166
+ # This function allows you to manually trigger a channel event from the client on any channel you would like. Any other clients who are subscribed to that event will receive the event and handle it however they've registered to do so.
167
+ @view.live.triggerChannelEvent = (channelName, eventName, objectToSend) =>
168
+ channel = @rails_websocket_dispatcher.subscribe channelName
169
+ channel.trigger eventName, objectToSend
170
+
171
+ #### Subscribe to the channel for a particular video stream (view.live.onEndOfStream)
172
+ # This function allows you to receive notifications when a video stream ends. When the stream ends, your endOfStreamHandler function (which should have the signature of function(streamObject)) will be called.
173
+ #
174
+ # Parameters:
175
+ # - stream: streamObject; This is the same thing that is returned by the active streams method. Alternatively, you can pass in a regular old javascript object, so long as it has an id property.
176
+ # - endOfStreamHandler: function(streamObject); This function will be called when the specified stream ends.
177
+ @view.live.onEndOfStream = (stream, endOfStreamHandler) =>
178
+ channel = @rails_websocket_dispatcher.subscribe "stream-#{stream.id}"
179
+ channel.bind 'end-of-stream', (updated_stream) =>
180
+ @view.live.stopViewing()
181
+ endOfStreamHandler(updated_stream)
182
+
183
+ onLiveViewError = (error) =>
184
+ if error
185
+ console.log "Error!", error
186
+ @view.live.stopViewing()
187
+ @view.live.kurentoObjects.callback(error) if @view.live.kurentoObjects.callback
188
+
189
+ onLiveViewOffer = (error, offer) =>
190
+ return onLiveViewError(error) if error
191
+ if @view.live.kurentoObjects.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint
192
+ [pipeline, presenterEndpoint] = [@view.live.kurentoObjects.pipeline, @view.live.kurentoObjects.presenterWebRtcEndpoint]
193
+ pipeline.create 'WebRtcEndpoint', (error, endpoint) =>
194
+ return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
195
+ @view.live.kurentoObjects.viewerWebRtcEndpoint = endpoint
196
+ kurentoRailsHelpers.onIceCandidate endpoint, @view.live.kurentoObjects.webRtcPeer, =>
197
+ endpoint.on 'OnIceCandidate', (event) =>
198
+ candidate = kurentoClient.register.complexTypes.IceCandidate event.candidate
199
+
200
+ endpoint.processOffer offer, (error, answer) =>
201
+ return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
202
+ @view.live.kurentoObjects.webRtcPeer.processAnswer answer, (error) =>
203
+ return onLiveViewError(error) if error
204
+ endpoint.gatherCandidates (error) =>
205
+ return onLiveViewError(error) if error
206
+ @view.live.kurentoObjects.presenterWebRtcEndpoint.connect endpoint, (error) =>
207
+ return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
208
+ @view.live.kurentoObjects.callback(null, "Successfully started stream!") if @view.live.kurentoObjects.callback
209
+
210
+ #### Start Viewing (view.live.startViewing)
211
+ # This function accesses a live stream and starts it viewing. There is no error checking for if the stream is actually live, so make sure you know that it is before you start it viewing.
212
+
213
+ # Parameters:
214
+ # - stream: streamObject; This MUST contain at the very least the fields for sender_rtc and pipeline, though a full stream object is acceptable as well.
215
+ # - videoElement: DOM Element; This should be an html 5 video tag
216
+ # callback: function(error, object); The function you want to be called upon successful completion of the playback initiation process, or on error
217
+ @view.live.startViewing = (stream, videoElement, callback = null) =>
218
+ @view.live.kurentoObjects.callback = callback
219
+ return onLiveViewError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
220
+ videoElement = $(videoElement)[0]
221
+ @kurento.getMediaobjectById stream.sender_rtc, (error, webRtcEndpoint) =>
222
+ return onLiveViewError(error) if error
223
+ @view.live.kurentoObjects.presenterWebRtcEndpoint = webRtcEndpoint
224
+ @kurento.getMediaobjectById stream.pipeline, (error, pipeline) =>
225
+ return onLiveViewError(error) if error or not @view.live.kurentoObjects.presenterWebRtcEndpoint
226
+ @view.live.kurentoObjects.pipeline = pipeline
227
+ options = {remoteVideo: videoElement}
228
+ @view.live.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly options, (error) ->
229
+ return onLiveViewError(error) if error
230
+ this.generateOffer(onLiveViewOffer)
231
+
232
+
233
+ #### Stop viewing (view.live.stopViewing)
234
+ # This function stops whatever live stream is currently playing, then calls the callback when it has successfully stopped playback.
235
+ @view.live.stopViewing = (callback = null) =>
236
+ if @view.live.kurentoObjects.callback
237
+ @view.live.kurentoObjects.callback = null
238
+ if @view.live.kurentoObjects.viewerWebRtcEndpoint
239
+ @view.live.kurentoObjects.viewerWebRtcEndpoint.release()
240
+ @view.live.kurentoObjects.viewerWebRtcEndpoint = null
241
+ if @view.live.kurentoObjects.webRtcPeer
242
+ @view.live.kurentoObjects.webRtcPeer.dispose()
243
+ @view.live.kurentoObjects.webRtcPeer = null
244
+ @view.live.kurentoObjects.pipeline = null if @view.live.kurentoObjects.pipeline
245
+ @view.live.kurentoObjects.presenterWebRtcEndpoint = null if @view.live.kurentoObjects.presenterWebRtcEndpoint
246
+ callback() if callback
247
+
248
+ #### Get Available Recorded Streams (view.recorded.availableStreams)
249
+ # This function returns an array of all of the available stream objects. If you want to search for more specific streams, you can also do that by using the option search params object. The callback you provide will be called when either the streams are successfully retrieved, or an error occurs. Your callback should take the form of function(error, streamsArray)
250
+ @view.recorded.availableStreams = (callback = null, searchParams = {}) =>
251
+ success = (streams) =>
252
+ callback(null, streams) if callback
253
+ error = (streams) =>
254
+ callback("There was an error getting the streams", streams) if callback
255
+ @rails_websocket_dispatcher.trigger 'streams.recorded_streams', searchParams, success, error
256
+
257
+ onRecordedError = (error) =>
258
+ console.log "Error: ", error if error
259
+ @view.recorded.stopViewing()
260
+ @view.recorded.kurentoObjects.callback(error) if @view.recorded.kurentoObjects.callback
261
+
262
+ onRecordedOffer = (error, offer) =>
263
+ console.log "In the offer function"
264
+ onRecordedError(error) if error
265
+ @kurento.create 'MediaPipeline', (error, pipeline) =>
266
+ return onRecordedError(error) if error
267
+ @view.recorded.kurentoObjects.pipeline = pipeline
268
+ pipeline.create 'WebRtcEndpoint', (error, endpoint) =>
269
+ return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
270
+ @view.recorded.kurentoObjects.webRtcEndpoint = endpoint
271
+ kurentoRailsHelpers.onIceCandidate endpoint, @view.recorded.kurentoObjects.webRtcPeer, =>
272
+ endpoint.processOffer offer, (error, answer) =>
273
+ return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
274
+ endpoint.gatherCandidates =>
275
+ @view.recorded.kurentoObjects.webRtcPeer.processAnswer answer
276
+
277
+ options = {uri: @view.recorded.kurentoObjects.fileUrl}
278
+ pipeline.create 'PlayerEndpoint', options, (error, player) =>
279
+ return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
280
+ player.on 'EndOfStream', (event) =>
281
+ channel = @rails_websocket_dispatcher.subscribe "recorded-stream-#{@view.recorded.kurentoObjects.streamId}"
282
+ channel.trigger 'end-of-stream', @view.recorded.kurentoObjects.streamId
283
+ @view.recorded.stopViewing()
284
+ player.connect endpoint, (error) =>
285
+ return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
286
+ player.play (error) =>
287
+ return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
288
+ @view.recorded.kurentoObjects.callback() if @view.recorded.kurentoObjects.callback
289
+
290
+ #### Start Viewng (view.recorded.startViewing)
291
+ # This function will allow you to start viewing a recorded video stream
292
+ #
293
+ # Parameters:
294
+ # - stream: streamObject; must contain at least the file url and the stream id.
295
+ # - videoElement: dom element; must be an html 5 video tag
296
+ # - callback: function(error, object)
297
+ @view.recorded.startViewing = (stream, videoElement, callback = null) =>
298
+ console.log "Stream", stream
299
+ @view.recorded.kurentoObjects.fileUrl = stream.file_url
300
+ @view.recorded.kurentoObjects.streamId = stream.id
301
+ @view.recorded.kurentoObjects.videoElement = videoElement
302
+ @view.recorded.kurentoObjects.callback = callback
303
+ return onRecordedError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
304
+ videoElement = $(videoElement)[0]
305
+ options = {remoteVideo: videoElement}
306
+ @view.recorded.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly options, (error) ->
307
+ return onRecordedError(error) if error
308
+ console.log "About to generate an offer."
309
+ this.generateOffer(onRecordedOffer)
310
+
311
+ #### Stop viewing (view.recorded.stopViewing)
312
+ # This function will stop playback of whatever video file is currently playing
313
+ # It is safe to call even if no video file is playing
314
+ @view.recorded.stopViewing = (callback = null) =>
315
+ @view.recorded.kurentoObjects.fileUrl = null if @view.recorded.kurentoObjects.fileUrl
316
+ @view.recorded.kurentoObjects.streamId = null if @view.recorded.kurentoObjects.streamId
317
+ @view.recorded.kurentoObjects.callback = null if @view.recorded.kurentoObjects.callback
318
+ @view.recorded.kurentoObjects.player = null if @view.recorded.kurentoObjects.player
319
+ if @view.recorded.kurentoObjects.videoElement
320
+ $(@view.recorded.kurentoObjects.videoElement).attr 'src', ''
321
+ @view.recorded.kurentoObjects.videoElement = null
322
+ if @view.recorded.kurentoObjects.webRtcEndpoint
323
+ @view.recorded.kurentoObjects.webRtcEndpoint.release()
324
+ @view.recorded.kurentoObjects.webRtcEndpoint = null
325
+ if @view.recorded.kurentoObjects.pipeline
326
+ @view.recorded.kurentoObjects.pipeline.release()
327
+ @view.recorded.kurentoObjects.pipeline = null
328
+ callback() if callback
329
+
330
+ #### On End Of Stream (view.recorded.onEndOfStream)
331
+ # This function can be used to register an end of stream handler function for a video. The stream parameter must contain at least an id, and the endOfStreamHandler should have the signature function(streamId)
332
+ @view.recorded.onEndOfStream = (stream, endOfStreamHandler) =>
333
+ channel = @rails_websocket_dispatcher.subscribe "recorded-stream-#{stream.id}"
334
+ channel.bind 'end-of-stream', (streamId) =>
335
+ endOfStreamHandler(streamId)
336
+
337
+
338
+ #### Start Viewing (view.startViewing)
339
+ # This is a helper function that makes no distinction about whether the stream you want to play is live or recorded. It will determine whether it is live for you, then call the appropriate handler function. However, an additional database call is made to determine if the stream is live, so in cases where you actually know whether the stream is live or not, you should call view.live.startViewing and view.recorded.startViewing directly. The video element should be an html5 video tag, which your video will be displayed in upon successfull completion of the viewing. As usual, your callback should take the form function(error, object)
340
+ @view.startViewing = (stream, videoElement, callback = null) =>
341
+ live = => @view.live.startViewing(stream, videoElement, callback)
342
+ recorded = => @view.recorded.startViewing(stream, videoElement, callback)
343
+ @rails_websocket_dispatcher.trigger 'streams.view', '', live, recorded
344
+
345
+ #### Stop Viewing (view.stopViewing)
346
+ # This is a helper function that makes no distinction about whether the currently playing stream is live or recorded (or even if you have one of each playing). It will stop the streams either way.
347
+ @view.stopViewing = (callback = null) =>
348
+ @view.live.stopViewing(callback)
349
+ @view.recorded.stopViewing(callback)
350
+
351
+ #### On End of Stream (view.onEndOfStream)
352
+ # This is a helper function that makes no distinction about whether a stream is live or recorded. It will determine whether the stream is live or recorded for you, then call the correct endOfStream handler registration function. Note that this function does make an extra database call, though, so if you already know whether the stream is live or not, it will be faster to use view.live.onEndOfStream and view.recorded.endOfStream
353
+ @view.onEndOfStream = (stream, endOfStreamHandler) =>
354
+ live = => @view.live.onEndOfStream(stream, endOfStreamHandler)
355
+ recorded = => @view.recorded.onEndOfStream(stream, endOfStreamHandler)
356
+ @rails_websocket_dispatcher.trigger 'streams.view', '', live, recorded
357
+
358
+ #### Currently Viewing
359
+ # This is just a simple boolean function to determine if something is currently playing
360
+ # It returns true if the user is currently viewing either a live stream or a recorded one.
361
+ # If the optional stream object is provided, the function will return true only if THAT specific stream is currently playing (it compares it based off of pipeline id and sender_rtc id, so make sure to pass that in).
362
+ @view.currentlyViewing = (stream = null) =>
363
+ if stream
364
+ if @view.live.kurentoObjects.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint
365
+ return @view.live.kurentoObjects.pipeline.id is stream.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint.id is stream.sender_rtc
366
+ else if @view.recorded.kurentoObjects.pipeline
367
+ return @view.recorded.kurentoObjects.pipeline.id is stream.pipeline
368
+ else
369
+ return false
370
+ return @view.live.kurentoObjects.pipeline or @view.recorded.kurentoObjects.pipeline
371
+ ### END VIEWING FUNCTIONS AND MEMBERS ###
372
+
373
+ ### AND FINALLY, A UNIVERSAL PAGE UNLOAD HANDLER ###
374
+ # I put this on a timeout of like 2 seconds so that if the user decides to register any page unload handlers, we won't be interfering with those, and they won't be overwriting our handlers
375
+ timeoutFunction = =>
376
+ window.addEventListener('unload', =>
377
+ @broadcast.stopBroadcasting()
378
+ @view.stopViewing()
379
+ @kurento.close() if @kurento
380
+ @rails_websocket_dispatcher.disconnect() if @rails_websocket_dispatcher
381
+ )
382
+ setTimeout timeoutFunction, 2000
383
+ return @
384
+
385
+ window.createStreamingHelper = (kurento_url, rails_url) =>
386
+ return new StreamingHelper(kurento_url, rails_url)
387
+